Qué son
Un webhook es un endpoint HTTPS suyo al que la API le hace POST cuando un evento ocurre en su tienda (un pedido se completó, una recarga fue aprobada, un cliente se registró). Evita que su bot tenga que pollear la API.
Registrar un endpoint
POST /api/v1/external/webhooks · scope webhooks:manage
Body
| Campo | Tipo | Descripción |
|---|---|---|
url | string | URL HTTPS de su endpoint. Obligatorio. |
description | string | Etiqueta interna (máx 200 caracteres). Opcional. |
events | array | Lista de eventos a suscribir. Al menos uno. |
Request
curl -X POST https://api.nuvlyx.com/api/v1/external/webhooks \
-H "Authorization: Bearer nvl_live_TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.mi-erp.com/hooks/nuvlyx",
"description": "ERP de produccion",
"events": ["order.completed", "recharge.approved"]
}' Response 201
{
"id": "wep_…",
"url": "https://api.mi-erp.com/hooks/nuvlyx",
"events": ["order.completed", "recharge.approved"],
"status": "ACTIVE",
"secret": "a8f3c8e1b7d4...64-hex-chars...",
"createdAt": "2026-05-18T..."
} El secret se devuelve UNA SOLA VEZ. Guárdelo
en un lugar seguro — sirve para verificar la firma de cada evento que
reciba.
Catálogo de eventos
| Evento | Cuándo se dispara |
|---|---|
order.created | Pedido creado en estado PENDING o PROCESSING. |
order.completed | Pedido cerrado con todos los items DELIVERED. |
order.items_fulfilled | Backfill de items PENDING tras carga de stock. |
recharge.approved | Recarga al monedero acreditada. |
recharge.rejected | Recarga rechazada por el OWNER o el sistema. |
customer.registered | Cliente nuevo creado en la tienda. |
stock.low | Una variación cruza el umbral de stock bajo. |
stock.available | Una variación que estaba en cero recibió stock. |
Forma del payload
Cada evento llega como POST con cuerpo JSON. Estructura común:
{
"id": "evt_a4f2c8e1b3d7...",
"type": "order.completed",
"created_at": "2026-05-18T15:42:00Z",
"livemode": true,
"data": {
// payload especifico del evento
}
} Verificar la firma
Cada POST incluye un header X-Nuvlyx-Signature al estilo Stripe:
X-Nuvlyx-Signature: t=1747754520,v1=<hmac_sha256_hex>
Para validar: concatene {t}.{rawBody}, calcule HMAC-SHA256
con su secret y compárelo con v1 usando
comparación constante (resistente a timing attacks).
Node.js
import crypto from 'node:crypto';
export function verifyNuvlyxSignature(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(parts.v1)
);
} Python
import hmac, hashlib
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split('=') for p in header.split(','))
expected = hmac.new(
secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, parts['v1']) Reintentos y suspensión
- Su endpoint debe responder
2xxen menos de 8 segundos. - En timeout, red, o respuesta
5xx: hasta 3 reintentos automáticos (con backoff via RabbitMQ). - Después de 5 fallos consecutivos, el endpoint se marca
SUSPENDEDautomáticamente y deja de recibir eventos. Lo puede reactivar borrándolo y re-creándolo. - El primer envío
2xxresetea el contador de fallos a cero.
Deduplicación
El header X-Nuvlyx-Event-Id contiene el mismo id del
payload. Su receptor debe persistirlo y rechazar duplicados — los eventos
pueden llegar más de una vez ante reintentos.
Auditar entregas
GET /api/v1/external/webhooks/:id/deliveries devuelve los últimos 50 intentos con status, latencia y mensaje de error.
Eliminar un endpoint
DELETE /api/v1/external/webhooks/:id