API de Webhooks para vendedores

Documentación para integrar los eventos de tu tienda con tu ERP o sistemas externos.

Catálogo de eventos

EventoCuándo se dispara
suborder.paidCuando la suborden de tu tienda pasa a En preparación tras el pago del pedido.
suborder.shippedCuando el envío del paquete es confirmado por el transportista (Sendcloud).
suborder.deliveredCuando el transportista confirma la entrega al cliente.
suborder.cancelledCuando la suborden se cancela o se reembolsa (con el motivo y el importe devuelto).
product.stock_lowCuando el stock de un producto cae a la altura o por debajo del umbral configurado (máx. 1 aviso por día).

Ejemplo de payload

suborder.paid

{
  "event": "suborder.paid",
  "occurredAt": "2026-09-03T10:15:00.000Z",
  "storeId": "c6f4af6f-1e2d-4c3b-9a5e-2f7d8e9a1b2c",
  "subOrderId": "3a1b7c9d-5e6f-4a8b-8c2d-1e3f4a5b6c7d",
  "orderId": "9f8e7d6c-5b4a-49c8-b2d1-0e9f8a7b6c5d",
  "status": "PROCESSING",
  "amounts": {
    "subtotalCents": 11000,
    "commissionCents": 1000,
    "commissionIvaCents": 210,
    "payoutCents": 9790
  },
  "extra": {
    "items": [
      {
        "productId": "1d2c3b4a-5e6f-47a8-9b0c-1d2e3f4a5b6c",
        "quantity": 1
      }
    ]
  }
}

suborder.shipped

{
  "event": "suborder.shipped",
  "occurredAt": "2026-09-03T10:15:00.000Z",
  "storeId": "c6f4af6f-1e2d-4c3b-9a5e-2f7d8e9a1b2c",
  "subOrderId": "3a1b7c9d-5e6f-4a8b-8c2d-1e3f4a5b6c7d",
  "orderId": "9f8e7d6c-5b4a-49c8-b2d1-0e9f8a7b6c5d",
  "status": "SHIPPED",
  "amounts": {
    "subtotalCents": 11000,
    "commissionCents": 1000,
    "commissionIvaCents": 210,
    "payoutCents": 9790
  },
  "extra": {
    "trackingUrl": "https://tracking.sendcloud.sc/parcel/abc123"
  }
}

suborder.delivered

{
  "event": "suborder.delivered",
  "occurredAt": "2026-09-03T10:15:00.000Z",
  "storeId": "c6f4af6f-1e2d-4c3b-9a5e-2f7d8e9a1b2c",
  "subOrderId": "3a1b7c9d-5e6f-4a8b-8c2d-1e3f4a5b6c7d",
  "orderId": "9f8e7d6c-5b4a-49c8-b2d1-0e9f8a7b6c5d",
  "status": "DELIVERED",
  "amounts": {
    "subtotalCents": 11000,
    "commissionCents": 1000,
    "commissionIvaCents": 210,
    "payoutCents": 9790
  },
  "extra": {
    "deliveredAt": "2026-09-04T09:30:00.000Z"
  }
}

suborder.cancelled

{
  "event": "suborder.cancelled",
  "occurredAt": "2026-09-03T10:15:00.000Z",
  "storeId": "c6f4af6f-1e2d-4c3b-9a5e-2f7d8e9a1b2c",
  "subOrderId": "3a1b7c9d-5e6f-4a8b-8c2d-1e3f4a5b6c7d",
  "orderId": "9f8e7d6c-5b4a-49c8-b2d1-0e9f8a7b6c5d",
  "status": "REFUNDED",
  "amounts": {
    "subtotalCents": 11000,
    "commissionCents": 1000,
    "commissionIvaCents": 210,
    "payoutCents": 9790
  },
  "extra": {
    "kind": "refunded",
    "refundAmountCents": 11000
  }
}

product.stock_low

{
  "event": "product.stock_low",
  "occurredAt": "2026-09-03T02:00:00.000Z",
  "storeId": "c6f4af6f-1e2d-4c3b-9a5e-2f7d8e9a1b2c",
  "productId": "1d2c3b4a-5e6f-47a8-9b0c-1d2e3f4a5b6c",
  "productName": "Sobrasada de Mallorca",
  "stock": 2,
  "threshold": 3,
  "dedupKey": "stock-low:1d2c3b4a-5e6f-47a8-9b0c-1d2e3f4a5b6c:2026-09-03"
}

Verificación de la firma (HMAC-SHA256)

Cada entrega incluye la cabecera X-Webhook-Signature: el HMAC-SHA256 (hex) del cuerpo crudo de la petición, calculado con tu clave secreta de endpoint. Verifica siempre la firma antes de procesar el payload.

Node.js

import { createHmac, timingSafeEqual } from 'node:crypto';

// rawBody = the RAW request body string (before JSON.parse)
const expected = createHmac('sha256', WEBHOOK_SECRET)
  .update(rawBody)
  .digest('hex');

const received = req.headers['x-webhook-signature'] ?? '';
const valid =
  received.length === expected.length &&
  timingSafeEqual(Buffer.from(expected), Buffer.from(received));

if (!valid) return res.status(400).end();

PHP

<?php
// $rawBody = file_get_contents('php://input') (raw, not decoded)
$expected = hash_hmac('sha256', $rawBody, $secret);
$received = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';

if (!hash_equals($expected, $received)) {
    http_response_code(400);
    exit;
}

Reintentos

Si tu endpoint no responde con un 2xx, reintentamos hasta 5 intentos por entrega con el siguiente esquema:

IntentoEspera antes del reintento
1Inmediato
2300s (0 h)
31800s (1 h)
47200s (2 h)
521600s (6 h)

Tras el 5.º intento fallido el evento queda marcado como fallido y recibirás un aviso por email. Reparado el endpoint, los eventos nuevos vuelven a entregarse con normalidad.