Ir al contenido
Soporte

Webhooks

Los webhooks le permiten recibir notificaciones en tiempo real cuando ocurren eventos en su cuenta de LabelGrid. Use webhooks para automatizar flujos de trabajo e integrar con sistemas externos.

Para Desarrolladores: También puede gestionar webhooks programáticamente a través de la API. Consulte la Documentación de la API de LabelGrid para endpoints y ejemplos.

  1. Configure un webhook** - Especifique una URL y qué eventos escuchar
  2. Ocurre un evento - Por ejemplo, un lanzamiento se entrega a una tienda
  3. LabelGrid envía una solicitud POST - Su servidor recibe los datos del evento
  4. Su sistema lo procesa - Automatiza flujos de trabajo basados en el evento

  1. Haga clic en su ícono de perfil en la esquina superior derecha
  2. Seleccione Webhooks del menú desplegable

  1. Haga clic en Create Webhook
  2. Ingrese un Name para identificar este webhook
  3. Ingrese la URL donde quiere recibir notificaciones
  4. Seleccione qué Events deben activar este webhook
  5. Haga clic en Create

Cuando crea un webhook, recibirá una clave secreta. Úsala para verificar que las solicitudes entrantes son realmente de LabelGrid:

  • Almacena el secreto de forma segura
  • Verifique la firma en las solicitudes entrantes
  • Si está comprometido, regenera el secreto

Configure su webhook para escuchar estos eventos. El Identificador de Evento es el valor que verá en la propiedad event del payload y en el encabezado X-Webhook-Event:

Identificador de EventoDescripción
delivery.completedSe activa cuando un lanzamiento se entrega exitosamente a un outlet
delivery.failedSe activa cuando la entrega a un outlet falla
takedown.completedSe activa cuando una solicitud de retiro se completa
release.review.status_changedSe activa cuando el estado de revisión de un lanzamiento cambia
release.preflight.report_readySe activa cuando el informe de Preflight QC de un lanzamiento en espera está listo para obtenerse
stream_radar.flag_createdSe activa cuando se genera un aviso de Stream Radar, o cuando un aviso resuelto se reabre tras una nueva detección
stream_radar.flag_resolvedSe activa cuando un aviso de Stream Radar se resuelve porque cesaron las detecciones
release.distributedSe activa cuando un lanzamiento es distribuido
payment.statement_readySe activa cuando una declaración de pago está lista para visualizar
transcode.completedSe activa cuando la transcodificación de audio de una pista finaliza correctamente
transcode.failedSe activa cuando la transcodificación de una pista falla o termina incompleta
distribution.outlet.status_changedSe activa en cada transición del estado de distribución por outlet

Puede seleccionar múltiples eventos para un solo webhook, o crear webhooks separados para diferentes tipos de eventos.

También puede leer esta lista de forma programática: GET /api/public/webhooks/event-types devuelve cada evento junto con un esquema data que describe las claves y los tipos de su carga útil, de modo que los consumidores con esquema estricto pueden ampliar su validación de entrada con antelación.


La lista de webhooks muestra:

ColumnaDescripción
NameEl nombre del webhook que asignó
URLDonde se envían las notificaciones
EventsNúmero de eventos configurados
StatusActivo o Inactivo
Success / FailConteo de entregas exitosas y fallidas
Last TriggeredCuándo se llamó al webhook por última vez
  1. Haga clic en la acción Edit en la fila del webhook
  2. Modifica el nombre, URL o eventos
  3. Haga clic en Save

Cambie el estado activo de un webhook sin eliminarlo:

  • Active - El webhook recibirá notificaciones
  • Inactive - El webhook está pausado, no se envían notificaciones
  1. Haga clic en la acción Delete en la fila del webhook
  2. Confirme la eliminación

Antes de depender de un webhook en producción, pruébalo:

  1. Haga clic en la acción Test en su webhook
  2. LabelGrid envía una carga útil de prueba a su URL
  3. Verifique que su endpoint recibió y procesó correctamente

Monitoree la actividad del webhook y soluciona problemas:

  1. Haga clic en la acción View Logs en un webhook
  2. Ve un historial de todas las entregas del webhook

Cada entrada de registro muestra:

CampoDescripción
Event TypeQué evento activó esta entrega
Response StatusCódigo de estado HTTP de su servidor
DurationCuánto tiempo tomó la solicitud
AttemptNúmero de intento de reintento
TimestampCuándo ocurrió la entrega

Cuando ocurre un evento, LabelGrid envía una solicitud POST a su URL con una carga útil JSON:

{
"event": "delivery.completed",
"timestamp": "2026-05-05T10:00:00+00:00",
"webhook_id": "123",
"data": {
// Datos específicos del evento
}
}

El campo timestamp use el formato ISO 8601. webhook_id es el ID de su webhook configurado (coincide con el encabezado X-Webhook-Id).


La estructura del objeto data depende del tipo de event. Todos los tipos de campos a continuación son tipos JSON tal como se serializan en la carga útil.

Se activa una vez por outlet cuando la entrega de un lanzamiento alcanza un estado de éxito terminal.

{
"event": "delivery.completed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"distro_queue_id": 456,
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"outlet_id": 12,
"outlet_name": "Spotify",
"status": "complete"
}
}
CampoTipoDescripción
distro_queue_idintegerID interno de la cola para este intento de entrega
release_idintegerEl lanzamiento que fue entregado
label_idintegerEl sello propietario del lanzamiento, para que pueda enrutar el evento sin una consulta adicional
release_catstring | nullSu referencia de catálogo del lanzamiento
outlet_idinteger | nullEl ID del outlet de destino
outlet_namestring | nullNombre legible del outlet (por ejemplo, "Spotify")
statusstringSiempre "complete" para este evento

Se activa una vez por outlet cuando la entrega de un lanzamiento alcanza un estado de fallo terminal. Misma carga útil que delivery.completed más un campo message.

{
"event": "delivery.failed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"distro_queue_id": 456,
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"outlet_id": 12,
"outlet_name": "Spotify",
"status": "error",
"message": "Outlet rejected the delivery: missing ISRC."
}
}
CampoTipoDescripción
statusstringUno de error, fault, rejected, batch_exception
messagestring | nullMotivo del fallo desde el outlet o el pipeline de distribución

Se activa una vez por outlet cuando una solicitud de retiro tiene éxito. Misma forma que delivery.completed más un indicador takedown: true.

{
"event": "takedown.completed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"distro_queue_id": 456,
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"outlet_id": 12,
"outlet_name": "Spotify",
"status": "complete",
"takedown": true
}
}

Se activa una vez por lanzamiento cuando el lanzamiento transita al estado de entrega distributed. Solo se activa en la transición a distributed, no en guardados posteriores mientras el lanzamiento ya está distribuido.

{
"event": "release.distributed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"release_title": "Summer EP",
"delivery_status": "distributed"
}
}

Se activa cuando un lanzamiento cambia entre estados de revisión.

{
"event": "release.review.status_changed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"release_title": "Summer EP",
"previous_status": "to_review",
"new_status": "approved"
}
}
CampoTipoDescripción
previous_statusstringEstado anterior. Uno de draft, to_review, approved, rejected, require_changes, audit
new_statusstringEstado nuevo. El mismo conjunto de valores
review_issuesarray (opcional)Presente solo en las transiciones a require_changes y rejected: los problemas que necesitan su atención. La clave se omite en cualquier otra transición, así que no asuma que siempre está presente

Se activa cuando el informe de calidad de Preflight QC para un lanzamiento en la espera previa a la revisión está listo para obtenerse. Requiere el complemento Preflight QC en su cuenta.

{
"event": "release.preflight.report_ready",
"timestamp": "2026-07-07T10:00:00+00:00",
"webhook_id": "123",
"data": {
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"release_title": "Summer EP",
"generated_at": "2026-07-07T09:58:12+00:00",
"profile": { "name": "quality_report", "version": 2 },
"counts": { "blocking": 1, "informational": 2, "requires_feedback": 1 }
}
}
CampoTipoDescripción
release_idintegerEl lanzamiento al que pertenece el informe
label_idintegerEl sello propietario del lanzamiento
release_catstring | nullSu referencia de catálogo del lanzamiento
release_titlestring | nullEl título del lanzamiento
generated_atstringCuándo finalizaron las comprobaciones (ISO 8601). Coincide con el report.generated_at del endpoint del informe de calidad
profileobjectEl perfil de calidad con el que se calcularon los recuentos: {name, version}
countsobjectSolo recuentos agregados: {blocking, informational, requires_feedback}. El recuento requires_feedback se solapa con los otros dos

Se activa cuando se genera un aviso de Stream Radar: ya sea un aviso completamente nuevo o un aviso previamente resuelto que se reabre tras una nueva detección. Requiere el complemento Stream Radar en su cuenta. El campo transition distingue los dos casos: published para un aviso nuevo, reopened para uno que ha vuelto a estar activo.

{
"event": "stream_radar.flag_created",
"timestamp": "2026-07-07T10:00:00+00:00",
"webhook_id": "123",
"data": {
"flag_id": 4501,
"dsp": "spotify",
"isrc": "USRC12345678",
"release_id": 789,
"track_id": 654,
"severity": "high",
"status": "active",
"transition": "published",
"first_detected_at": "2026-07-06T00:00:00+00:00",
"last_detected_at": "2026-07-07T00:00:00+00:00",
"estimated_affected_streams": 12500,
"published_at": "2026-07-07T09:58:12+00:00",
"resolved_at": null
}
}
CampoTipoDescripción
flag_idintegerEl identificador estable del aviso; coincide con el id de los endpoints de Stream Radar
dspstringLa plataforma en la que se observó el patrón (p. ej. spotify)
isrcstringEl ISRC de la grabación implicada
release_idintegerEl lanzamiento al que pertenece la grabación
track_idinteger | nullLa pista concreta, cuando el ISRC corresponde de forma inequívoca a una de sus pistas
severitystringlow, medium o high
statusstringactive para este evento
transitionstringpublished para un aviso nuevo, reopened cuando un aviso resuelto ha vuelto a estar activo
first_detected_atstring | nullCuándo se detectó por primera vez el patrón para esta pista y plataforma (ISO 8601)
last_detected_atstring | nullLa detección más reciente (ISO 8601)
estimated_affected_streamsinteger | nullUna estimación de cuántas reproducciones están implicadas
published_atstringCuándo se le comunicó el aviso por primera vez (ISO 8601)
resolved_atstring | nullnull mientras el aviso está activo

Se activa cuando un aviso de Stream Radar se resuelve porque cesaron las detecciones. Requiere el complemento Stream Radar. Los mismos campos que stream_radar.flag_created (sin transition), con status en resolved y resolved_at con valor.

{
"event": "stream_radar.flag_resolved",
"timestamp": "2026-07-14T10:00:00+00:00",
"webhook_id": "123",
"data": {
"flag_id": 4501,
"dsp": "spotify",
"isrc": "USRC12345678",
"release_id": 789,
"track_id": 654,
"severity": "high",
"status": "resolved",
"first_detected_at": "2026-07-06T00:00:00+00:00",
"last_detected_at": "2026-07-12T00:00:00+00:00",
"estimated_affected_streams": 18700,
"published_at": "2026-07-07T09:58:12+00:00",
"resolved_at": "2026-07-14T09:55:03+00:00"
}
}

Se activa cuando se genera una declaración de pago y está lista para visualizar.

{
"event": "payment.statement_ready",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"payment_request_id": 1024,
"invoice_number": "INV-2026-001",
"period": "2026-04-30",
"amount": 1234.56,
"total_due_usd": 1234.56,
"currency": "USD"
}
}
CampoTipoDescripción
payment_request_idintegerID interno de la solicitud de pago
invoice_numberstringReferencia de factura para la declaración
periodstring | nullFecha de fin de periodo (fecha ISO 8601, YYYY-MM-DD)
amountnumberMonto de la declaración en la moneda currency
total_due_usdnumberTotal de la declaración convertido a USD
currencystringCódigo de moneda ISO 4217 (por defecto USD)

Se activa cuando la transcodificación de audio de una pista finaliza. transcode.completed se activa en caso de éxito; transcode.failed se activa cuando la transcodificación falla o termina incompleta. Ambos comparten la misma forma de carga útil.

{
"event": "transcode.completed",
"timestamp": "2026-07-07T10:00:00+00:00",
"webhook_id": "123",
"data": {
"release_id": 789,
"label_id": 321,
"track_id": 654,
"transcoder_queue_id": 987,
"status": "complete",
"status_message": "transcode_complete",
"files": [
{ "asset_type_id": 2, "status": "complete" }
]
}
}
CampoTipoDescripción
release_idintegerEl lanzamiento al que pertenece la pista
label_idintegerEl sello propietario del lanzamiento
track_idintegerLa pista que se transcodificó
transcoder_queue_idintegerID interno de la cola de transcodificación
statusstringEstado bruto de la cola: complete, error o incomplete
status_messagestringCódigo de motivo seguro y enumerado: transcode_complete, transcode_error o transcode_incomplete
filesarrayDetalle por archivo de la pista: {asset_type_id, status} por cada archivo transcodificado

Se activa en cada transición del estado de distribución por outlet (por ejemplo, scheduled → transcoding → batched → complete), no solo en las terminales que cubren delivery.completed, delivery.failed y takedown.completed. Este evento es verboso por diseño: suscríbase a él solo si quiere la progresión completa por outlet.

{
"event": "distribution.outlet.status_changed",
"timestamp": "2026-07-07T10:00:00+00:00",
"webhook_id": "123",
"data": {
"distro_queue_id": 456,
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"outlet_id": 12,
"outlet_name": "Spotify",
"previous_status": "transcoding",
"status": "batched"
}
}
CampoTipoDescripción
distro_queue_idintegerID interno de la cola para esta entrega
release_idintegerEl lanzamiento que se está distribuyendo
label_idintegerEl sello propietario del lanzamiento
release_catstring | nullSu referencia de catálogo del lanzamiento
outlet_idinteger | nullEl ID del outlet de destino
outlet_namestring | nullNombre legible del outlet
previous_statusstring | nullEl estado anterior; null cuando la fila no tenía un estado anterior reconocido
statusstringEl nuevo estado

Cada entrega de webhook está firmada para que pueda verificar que realmente proviene de LabelGrid. Siempre verifica la firma antes de procesar el evento.

Cada solicitud POST de webhook incluye estos encabezados:

EncabezadoDescripción
X-Webhook-SignatureHMAC-SHA256 del cuerpo de la solicitud sin procesar, en hexadecimal en minúsculas, sin prefijo de algoritmo
X-Webhook-TimestampMarca de tiempo ISO 8601 de la entrega (mismo valor que la propiedad timestamp en el cuerpo)
X-Webhook-EventIdentificador del evento (por ejemplo, delivery.completed)
X-Webhook-IdEl ID del webhook que recibe la entrega
User-AgentLabelGrid-Webhooks/1.0
Content-Typeapplication/json
  • Algoritmo: HMAC-SHA256
  • Codificación: Hexadecimal en minúsculas
  • Prefijo: Ninguno — el valor es solo el digest hexadecimal, no sha256=...
  • Contenido firmado: El cuerpo JSON sin procesar completo de la solicitud (el cuerpo incluye la marca de tiempo como propiedad, por lo que la marca de tiempo se firma implícitamente)
  1. Lea el cuerpo sin procesar de la solicitud antes de cualquier análisis o transformación JSON. Re-serializar el JSON ya parseado puede producir bytes diferentes y romper la firma.
  2. Calcule HMAC-SHA256(cuerpo_sin_procesar, tu_secreto_de_webhook) y obtén el digest hexadecimal en minúsculas.
  3. Compare con X-Webhook-Signature usando una comparación de tiempo constante.
  4. (Recomendado) Rechaza la solicitud si X-Webhook-Timestamp es más antigua que su ventana de tolerancia de repetición — sugerimos 5 minutos.
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$expected = hash_hmac('sha256', $rawBody, $webhookSecret);
if (! hash_equals($expected, $signature)) {
http_response_code(401);
exit('Firma no válida');
}
// Rechazar entregas de más de 5 minutos
if (abs(time() - strtotime($timestamp)) > 300) {
http_response_code(401);
exit('Entrega obsoleta');
}
$payload = json_decode($rawBody, true);
// ... procesar el evento
http_response_code(200);
const crypto = require('crypto');
// Express: capturar el cuerpo sin procesar ANTES de cualquier middleware JSON
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const rawBody = req.body; // Buffer
const signature = req.header('X-Webhook-Signature') || '';
const timestamp = req.header('X-Webhook-Timestamp') || '';
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(rawBody)
.digest('hex');
const sigBuf = Buffer.from(signature, 'hex');
const expBuf = Buffer.from(expected, 'hex');
if (sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(sigBuf, expBuf)) {
return res.status(401).send('Firma no válida');
}
if (Math.abs(Date.now() - new Date(timestamp).getTime()) > 5 * 60 * 1000) {
return res.status(401).send('Entrega obsoleta');
}
const payload = JSON.parse(rawBody.toString('utf8'));
// ... procesar el evento
res.sendStatus(200);
});
import hmac, hashlib
from datetime import datetime, timezone
raw_body = request.get_data() # Flask: bytes, antes de cualquier parseo JSON
signature = request.headers.get('X-Webhook-Signature', '')
timestamp = request.headers.get('X-Webhook-Timestamp', '')
expected = hmac.new(
webhook_secret.encode('utf-8'),
raw_body,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature):
return ('Firma no válida', 401)
delivery_time = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
if abs((datetime.now(timezone.utc) - delivery_time).total_seconds()) > 300:
return ('Entrega obsoleta', 401)
return ('', 200) # procesar el evento, luego responder
  • Re-serializar el cuerpo antes de hacer el hash. Los frameworks que parsean JSON automáticamente (Express express.json(), el cuerpo de solicitud por defecto de Laravel) pierden los bytes originales. Captura primero el cuerpo sin procesar.
  • Usar una comparación que no sea de tiempo constante (==, ===). Es susceptible a ataques de temporización — siempre usa hash_equals (PHP), crypto.timingSafeEqual (Node), hmac.compare_digest (Python), o el equivalente de su lenguaje.
  • Esperar un prefijo sha256=. El valor del encabezado es solo el digest hexadecimal sin prefijo.
  • Omitir la verificación de marca de tiempo. Sin ella, una firma filtrada podría ser repetida indefinidamente.

LímiteValor
Timeout de solicitud10 segundos
Tamaño máximo de carga útil64 KB
Máximo de webhooks por usuario10

Si su endpoint no responde dentro de 10 segundos, la entrega se trata como un fallo y se reintenta.

Si su endpoint retorna un estado no-2xx o sufre timeout, LabelGrid reintenta con backoff exponencial:

IntentoEspere antes del reintento
1 → 230 segundos
2 → 31 minuto
3 → 42 minutos
4 → 54 minutos
5 → 68 minutos
6 → 716 minutos
7 → 832 minutos
8 → 964 minutos
9 → 10128 minutos

Cada intervalo incluye 0–30 segundos de jitter. Después de 10 intentos (~4.5 horas de tiempo total transcurrido), la entrega se registra como fallida permanentemente y no se vuelve a reintentar.

Si un webhook acumula 100 entregas fallidas acumuladas, se desactiva automáticamente. Reactívalo manualmente en la lista de webhooks después de arreglar su endpoint. Reactivar un webhook desactivado restablece su conteo de fallos.

  • Retorna una respuesta 2xx rápidamente (dentro de 10 segundos)
  • Procese los datos de forma asíncrona después de confirmar
  • Verifique la firma en cada solicitud (consulte Verificando Firmas de Webhook)
  • Monitoree su conteo de fallos en la lista de webhooks
  • Revise los registros de entrega cuando investigues eventos perdidos

  • Envíe mensajes a Slack cuando los lanzamientos están en vivo
  • Envíe email a su equipo cuando las entregas fallan
  • Actualice dashboards internos
  • Active campañas de marketing cuando los lanzamientos se distribuyen
  • Actualice su sitio web cuando hay nuevo contenido disponible
  • Sincronice el estado con herramientas externas de gestión de proyectos
  • Recibe alertas instantáneas por fallos de entrega
  • Rastree el progreso de distribución en tiempo real
  • Monitoree cambios en el estado de revisión

  1. Verifique el estado - ¿Está el webhook Activo?
  2. Verifique la URL - ¿Es accesible el endpoint desde internet?
  3. Verifique los eventos - ¿Están seleccionados los eventos correctos?
  4. Revise los registros - ¿Hay errores registrados?
  1. Verifique su endpoint - ¿Está retornando 200 OK?
  2. Verifique el tiempo de respuesta - ¿Está respondiendo dentro del timeout?
  3. Revise los mensajes de error - ¿Qué está fallando?
  4. Pruebe manualmente - Envíe un webhook de prueba

Si su secreto del webhook está comprometido:

  1. Haga clic en Regenerate Secret en la configuración del webhook
  2. Actualice su aplicación con el nuevo secreto
  3. El secreto antiguo deja de funcionar inmediatamente

Si tiene preguntas sobre webhooks, contacte a nuestro equipo de soporte.

¿Aún no usas LabelGrid?

Todo lo que acabas de leer está disponible en nuestra plataforma.

Descubre lo que LabelGrid puede hacer →