Webhooks
Two directions
| Direction | Auth | Pages |
|---|---|---|
| Inbound (Sumeru receives) | HMAC-verify the source's signature | Shopify, WhatsApp, Marketplaces |
| Outbound (Sumeru sends) | We sign; you verify | Outbound (Zapier) |
Inbound: how it works
Latency contract
We always ack within 2 seconds (sources retry if slower). Processing happens async; the ack means "received, will process," not "fully processed."
Idempotency
Every webhook event has an event ID. Re-delivery (Shopify
retry, replay) is detected via (source, event_id)
dedup. Same event processed once.
Failure handling
If processing fails (DB down, etc.), event lands in DLQ:
bull:event-worker:failed
Auto-retry with exponential backoff. After 5 fails, surface in admin failures dashboard.
Outbound: how it works
Subscribing
curl -X POST \
-H "Authorization: Bearer copt_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhook",
"events": ["campaign.completed", "journey.exited"],
"secret": "auto"
}' \
"https://api.sumeru.systems/api/v1/webhook-subscriptions"
secret: auto returns a generated HMAC secret in response —
you store + use it to verify signatures.
Verifying outbound webhooks
Header: X-Sumeru-Signature: sha256=<hex>
const crypto = require('crypto');
function verifySumeruWebhook(rawBody, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
try {
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
} catch {
return false;
}
}
Available events
| Event | Fires when |
|---|---|
campaign.launched | Campaign send begins |
campaign.completed | Campaign send + attribution window done |
campaign.approved / .rejected | Approval workflow decision |
journey.enrolled | Customer enters journey |
journey.exited | Customer exits journey |
customer.created / .updated | Customer 360 changes |
lifecycle.changed | Stage transition |
order.attributed | Attribution complete for an order |
anomaly.fired | Anomaly detected |
gdpr.completed | Erasure / export finished |
Full event catalog with payload schemas: see per-event page.
Retry policy
5 retries with exponential backoff: 1s, 5s, 30s, 5min, 30min.
After 5 failures, event marked failed; surface in admin webhooks dashboard. Manual replay available.
Best practices
✅ Always verify signatures. Unsigned webhooks = security hole.
✅ Make endpoints idempotent. Same event may arrive twice (retries); your code shouldn't double-process.
✅ Return 2xx fast. Process async if you need to do work; return 200 first.
✅ Set up retry alerts. Failed-webhooks dashboard shows patterns; investigate sustained failure rates.
❌ Don't reject 5xx — retry. If your service is down, return 5xx; we'll retry. Don't return 200 to "stop retries."
❌ Don't use the body in routing logic before signature verification. Body should be opaque until verified.