API de Webhooks
Los webhooks permiten que tu aplicación reciba notificaciones HTTP en tiempo real cuando ocurren eventos en tu tienda Mercozy. En lugar de consultar la API, registra una URL y Mercozy enviará automáticamente los datos del evento.
Inicio rápido
Comienza en 3 pasos:
Paso 1: Registra tu endpoint
Obtén tu clave API desde Configuración > Integración > Claves API. Se requiere permiso de administrador. El secreto de firma del webhook (whsec_xxx) se genera automáticamente al crear un endpoint y se muestra solo una vez.
curl -X POST "https://api.mercozy.com/api/v1/external/webhooks" \
-H "X-API-Key: mk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "My ERP Integration",
"url": "https://your-app.com/webhooks/mercozy",
"events": ["order.created", "order.cancelled", "payment.received"]
}'Paso 2: Procesa los eventos entrantes
app.post('/webhooks/mercozy', (req, res) => {
// 1. Verify signature (see below)
const signature = req.headers['x-webhook-signature'];
if (!verifySignature(req.body, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// 2. Process the event
const { type, data } = req.body;
switch (type) {
case 'order.created':
syncOrderToERP(data);
break;
case 'payment.received':
updateAccountingSystem(data);
break;
}
// 3. Respond quickly with 200
res.status(200).json({ received: true });
});Paso 3: Prueba tu endpoint
curl -X POST "https://api.mercozy.com/api/v1/external/webhooks/{id}/test" \
-H "X-API-Key: mk_live_your_key_here"Endpoints
/webhooks
Lista todos los endpoints de webhook registrados.
/webhooks
Registra un nuevo endpoint de webhook para recibir notificaciones de eventos.
/webhooks/:id
Actualiza un endpoint de webhook existente (URL, eventos o estado activo).
/webhooks/:id
Elimina un endpoint de webhook. Los eventos ya no se entregarán.
Referencia de Campos
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
id | string | No | Identificador único del webhook (solo lectura) |
name | string | Sí | Un nombre descriptivo para este endpoint |
url | string | Sí | URL del endpoint HTTPS para recibir eventos de webhook |
events | string[] | Sí | Array de tipos de evento a suscribir |
secret | string | No | Secreto de firma para verificación de payload (solo lectura, generado al crear) |
isActive | boolean | No | Si el webhook está activo (predeterminado: true) |
createdAt | datetime | No | Marca de tiempo ISO 8601 (solo lectura) |
Tipos de Evento Disponibles
Mercozy admite 18 tipos de eventos de webhook en 6 categorías. Suscríbete solo a los eventos que necesites.
Pedidos
- order.created
Se activa cuando se realiza un nuevo pedido
- order.updated
Se activa cuando se modifican los detalles del pedido
- order.cancelled
Se activa cuando se cancela un pedido
- order.completed
Se activa cuando un pedido se marca como completado
- order.status_changed
Se activa en cualquier cambio de estado del pedido
Pagos
- payment.received
Se activa cuando se captura el pago o se cobra contra reembolso
- payment.refunded
Se activa cuando se reembolsa o cancela un pago
Productos
- product.created
Se activa cuando se añade un nuevo producto
- product.updated
Se activa cuando cambian los detalles del producto
- product.deleted
Se activa cuando se elimina un producto
Inventario
- stock.low
Se activa cuando el stock baja del umbral
- stock.updated
Se activa cuando cambia la cantidad en stock
Clientes
- customer.created
Se activa cuando se registra un nuevo cliente
- customer.updated
Se activa cuando se actualiza la información del cliente
Entregas
- delivery.batch_started
Se activa cuando se despacha un lote de entregas
- delivery.batch_completed
Se activa cuando se completan todas las paradas del lote
- delivery.completed
Se activa cuando se completa una entrega individual
- delivery.failed
Se activa cuando falla un intento de entrega
Payload del Webhook
Cada entrega de webhook incluye el tipo de evento, marca de tiempo y los datos del recurso relevante.
Datos del evento de pedido
{
"id": "evt_clx1abc2def3",
"type": "order.created",
"timestamp": "2026-03-19T10:30:00Z",
"data": {
"id": "clx9order123",
"orderNumber": "ORD-1042",
"status": "PENDING",
"total": 4999,
"currency": "USD"
}
}Datos del evento de pago
{
"id": "evt_clx2pay4ghi5",
"type": "payment.received",
"timestamp": "2026-03-19T10:35:00Z",
"data": {
"orderId": "clx9order123",
"orderNumber": "ORD-1042",
"amount": 4999,
"currency": "USD",
"paymentMethod": "CARD",
"status": "CAPTURED"
}
}Datos del evento de producto
{
"id": "evt_clx3prod6jkl",
"type": "product.created",
"timestamp": "2026-03-19T11:00:00Z",
"data": {
"id": "clx9prod789",
"name": "Organic Coffee Beans",
"sku": "COF-001",
"price": 1299,
"status": "ACTIVE"
}
}Datos del evento de entrega
{
"id": "evt_clx4del7mno",
"type": "delivery.completed",
"timestamp": "2026-03-19T14:22:00Z",
"data": {
"orderId": "clx9order123",
"orderNumber": "ORD-1042"
}
}Encabezados HTTP
Cada solicitud de webhook incluye los siguientes encabezados:
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
Content-Type | string | Sí | application/json |
X-Webhook-Signature | string | Sí | Firma HMAC-SHA256: sha256={resumen_hexadecimal} |
X-Webhook-Timestamp | string | Sí | Marca de tiempo Unix en milisegundos |
X-Webhook-ID | string | Sí | ID de evento único para deduplicación |
User-Agent | string | Sí | Mercozy-Webhook/1.0 (+https://www.mercozy.com/docs/webhooks) |
Verificación de Firma
Cada webhook incluye un encabezado X-Webhook-Signature que contiene una firma HMAC-SHA256. Verifica siempre esta firma antes de procesar el evento para asegurar que la solicitud proviene de Mercozy.
Node.js
const crypto = require('crypto');
function verifyWebhook(rawBody, signature, timestamp, secret) {
// Signature is computed over "timestamp.body"
const message = timestamp + '.' + rawBody;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express middleware
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-webhook-signature'];
const ts = req.headers['x-webhook-timestamp'];
if (!verifyWebhook(req.body, sig, ts, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Process event...
res.status(200).json({ received: true });
});Python
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
# Signature is computed over "timestamp.body"
message = (timestamp + '.').encode() + payload
expected = 'sha256=' + hmac.new(
secret.encode(),
message,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask example
@app.route('/webhooks', methods=['POST'])
def handle_webhook():
sig = request.headers.get('X-Webhook-Signature')
ts = request.headers.get('X-Webhook-Timestamp')
if not verify_webhook(request.data, sig, ts, WEBHOOK_SECRET):
return 'Invalid signature', 401
event = request.get_json()
# Process event...
return {'received': True}, 200PHP
<?php
function verifyWebhook(string $payload, string $signature, string $timestamp, string $secret): bool {
// Signature is computed over "timestamp.body"
$message = $timestamp . '.' . $payload;
$expected = 'sha256=' . hash_hmac('sha256', $message, $secret);
return hash_equals($expected, $signature);
}
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
if (!verifyWebhook($payload, $signature, $timestamp, $webhookSecret)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($payload, true);
// Process event...
http_response_code(200);
echo json_encode(['received' => true]);Política de reintentos
Si tu endpoint devuelve un código de estado no-2xx o se agota el tiempo (10 segundos), Mercozy reintentará la entrega con retroceso exponencial:
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
Intento 1 | No | Inmediato | |
Intento 2 | No | Después de ~1s | |
Intento 3 | No | Después de ~4s |
Tras 3 intentos fallidos, la entrega se marca como fallida. Puedes reintentar manualmente desde el panel o la API.
Mejores prácticas
Responde en menos de 5 segundos — Devuelve un estado 200 inmediatamente y procesa el evento de forma asíncrona. Mercozy tiene un tiempo de espera de 10 segundos.
Verifica siempre las firmas — Comprueba el encabezado X-Webhook-Signature antes de procesar cualquier evento para evitar solicitudes falsificadas.
Maneja duplicados (idempotencia) — Usa el encabezado X-Webhook-ID para deduplicar eventos. Almacena los IDs procesados y omite los que ya hayas manejado.
Usa endpoints HTTPS — Mercozy requiere URLs HTTPS en producción para proteger los datos del webhook en tránsito.
Registra y monitorea — Registra todos los webhooks entrantes y monitorea los fallos. Usa la API de registros de entrega para verificar el estado.