Skip to content
API · Webhooks

Webhooks firmados con HMAC

Reciba eventos de la tienda en su sistema externo en tiempo real.

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

CampoTipoDescripción
urlstringURL HTTPS de su endpoint. Obligatorio.
descriptionstringEtiqueta interna (máx 200 caracteres). Opcional.
eventsarrayLista 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

EventoCuándo se dispara
order.createdPedido creado en estado PENDING o PROCESSING.
order.completedPedido cerrado con todos los items DELIVERED.
order.items_fulfilledBackfill de items PENDING tras carga de stock.
recharge.approvedRecarga al monedero acreditada.
recharge.rejectedRecarga rechazada por el OWNER o el sistema.
customer.registeredCliente nuevo creado en la tienda.
stock.lowUna variación cruza el umbral de stock bajo.
stock.availableUna 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 2xx en 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 SUSPENDED automáticamente y deja de recibir eventos. Lo puede reactivar borrándolo y re-creándolo.
  • El primer envío 2xx resetea 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