Webhooks Guide
Build a signed webhook receiver, verify signatures, and handle retries.
This guide walks through building a production-grade webhook receiver for Cresora events. For the full event catalog, see Event types.
1. Register your endpoint
In the Partner Portal → Developers → Webhooks, add an endpoint. Enter your public HTTPS URL and copy the signing secret — it is displayed once.
2. Implement the receiver
Your endpoint must:
- Accept
POSTrequests withContent-Type: application/json - Return
2xxwithin 10 seconds - Verify the HMAC-SHA256 signature before processing
See Signature verification for the complete verification code.
3. Handle events idempotently
Cresora retries most non-2xx responses, so your handler must be idempotent — processing the same event twice must produce the same outcome. Note that 400, 401, 404, 405, 410 and 422 are treated as permanent rejections and are never retried; see Retry handling.
// Use the event ID as an idempotency key
async function handleWebhook(event) {
const alreadyProcessed = await db.webhookEvents.findOne({ id: event.id });
if (alreadyProcessed) return; // deduplicate
await db.webhookEvents.insert({ id: event.id, processedAt: new Date() });
await processEvent(event);
}4. Respond quickly, process async
Return 200 OK as fast as possible, then process the event in a background job:
app.post("/webhooks/cresora", (req, res) => {
verifySignature(req); // throws on invalid signature
res.sendStatus(200); // respond immediately
queue.enqueue(req.body); // process asynchronously
});Retry schedule
| Attempt | Delay after failure |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| Total | 4 attempts over roughly 35 minutes |
After the 3rd retry the delivery is recorded EXHAUSTED and Cresora stops — there is no notification. See Retry handling and Failed deliveries.
Testing locally
Use ngrok or any tunnel service to expose localhost, then register the tunnel URL in the Portal. See Testing webhooks →.