POST firmada al endpoint de webhook que configuraste, con el cuerpo del evento.
Configuración del endpoint
Tu endpoint debe ser accesible públicamente por HTTPS y responder con cualquier código de estado 2xx. Configura su URL con tu contacto de Meru o desde el dashboard. Al crearlo recibes un secreto de firma (con prefijowhsec_) — guárdalo de forma segura; lo necesitas para verificar las firmas.
Estructura del evento
Todos los cuerpos de entrega tienen la misma forma de nivel superior:El tipo de evento (ver los eventos más abajo).
Marca de tiempo ISO 8601 de cuándo se emitió el evento.
El cuerpo del evento. Su forma depende de
type.Eventos
| Evento | Descripción |
|---|---|
payout.update | Un payout cambió de estado |
balance.updated | Un depósito (fiat o cripto) cambió de estado |
customer.status.updated | Cambió el KYC/KYB/estado del customer |
customer.product.request.updated | Avanzó el onboarding/aprovisionamiento de un producto |
card.transaction.* | Se creó/actualizó una transacción de tarjeta |
Headers de entrega
Cada solicitud incluye estos headers:| Header | Descripción |
|---|---|
Content-Type | application/json |
webhook-id | ID único del mensaje/entrega. Úsalo para idempotencia. |
webhook-timestamp | Marca de tiempo Unix (segundos) de la entrega, usada para verificar la firma. |
webhook-signature | Lista separada por espacios de firmas v1,<base64> (más de una durante la rotación del secreto). |
Verificación de firmas
Cada solicitud se firma con HMAC-SHA256 para que puedas confirmar que viene de nosotros. El contenido firmado es el string{webhook-id}.{webhook-timestamp}.{cuerpoCrudo}, con clave en tu secreto de firma (decodificado de base64 tras quitar el prefijo whsec_), y el resultado se codifica en base64 dentro del header webhook-signature.
Para verificar: recalcula la firma sobre el cuerpo crudo de la solicitud (antes de cualquier parseo de JSON) y compárala contra el header en tiempo constante. Rechaza las entregas cuyo webhook-timestamp tenga más de 5 minutos.
Idempotencia
El mismo evento puede entregarse más de una vez. Usa el headerwebhook-id como clave de idempotencia: guarda los IDs procesados y descarta los duplicados.
Reintentos
Si tu endpoint no devuelve un estado2xx (o agota el tiempo de espera), reintentamos la entrega automáticamente con backoff exponencial a lo largo de varias horas, respetando Retry-After en respuestas de error. Confirma rápido (en pocos segundos) y procesa el trabajo pesado de forma asíncrona. Los endpoints que fallan de forma persistente pueden quedar deshabilitados.
Buenas prácticas
- Verifica siempre la firma contra el cuerpo crudo antes de procesar.
- Responde rápido con un
2xxy procesa de forma asíncrona. - Sé idempotente usando
webhook-id. - No asumas orden — apóyate en
state/previousStateyupdatedAt, no en el orden de entrega. - Registra el
webhook-id, eltypey eldatade cada entrega.
Ejemplo de handler
Pruebas
Expón tu servidor local con un túnel (por ejemplo, ngrok) y registra la URL pública como tu endpoint de webhook:Solución de problemas
- Firma inválida: verifica contra el cuerpo crudo (no un JSON re-serializado), usa el secreto
whsec_correcto y lee los headerswebhook-id/webhook-timestamp/webhook-signaturetal cual. - Eventos duplicados: deduplica usando
webhook-id. - Eventos perdidos: asegúrate de que tu endpoint devuelva
2xxrápido; las respuestasnon-2xxy los timeouts se reintentan, pero las fallas persistentes pueden deshabilitar la entrega. - Errores de SSL: tu endpoint necesita un certificado TLS válido.
webhook-id.