Webhooks
Los webhooks envían eventos de una tienda a una URL receptora mediante POST con JSON. Su disponibilidad y los eventos permitidos dependen del nivel de API de la tienda.
Crear y administrar un webhook
Sección titulada «Crear y administrar un webhook»Podés administrar webhooks desde /admin/configuracion/api?tab=webhooks o mediante las operaciones públicas declaradas en la referencia API. Confirmá allí los scopes y el nivel vigente de cada operación.
Si usás las operaciones públicas, autenticalas con una API key de la tienda.
{ "url": "https://integracion.example.com/events", "eventos": ["orden.creada", "stock.actualizado"]}En producción, la URL receptora debe:
- usar HTTPS y ser pública;
- no incluir usuario ni contraseña embebidos;
- no resolver a
localhost, redes privadas, direcciones link-local ni otras direcciones internas; - aceptar
POSTcon JSON; y - responder en menos de 10 segundos.
Eventos declarados
Sección titulada «Eventos declarados»Los tipos de evento y su disponibilidad se publican en los schemas del contrato OpenAPI. Consultá la referencia API para obtener el enum vigente y no mantengas una lista paralela en tu integración.
Al crear un webhook se genera un secret con prefijo whsec_. Se devuelve una sola vez y no aparece al listar webhooks, por lo que debés copiarlo inmediatamente y guardarlo en un gestor de secretos del servidor.
Los máximos por nivel se declaran en x-plan-limits. Leé esa extensión en la operación correspondiente para obtener el valor vigente.
Envelope del payload
Sección titulada «Envelope del payload»Los eventos usan este formato general:
{ "evento": "orden.creada", "tienda_id": 123, "created_at": "2026-08-15T12:00:00.000Z", "data": {}}eventoidentifica el tipo de evento.tienda_ididentifica la tienda.created_atindica cuándo ocurrió.datacontiene los datos específicos del evento.
Headers de una entrega
Sección titulada «Headers de una entrega»Los eventos procesados por la cola incluyen:
Content-Type: application/jsonUser-Agent: Wendo-Webhooks/1.0X-Wendo-Event: orden.creadaX-Wendo-Delivery-Id: 12345X-Wendo-Signature: sha256=<hex>X-Wendo-Tienda-Id: 123Los nombres de los headers HTTP son case-insensitive. El ping de prueba es una excepción respecto de X-Wendo-Delivery-Id, como se explica más adelante.
Verificar la firma
Sección titulada «Verificar la firma»X-Wendo-Signature tiene este formato:
sha256=<HMAC-SHA256 hexadecimal del body JSON exacto usando el secret>La firma cubre el body, no los headers. Para verificarla de forma segura:
- Leé primero el raw body y conservá exactamente los bytes recibidos.
- No parsees y vuelvas a serializar el JSON antes de verificar.
- Calculá el HMAC-SHA256 con el secret del webhook.
- Compará la firma en tiempo constante.
- Rechazá el request si la firma falta o no coincide.
- Verificá la firma antes de ejecutar cualquier efecto de negocio.
No registres el secret ni el body completo cuando pueda contener datos sensibles.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyWendoWebhook( rawBody: string, receivedSignature: string | null, secret: string,): boolean { if (!receivedSignature) return false;
const expectedSignature = `sha256=${createHmac('sha256', secret) .update(rawBody, 'utf8') .digest('hex')}`;
const received = Buffer.from(receivedSignature, 'utf8'); const expected = Buffer.from(expectedSignature, 'utf8');
return ( received.length === expected.length && timingSafeEqual(received, expected) );}El secret debe permanecer en una variable privada del servidor, nunca en una variable pública.
Handler genérico
Sección titulada «Handler genérico»Este ejemplo verifica la firma antes de parsear y deja explícito el punto de deduplicación. processEvent representa tu lógica de negocio; el schema concreto de event.data depende del evento y no se completa aquí.
export async function handleWebhook(request: Request): Promise<Response> { const rawBody = await request.text(); const signature = request.headers.get('x-wendo-signature');
if (!verifyWendoWebhook(rawBody, signature, process.env.WENDO_WEBHOOK_SECRET!)) { return new Response('Invalid signature', { status: 401 }); }
const event = JSON.parse(rawBody); const deliveryId = request.headers.get('x-wendo-delivery-id');
// Verificar si deliveryId ya fue procesado antes de ejecutar efectos. // Guardar el resultado dentro de la misma transacción del negocio.
await processEvent(event);
return new Response(null, { status: 204 });}Respuestas y reintentos
Sección titulada «Respuestas y reintentos»Cualquier respuesta final 2xx se considera una entrega exitosa. Los errores de red, los timeouts y las respuestas no exitosas se reintentan. Cada intento tiene un timeout de 10 segundos y una entrega realiza hasta 5 intentos totales:
| Intento | Momento normal |
|---|---|
| 1 | Envío inicial. |
| 2 | 1 minuto después. |
| 3 | 5 minutos después. |
| 4 | 30 minutos después. |
| 5 | 2 horas después. |
Después del quinto fallo, la entrega queda agotada. Guardá el evento de forma durable, respondé 2xx y procesá las tareas pesadas de manera asíncrona para evitar timeouts. Al diseñar respuestas de error, consultá también la guía de errores.
Idempotencia y duplicados
Sección titulada «Idempotencia y duplicados»Los webhooks pueden repetirse debido a los reintentos. X-Wendo-Delivery-Id permanece asociado a la misma entrega y debe usarse como clave de idempotencia:
- Antes de ejecutar efectos, verificá si el delivery ID ya fue procesado.
- Guardá el ID procesado dentro de la misma transacción que la operación de negocio.
- Si el ID ya existe, no repitas el efecto y respondé
2xx.
X-Wendo-Delivery-Id no es una garantía criptográfica anti-replay. Actualmente no se envía un timestamp firmado y no debe asumirse una ventana anti-replay basada en tiempo. Esta deduplicación es independiente de la cabecera Idempotency-Key usada por algunas operaciones de la API; consultá idempotencia para distinguir ambos mecanismos.
Ping de prueba
Sección titulada «Ping de prueba»Desde el administrador podés enviar un ping para comprobar la recepción y la firma. El ping:
- usa el evento
ping; - se firma con el mismo HMAC-SHA256;
- incluye
X-Wendo-Event,X-Wendo-SignatureyX-Wendo-Tienda-Id; - es un intento directo y no usa la cola normal de reintentos; y
- puede no incluir
X-Wendo-Delivery-Id.
El receptor no debe exigir un delivery ID para aceptar un evento ping. Sí debe verificar su firma antes de procesarlo.
