Articles on: Organizers
This article is also available in:

Webhooks

Webhooks allow you to receive real-time notifications in your own system when certain actions occur on go&dance, such as when a purchase is created or updated.


Instead of periodically checking go&dance for changes, you can configure a destination URL where go&dance will automatically send an HTTP request whenever one of the events supported by the integration occurs.


Once webhooks are enabled for your account, they apply to all events for which you are the owner. You do not need to configure webhooks separately for each event.


Each notification includes information about the event that triggered it and the corresponding data, allowing you to integrate go&dance with your own applications, databases, CRM, ERP, or other external systems.


Important: webhooks are generated only for events for which your user is the owner. Events that you can access or manage but do not own are not included.


Table of contents




When one of the events subscribed to by an active webhook occurs, go&dance sends an HTTP request to the configured destination URL.


The request contains:


  • Headers with information about the event and delivery.
  • A JSON-formatted body containing the event data.
  • An HMAC-SHA256 signature that allows you to verify that the request was generated by go&dance.


Request headers


The specific headers sent by go&dance are:


Header

Description

X-GoDance-Log-Id

Unique identifier of the action that triggered the webhook. It remains the same across retries and should be used to prevent processing the same action more than once.

X-GoDance-Delivery-Id

Unique identifier of a delivery attempt. Each retry generates a new identifier.

X-GoDance-Signature

HMAC-SHA256 signature used to verify the authenticity of the request.

X-GoDance-Timestamp

Timestamp used as part of the signature calculation.

X-GoDance-Type

Type of event that triggered the notification.


Example:


User-Agent: GoDance/Webhooks
Content-Type: application/json
X-GoDance-Log-Id: 2d0df0a0-63c5-48bc-8484-535a5fb0aad6
X-GoDance-Delivery-Id: 6d498a41-d1ad-4ccc-8abf-1045cc52cbac
X-GoDance-Signature: 8ae2fe919f8412ed9001397356a38fa19c4939235511c7a550041454b628473c
X-GoDance-Timestamp: 1790245818
X-GoDance-Type: PURCHASE.UPDATED


X-GoDance-Log-Id


Uniquely identifies the action that triggered the webhook.


The same action may have several delivery attempts. For example, if a delivery fails and is later retried, all those attempts correspond to the same
X-GoDance-Log-Id.


Your integration should use this value to prevent processing the same action more than once.


X-GoDance-Delivery-Id


Identifies a specific delivery attempt.


If a webhook delivery is retried, a new X-GoDance-Delivery-Id will be generated, while the X-GoDance-Log-Id will remain the same.


Therefore:


Action
X-GoDance-Log-Id: A
│
├── Delivery 1 → X-GoDance-Delivery-Id: B
│
├── Delivery 2 → X-GoDance-Delivery-Id: C
│
└── Delivery 3 → X-GoDance-Delivery-Id: D


Use X-GoDance-Log-Id, not X-GoDance-Delivery-Id, as the idempotency key.


X-GoDance-Type


Indicates the event that triggered the notification.


For example:


PURCHASE.CREATED


The same type is included in the type property of the request body.


X-GoDance-Timestamp


Timestamp generated for the delivery.


This value is part of the data used to calculate the signature.


X-GoDance-Signature


HMAC-SHA256 signature generated using the webhook secret.


You must validate this signature before processing the received information.



Validate the signature


Each webhook has a secret with a format similar to:


gd_sec_********************************


go&dance uses this secret to generate an HMAC-SHA256 signature for each delivery.


The signature is calculated using the following concatenation:


{deliveryId}|{timestamp}|{body}


where:


  • deliveryId is the value of X-GoDance-Delivery-Id.
  • timestamp is the value of X-GoDance-Timestamp.
  • body is exactly the original request body as received in the HTTP request.


The calculation is equivalent to:


HMAC-SHA256(
deliveryId + "|" + timestamp + "|" + body,
secret
)


The result must match:


X-GoDance-Signature


Important: always use the original body


To validate the signature correctly, you must use the original request body exactly as it was received.


Do not parse, transform, modify, or reserialize the JSON before calculating the signature.


For example, this flow is incorrect:


Received body
↓
JSON.parse(...)
↓
JSON.stringify(...)
↓
Calculate signature ❌


Even if the resulting JSON represents exactly the same data, its representation may have changed.


Seemingly irrelevant changes such as spaces, line breaks, character escaping, or property order can produce different content and therefore a different HMAC
signature.


The correct flow is:


Original body received
│
├──────────────► Calculate and validate signature
│
▼
JSON.parse(...)
│
▼
Process event


First validate the signature using the original body. Then you can parse and process the JSON.



TypeScript example


The following example uses a Node.js HTTP server and preserves the original body before converting it to JSON:


import crypto from 'node:crypto';

const secret = 'gd_sec_...';

// Get the headers
const deliveryId = request.headers['x-godance-delivery-id'];
const timestamp = request.headers['x-godance-timestamp'];
const signature = request.headers['x-godance-signature'];
const logId = request.headers['x-godance-log-id'];

// IMPORTANT: get the original body without parsing or serializing it
const rawBody = request.rawBody;

// Calculate the expected signature
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(`${deliveryId}|${timestamp}|${rawBody}`)
.digest('hex');

// Validate the signature
if (signature !== expectedSignature) {
return response.status(401).send('Invalid signature');
}

// Prevent processing the same action twice
if (await alreadyProcessed(logId)) {
return response.status(200).send('OK');
}

// Once the signature has been validated, we can parse the body
const payload = JSON.parse(rawBody);

await processWebhook(payload);
await markAsProcessed(logId);

return response.status(200).send('OK');


If you use Express, NestJS, or another framework that automatically transforms the request body, make sure you preserve the raw body before the framework
processes it. The signature must always be calculated using the original bytes received.



Prevent duplicate processing


Webhooks should be processed idempotently.


The same action may generate several delivery attempts if, for example, a previous attempt failed.


Each attempt will have a different X-GoDance-Delivery-Id, but they will all share the same:


X-GoDance-Log-Id


Therefore, you should use X-GoDance-Log-Id to determine whether an action has already been processed.


A common strategy is:


  1. Receive the webhook and validate its signature.
  2. Check whether the X-GoDance-Log-Id already exists in your system.
  3. If it already exists, do not execute the operation again.
  4. If it does not exist, process the event and store the X-GoDance-Log-Id.


Do not use X-GoDance-Delivery-Id as the idempotency key. A retry of the same action will have a different delivery identifier.



Endpoint response


go&dance considers any HTTP response with a 2xx status code to be successful.


For example:


200 OK
201 Created
202 Accepted
204 No Content


Any 2xx response is considered a successful delivery.


Responses outside the 2xx range are considered failed deliveries and will appear as such in Webhook logs.



Payload


The request body is sent in JSON format:


Content-Type: application/json


Its general structure is:


{
  "type": "PURCHASE.UPDATED",
  "data": {
    ...
  }
}


The following events are currently available:


Event

Payload

Description

PURCHASE.CREATED

PurchaseDto

Triggered when a purchase is created, whether online or offline.

PURCHASE.UPDATED

PurchaseDto

Triggered when a purchase is updated.

PURCHASE.DELETED

DeletedProductDto

Triggered when a product is deleted from a purchase.


Payload is the data structure received in the data field.



Data structures


PurchaseDto


Field

Type

Required

Description

uuid

string

Yes

Unique identifier of the purchase.

recordLocator

string

Yes

Purchase record locator. This is the identifier the user sees in their purchase.

event

EventDto

Yes

Event associated with the purchase.

buyer

BuyerDto

No

Buyer information.

type

string

Yes

Origin of the purchase. It can contain manual for manually generated orders or online for purchases made through the platform.

amount

number

Yes

Purchase amount. Includes all additional charges such as booking fees, cancellation insurance, etc.

promoterCode

string

No

Associated promoter code, when applicable.

discountCode

string

No

Associated discount code, when applicable.

products

ProductDto[]

Yes

Products included in the purchase.

createdAt

string

Yes

Purchase creation date.


EventDto


Field

Type

Required

Description

uuid

string

Yes

Unique identifier of the event.

customId

string

No

Custom event identifier, when available.

name

string

Yes

Event name.


BuyerDto


Field

Type

Required

name

string

Yes

email

string

Yes


The buyer property may be absent or have a null value.


ProductDto


Each product has the following structure:


Field

Type

Required

Description

uuid

string

Yes

Unique identifier of the product.

ticketCode

string

No

Product record locator. This is the identifier the user sees in their purchase and the code used to scan the ticket with the mobile app.

type

string

Yes

Product type. Possible values for this field include ticket, distribution-fee, gateway-fee, cancellation-insurance, or others.

status

CANCELED

Yes

Product status.

onBehalfOfOrganization

boolean

Yes

Indicates whether the product belongs to the organizer or is a go&dance product, such as distribution fees.

ticket

TicketDto

No

Ticket information, when applicable.

amount

number

Yes

Product price. This is the sale price that you, as the organizer, have set for the product.

refundedAmount

number

Yes

If refunds have been issued, the amount refunded to the user for this product.

platformFee

number

Yes

Platform commission. This is the commission charged to the organizer for the sale of this ticket when the settlement is processed.

attendees

AttendeeDto[]

Yes

Attendees associated with the product.


Products with onBehalfOfOrganization = true are products sold on behalf of the organizer, such as event tickets. In these cases, the organizer is
responsible for the sale and, when applicable, for issuing the corresponding invoice to the buyer.


Products with onBehalfOfOrganization = false are products or services sold directly by go&dance. In these cases, go&dance is responsible for their
management and the corresponding invoicing. These are typically items such as **booking fees, Flexible Cancellation, or other services offered directly by

go&dance**.


PurchaseDeletedDto


Field

Type

Required

Description

uuid

string

Yes

Unique identifier of the purchase.

recordLocator

string

Yes

Purchase record locator. This is the identifier the user sees in their purchase.

productUuid

string

Yes

Unique identifier of the product.

ticketCode

string

Yes

Product record locator. This is the identifier the user sees in their purchase and the code used to scan the ticket with the mobile app.


The PURCHASE.DELETED event is sent by POST whenever a ticket is deleted from a purchase. The data field contains a DeletedProductDto with the purchase and deleted product identifiers.


If several tickets are deleted from the same purchase, go&dance sends one separate webhook request for each deleted ticket. For example, deleting three tickets from the same purchase generates three PURCHASE.DELETED requests, each containing the productUuid and ticketCode of the corresponding deleted ticket.


Important: do not assume that a single PURCHASE.DELETED request represents all tickets deleted from a purchase. Process each request independently.


Product types


Product types currently in use include:


ticket
distribution-fee
gateway-fee
cancellation-insurance


The integration must be prepared to receive other type values in the future.


Do not assume that all products are tickets. Depending on the type, properties such as ticket may be absent or null.


TicketDto


When the product corresponds to a ticket, ticket contains:


Field

Type

Required

uuid

string

Yes

customId

string null

No

name

string

Yes


AttendeeDto


Each element in attendees has the following structure:


Field

Type

Required

Description

uuid

string

Yes

Unique identifier of the attendee.

position

number

Yes

Attendee's position within the product.

completed

boolean

Yes

Indicates whether the attendee information is complete.

values

object

Yes

Values associated with the attendee information fields.


The contents of values are dynamic and depend on the information requested for the ticket.


For example:


{
  "fullName": "Example Attendee",
  "email": "attendee@example.com",
  "gender": "male",
  "role": "leader",
  "residenceCountry": "IT",
  "phone": "+00 000 000 000"
}


The integration should not assume that these properties will always be present or that they are the only properties available.



Associate go&dance events and tickets with your own IDs


If you integrate go&dance with your own system, you can use custom IDs (customId) to associate go&dance events and tickets with the corresponding records
in your database.


You can assign your own alphanumeric ID to each event and each ticket type. This allows you to identify them using the same references you already use in
your system.


Alternatively, you can store the go&dance uuid values in your database and use those identifiers for the association. You can choose whichever approach
best fits your integration.


Configure the custom ID for an event


To assign your own ID to an event, go to:


Event page > Configuration > Additional event information


Enter your identifier in the Custom ID field.


This value is returned in the webhook payload as:


event.customId


For example:


{
  "event": {
    "uuid": "goanddance-event-uuid",
    "customId": "MY-EVENT-2026",
    "name": "My Event"
  }
}


You can then use either uuid or customId to associate the event with the corresponding event in your database.


Configure the custom ID for a ticket


You can also assign your own ID to each ticket type.


Edit the ticket and open the Advanced tab. Enter your identifier in the Custom ID field.


This value is returned in the webhook payload inside ticket:


products[].ticket.customId


For example:


{
  "ticket": {
    "uuid": "goanddance-ticket-uuid",
    "customId": "FULL-PASS-2026",
    "name": "Full Pass"
  }
}


This allows you to associate each go&dance ticket type with the corresponding product, ticket, or record in your own database.


Which identifier should I use?


Both approaches are valid:


  • Use customId if you want go&dance to return the identifiers you already use in your own system.
  • Use uuid if you prefer to store go&dance identifiers in your database and use them to create the association.


For example:


Your database                  go&dance
────────────────────────────────────────────
Event EVT-2026 ←────→ event.customId
Full Pass FP-01 ←────→ ticket.customId


or:


Your database                  go&dance
────────────────────────────────────────────
Event ←────→ event.uuid
Ticket type ←────→ ticket.uuid


Important: customId is optional. Your integration should therefore not assume that it will always have a value. If you rely on customId to associate
records between systems, make sure you have configured it for the corresponding events and tickets.



Security and best practices


For a secure and reliable integration:


  • Use HTTPS for the destination URL.
  • Store the webhook secret securely.
  • Do not expose the secret in client-side code or public repositories.
  • Always validate X-GoDance-Signature before processing the payload.
  • Calculate the signature exclusively using the original body received, without parsing or reserializing it.
  • Use a timing-attack-resistant comparison when verifying the signature.
  • Use X-GoDance-Log-Id as the idempotency key.
  • Do not use X-GoDance-Delivery-Id to detect duplicate actions.
  • Do not assume that product types are limited to the values currently known.
  • Regenerate the secret if you believe it may have been compromised.

Updated on: 29/09/2026

Was this article helpful?

Share your feedback

Cancel

Thank you!