Cómo se entregan
Hacemos unPOST con un cuerpo JSON a la URL de webhook de tu app.
- La URL tiene que ser
httpsy pública. No funciona conlocalhost: para desarrollar usá un túnel (ngrok, cloudflared). - Tenemos 5 segundos para recibir tu respuesta y no seguimos redirects.
- Cualquier respuesta
2xxcuenta como recibido; cualquier otra cosa (o un timeout) se reintenta. - El cuerpo pesa como máximo 256 KB. De tu respuesta leemos como mucho 1 KB, sólo para mostrártela en el log del portal.
- Respondé rápido y procesá después: guardá el evento y devolvé
200antes de hacer trabajo pesado.
Headers
Cuerpo del evento
Qué trae meta en cada evento
Ejemplos:
product.updated mandamos un único products.bulk_updated. Es una señal para que vuelvas a sincronizar con GET /products?updated_since=.... Lo reciben las apps suscriptas a product.updated.
Eventos disponibles
Elegís a qué eventos suscribirte en el portal. Para recibir un evento, tu instalación necesita el scoperead_ del recurso (ver Scopes y permisos).
Los 4 webhooks obligatorios
app.uninstalled, store.redact, customers.redact y customers.data_request se entregan siempre, sin que te suscribas y aunque tu instalación no tenga scopes de clientes. Una app sin URL de webhook no se puede publicar.
email_hash es el SHA-256 en hexadecimal del email en minúsculas (no mandamos el email en claro, porque estos eventos llegan a todas las apps). Es null si el email ya fue anonimizado. Si el cliente ya no existe, data es sólo { "id": "<id del cliente>" }.
Garantías
- Al menos una vez. Puede llegarte el mismo evento más de una vez. Deduplicá por
X-Commercy-Event-Id. - Sin orden garantizado. Un
order.updatedpuede llegar antes que elorder.createdo después. Guiate por el estado que traedata, no por el orden de llegada. - Sin eco. Los cambios que hace tu propia app por la API no te vuelven como webhook.
Reintentos y baja automática
Si tu endpoint no responde2xx, reintentamos hasta 12 veces más (13 intentos en total, durante unas 44 horas). Los tiempos de espera, con una variación aleatoria de ±10 %, son:
30 s, 1 min, 2 min, 5 min, 15 min, 30 min, 1 h, 2 h, 4 h, 8 h, 12 h, 16 h
Después del último intento la entrega queda como dead. Si todas las entregas de tu app fallan durante 48 horas seguidas, damos de baja el webhook y te avisamos por email. Mientras está de baja no se envían eventos. Lo reactivás desde el portal, una vez que tu endpoint esté sano.
Replay y log
En el portal ves el log de entregas de los últimos 30 días, con el código HTTP, el tiempo de respuesta y el inicio de tu respuesta. Desde ahí podés reenviar una entrega puntual o todas las fallidas de un rango de hasta 7 días. El replay conserva el mismoX-Commercy-Event-Id y genera un X-Commercy-Delivery nuevo.
Ping de prueba
Desde el portal podés enviar un eventoping a tu URL. Viene firmado igual que los demás, con store_id: null, data: { "app_id": "..." } y sin el header X-Commercy-Store.
Verificar la firma
Firmamos cada entrega para que puedas comprobar que viene de Commercy y que nadie la alteró. La firma es:v1=. Para verificarla:
- Leé el cuerpo crudo (los bytes tal como llegaron, antes de parsear el JSON). Si lo parseás y lo volvés a serializar, la firma no coincide.
- Calculá la firma con tu secreto de webhook completo, incluido el prefijo
whsec_. - Compará con
X-Commercy-Signaturecon una comparación de tiempo constante. - Rechazá las entregas con un timestamp que difiera más de 5 minutos de tu reloj (protección contra replay).
raw en esa ruta para conservar el cuerpo crudo:
Vector de prueba
Usalo para comprobar tu implementación sin esperar un webhook real. Con este secreto, timestamp y cuerpo, tu código tiene que generar exactamente la firma indicada.
Cuerpo (una sola línea, sin saltos ni espacios extra):