Webhooks
Los webhooks permiten recibir notificaciones en tiempo real en tu propio sistema cuando se producen determinadas acciones en go&dance, como cuando se crea o actualiza una compra.
En lugar de consultar periódicamente go&dance para comprobar si se han producido cambios, puedes configurar una URL de destino a la que go&dance enviará automáticamente una petición HTTP cada vez que ocurra uno de los eventos compatibles con la integración.
Una vez que los webhooks estén activados para tu cuenta, se aplicarán a todos los eventos de los que seas propietario. No es necesario configurar los webhooks por separado para cada evento.
Cada notificación incluye información sobre el evento que la ha generado y los datos correspondientes, lo que te permite integrar go&dance con tus propias aplicaciones, bases de datos, CRM, ERP u otros sistemas externos.
Importante: los webhooks solo se generan para los eventos de los que tu usuario es propietario. Los eventos a los que tengas acceso o que puedas gestionar, pero de los que no seas propietario, no están incluidos.
Índice
- Cabeceras de la solicitud
- Validar la firma
- Evitar el procesamiento duplicado
- Respuesta del endpoint
- Payload
- Estructuras de datos
- Asociar eventos y entradas de go&dance con tus propios ID
- Seguridad y buenas prácticas
Cuando ocurre uno de los eventos suscritos por un webhook activo, go&dance envía una solicitud HTTP a la URL de destino configurada.
La solicitud contiene:
- Cabeceras con información sobre el evento y la entrega.
- Un
bodycon formato JSON que contiene los datos del evento. - Una firma HMAC-SHA256 que permite verificar que la solicitud fue generada por go&dance.
Cabeceras de la solicitud
Las cabeceras específicas enviadas por go&dance son:
Cabecera | Descripción |
|---|---|
| Identificador único de la acción que desencadenó el webhook. Se mantiene igual en los reintentos y debe usarse para evitar procesar la misma acción más de una vez. |
| Identificador único de un intento de entrega. Cada reintento genera un identificador nuevo. |
| Firma HMAC-SHA256 utilizada para verificar la autenticidad de la solicitud. |
| Marca de tiempo utilizada como parte del cálculo de la firma. |
| Tipo de evento que desencadenó la notificación. |
Ejemplo:
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.UPDATEDX-GoDance-Log-Id
Identifica de forma única la acción que desencadenó el webhook.
La misma acción puede tener varios intentos de entrega. Por ejemplo, si una entrega falla y se reintenta más tarde, todos esos intentos corresponden al mismoX-GoDance-Log-Id.
Tu integración debe usar este valor para evitar procesar la misma acción más de una vez.
X-GoDance-Delivery-Id
Identifica un intento de entrega concreto.
Si se reintenta la entrega de un webhook, se generará un nuevo X-GoDance-Delivery-Id, mientras que el X-GoDance-Log-Id se mantendrá igual.
Por tanto:
Acción
X-GoDance-Log-Id: A
│
├── Entrega 1 → X-GoDance-Delivery-Id: B
│
├── Entrega 2 → X-GoDance-Delivery-Id: C
│
└── Entrega 3 → X-GoDance-Delivery-Id: D
Usa X-GoDance-Log-Id, no X-GoDance-Delivery-Id, como clave de idempotencia.
X-GoDance-Type
Indica el evento que desencadenó la notificación.
Por ejemplo:
PURCHASE.CREATEDEl mismo tipo se incluye en la propiedad type del cuerpo de la solicitud.
X-GoDance-Timestamp
Marca de tiempo generada para la entrega.
Este valor forma parte de los datos usados para calcular la firma.
X-GoDance-Signature
Firma HMAC-SHA256 generada con el secreto del webhook.
Debes validar esta firma antes de procesar la información recibida.
Validar la firma
Cada webhook tiene un secreto con un formato similar a:
gd_sec_********************************go&dance utiliza este secreto para generar una firma HMAC-SHA256 para cada entrega.
La firma se calcula mediante la siguiente concatenación:
{deliveryId}|{timestamp}|{body}donde:
deliveryIdes el valor deX-GoDance-Delivery-Id.timestampes el valor deX-GoDance-Timestamp.bodyes exactamente el cuerpo original de la solicitud tal como se recibió en la solicitud HTTP.
El cálculo equivale a:
HMAC-SHA256(
deliveryId + "|" + timestamp + "|" + body,
secret
)
El resultado debe coincidir con:
X-GoDance-SignatureImportante: usa siempre el cuerpo original
Para validar correctamente la firma, debes utilizar el cuerpo original de la solicitud exactamente como se recibió.
No analices, transformes, modifiques ni reserialices el JSON antes de calcular la firma.
Por ejemplo, este flujo es incorrecto:
Cuerpo recibido
↓
JSON.parse(...)
↓
JSON.stringify(...)
↓
Calcular firma ❌
Aunque el JSON resultante represente exactamente los mismos datos, su representación puede haber cambiado.
Cambios aparentemente irrelevantes, como espacios, saltos de línea, escape de caracteres u orden de las propiedades, pueden producir contenido distinto y, por
tanto, una firma HMAC diferente.
El flujo correcto es:
Cuerpo original recibido
│
├──────────────► Calcular y validar firma
│
▼
JSON.parse(...)
│
▼
Procesar evento
Primero valida la firma usando el cuerpo original. Después puedes analizar y procesar el JSON.
Ejemplo en TypeScript
El siguiente ejemplo utiliza un servidor HTTP de Node.js y conserva el cuerpo original antes de convertirlo a JSON:
import crypto from 'node:crypto';
const secret = 'gd_sec_...';
// Obtener las cabeceras
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'];
// IMPORTANTE: obtener el cuerpo original sin analizarlo ni serializarlo
const rawBody = request.rawBody;
// Calcular la firma esperada
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(`${deliveryId}|${timestamp}|${rawBody}`)
.digest('hex');
// Validar la firma
if (signature !== expectedSignature) {
return response.status(401).send('Firma no válida');
}
// Evitar procesar la misma acción dos veces
if (await alreadyProcessed(logId)) {
return response.status(200).send('OK');
}
// Una vez validada la firma, podemos analizar el cuerpo
const payload = JSON.parse(rawBody);
await processWebhook(payload);
await markAsProcessed(logId);
return response.status(200).send('OK');
Si usas Express, NestJS u otro framework que transforma automáticamente el cuerpo de la solicitud, asegúrate de conservar el cuerpo sin procesar antes de
que el framework lo procese. La firma siempre debe calcularse usando los bytes originales recibidos.
Evitar el procesamiento duplicado
Los webhooks deben procesarse de forma idempotente.
La misma acción puede generar varios intentos de entrega si, por ejemplo, un intento anterior falló.
Cada intento tendrá un X-GoDance-Delivery-Id diferente, pero todos compartirán el mismo:
X-GoDance-Log-IdPor tanto, debes usar X-GoDance-Log-Id para determinar si una acción ya se ha procesado.
Una estrategia habitual es:
- Recibir el webhook y validar su firma.
- Comprobar si el
X-GoDance-Log-Idya existe en tu sistema. - Si ya existe, no ejecutar la operación de nuevo.
- Si no existe, procesar el evento y guardar el
X-GoDance-Log-Id.
No uses
X-GoDance-Delivery-Idcomo clave de idempotencia. Un reintento de la misma acción tendrá un identificador de entrega diferente.
Respuesta del endpoint
go&dance considera satisfactoria cualquier respuesta HTTP con un código de estado 2xx.
Por ejemplo:
200 OK
201 Created
202 Accepted
204 No Content
Cualquier respuesta 2xx se considera una entrega satisfactoria.
Las respuestas fuera del rango 2xx se consideran entregas fallidas y aparecerán como tales en los registros de webhooks.
Payload
El cuerpo de la solicitud se envía en formato JSON:
Content-Type: application/jsonSu estructura general es:
{
"type": "PURCHASE.UPDATED",
"data": {
...
}
}Actualmente están disponibles los siguientes eventos:
Evento | Payload | Descripción |
|---|---|---|
|
| Se desencadena al crear una compra, ya sea en línea o presencial. |
|
| Se desencadena al actualizar una compra. |
|
| Se desencadena al eliminar un producto de una compra. |
Payload es la estructura de datos recibida en el campo data.
Estructuras de datos
PurchaseDto
Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
|
| Sí | Identificador único de la compra. |
|
| Sí | Localizador de la compra. Es el identificador que el usuario ve en su compra. |
|
| Sí | Evento asociado a la compra. |
|
| No | Información del comprador. |
|
| Sí | Origen de la compra. Puede contener |
|
| Sí | Importe de la compra. Incluye todos los cargos adicionales, como gastos de gestión, seguro de cancelación, etc. |
|
| No | Código de promotor asociado, cuando corresponda. |
|
| No | Código de descuento asociado, cuando corresponda. |
|
| Sí | Productos incluidos en la compra. |
|
| Sí | Fecha de creación de la compra. |
EventDto
Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
|
| Sí | Identificador único del evento. |
|
| No | Identificador personalizado del evento, si está disponible. |
|
| Sí | Nombre del evento. |
BuyerDto
Campo | Tipo | Obligatorio |
|---|---|---|
|
| Sí |
|
| Sí |
La propiedad buyer puede estar ausente o tener un valor null.
ProductDto
Cada producto tiene la siguiente estructura:
Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
|
| Sí | Identificador único del producto. |
|
| No | Localizador del producto. Es el identificador que el usuario ve en su compra y el código usado para escanear la entrada con la aplicación móvil. |
|
| Sí | Tipo de producto. Los valores posibles incluyen |
|
| Sí | Estado del producto. |
|
| Sí | Indica si el producto pertenece al organizador o es un producto de go&dance, como los gastos de distribución. |
|
| No | Información de la entrada, cuando corresponda. |
|
| Sí | Precio del producto. Es el precio de venta que tú, como organizador, has establecido para el producto. |
|
| Sí | Si se han emitido reembolsos, el importe reembolsado al usuario por este producto. |
|
| Sí | Comisión de la plataforma. Es la comisión cobrada al organizador por la venta de esta entrada al procesar la liquidación. |
|
| Sí | Asistentes asociados al producto. |
Los productos con
onBehalfOfOrganization = trueson productos vendidos en nombre del organizador, como las entradas del evento. En estos casos,
el organizador es responsable de la venta y, cuando corresponda, de emitir la factura correspondiente al comprador.
Los productos con
onBehalfOfOrganization = falseson productos o servicios vendidos directamente por go&dance. En estos casos, go&dance es responsable
de su gestión y de la facturación correspondiente. Normalmente son conceptos como **gastos de gestión, Cancelación Flexible u otros servicios ofrecidos
por go&dance**.
PurchaseDeletedDto
Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
|
| Sí | Identificador único de la compra. |
|
| Sí | Localizador de la compra. Es el identificador que el usuario ve en su compra. |
|
| Sí | Identificador único del producto. |
|
| Sí | Localizador del producto. Es el identificador que el usuario ve en su compra y el código usado para escanear la entrada con la aplicación móvil. |
El evento PURCHASE.DELETED se envía mediante POST cada vez que se elimina una entrada de una compra. El campo data contiene un DeletedProductDto con los identificadores de la compra y del producto eliminado.
Si se eliminan varias entradas de una misma compra, go&dance envía una petición de webhook independiente por cada entrada eliminada. Por ejemplo, si se eliminan tres entradas de una misma compra, se generarán tres peticiones PURCHASE.DELETED, cada una con el productUuid y el ticketCode de la entrada eliminada correspondiente.
Importante: no asumas que una única petición
PURCHASE.DELETEDrepresenta todas las entradas eliminadas de una compra. Procesa cada petición de forma independiente.
Tipos de producto
Los tipos de producto actualmente en uso incluyen:
ticket
distribution-fee
gateway-fee
cancellation-insurance
La integración debe estar preparada para recibir otros valores de type en el futuro.
No asumas que todos los productos son entradas. Según el
type, propiedades comoticketpueden estar ausentes o tener valornull.
TicketDto
Cuando el producto corresponde a una entrada, ticket contiene:
Campo | Tipo | Obligatorio |
|---|---|---|
|
| Sí |
|
| No |
|
| Sí |
AttendeeDto
Cada elemento de attendees tiene la siguiente estructura:
Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
|
| Sí | Identificador único del asistente. |
|
| Sí | Posición del asistente dentro del producto. |
|
| Sí | Indica si la información del asistente está completa. |
|
| Sí | Valores asociados a los campos de información del asistente. |
El contenido de values es dinámico y depende de la información solicitada para la entrada.
Por ejemplo:
{
"fullName": "Asistente de ejemplo",
"email": "attendee@example.com",
"gender": "male",
"role": "leader",
"residenceCountry": "IT",
"phone": "+00 000 000 000"
}La integración no debe asumir que estas propiedades siempre estarán presentes ni que son las únicas propiedades disponibles.
Asociar eventos y entradas de go&dance con tus propios ID
Si integras go&dance con tu propio sistema, puedes usar ID personalizados (customId) para asociar los eventos y entradas de go&dance con los registros
correspondientes de tu base de datos.
Puedes asignar tu propio ID alfanumérico a cada evento y a cada tipo de entrada. Esto te permite identificarlos mediante las mismas referencias que ya usas
en tu sistema.
También puedes guardar los valores de uuid de go&dance en tu base de datos y usar esos identificadores para la asociación. Puedes elegir el enfoque que
mejor se adapte a tu integración.
Configurar el ID personalizado de un evento
Para asignar tu propio ID a un evento, ve a:
Página del evento > Configuración > Información adicional del evento
Introduce tu identificador en el campo ID personalizado.
Este valor se devuelve en el payload del webhook como:
event.customIdPor ejemplo:
{
"event": {
"uuid": "goanddance-event-uuid",
"customId": "MY-EVENT-2026",
"name": "Mi evento"
}
}Después puedes usar uuid o customId para asociar el evento con el evento correspondiente de tu base de datos.
Configurar el ID personalizado de una entrada
También puedes asignar tu propio ID a cada tipo de entrada.
Edita la entrada y abre la pestaña Avanzado. Introduce tu identificador en el campo ID personalizado.
Este valor se devuelve en el payload del webhook dentro de ticket:
products[].ticket.customIdPor ejemplo:
{
"ticket": {
"uuid": "goanddance-ticket-uuid",
"customId": "FULL-PASS-2026",
"name": "Pase completo"
}
}Esto permite asociar cada tipo de entrada de go&dance con el producto, entrada o registro correspondiente de tu propia base de datos.
¿Qué identificador debo usar?
Ambos enfoques son válidos:
- Usa
customIdsi quieres que go&dance devuelva los identificadores que ya utilizas en tu propio sistema. - Usa
uuidsi prefieres guardar los identificadores de go&dance en tu base de datos y utilizarlos para crear la asociación.
Por ejemplo:
Tu base de datos go&dance
────────────────────────────────────────────
Evento EVT-2026 ←────→ event.customId
Pase completo FP-01 ←────→ ticket.customId
o:
Tu base de datos go&dance
────────────────────────────────────────────
Evento ←────→ event.uuid
Tipo de entrada ←────→ ticket.uuid
Importante:
customIdes opcional. Por tanto, tu integración no debe asumir que siempre tendrá un valor. Si dependes decustomIdpara asociar registros
entre sistemas, asegúrate de haberlo configurado para los eventos y entradas correspondientes.
Seguridad y buenas prácticas
Para una integración segura y fiable:
- Usa HTTPS para la URL de destino.
- Guarda el secreto del webhook de forma segura.
- No expongas el secreto en código del lado cliente ni en repositorios públicos.
- Valida siempre
X-GoDance-Signatureantes de procesar el payload. - Calcula la firma exclusivamente usando el cuerpo original recibido, sin analizarlo ni reserializarlo.
- Usa una comparación resistente a ataques de temporización al verificar la firma.
- Usa
X-GoDance-Log-Idcomo clave de idempotencia. - No uses
X-GoDance-Delivery-Idpara detectar acciones duplicadas. - No asumas que los tipos de producto están limitados a los valores conocidos actualmente.
- Regenera el secreto si crees que puede haberse visto comprometido.
Actualizado el: 29/09/2026
¡Gracias!