Webhooks
Push Bitelio events to your own server as they happen
A webhook is an HTTP POST Bitelio sends to a URL you control whenever something happens in your project — an email delivered, a contact subscribing, a Shopify order coming in. It's the alternative to polling the API: instead of asking "did anything happen?" on a timer, your server gets told the moment it does.
Not the same as a workflow Webhook step
Bitelio has a second, older way to push an HTTP request out: a Webhook step inside a workflow, configured per-workflow with its own URL, headers, and body. It's unsigned, has no delivery history, replay, or circuit breaker, and its queue-level retries only cover the step a retried job re-enters at — not the rest of the chain. This section documents the endpoint subscriptions registered under Settings → Webhooks: every request is signed, retried, and inspectable, with automatic degradation for an endpoint that keeps failing. Use whichever fits — or both.
Registering an endpoint
Go to Settings → Webhooks and add an endpoint:
- An HTTPS URL — plain HTTP is rejected. A webhook payload can carry contact data, so the connection has to be encrypted.
- The events you want to receive, picked from the catalogue shown in the form (e.g.
email.delivery,contact.subscribed,shopify.order.paid). - An optional description, to tell endpoints apart later.
Saving returns a signing secret (whsec_...), shown once in plaintext and never again — copy it straight into wherever your receiver reads its configuration from. You'll only see it again if you rotate it, which issues a new one while keeping the old one valid for 24 hours, so you can roll your receiver over to the new secret without dropping events in between.
Once the endpoint is saved, use Send test event to confirm your receiver responds correctly before relying on it. A test event travels the exact same signing, delivery, and retry path as a real one — it just carries a fixed event type, bitelio.webhook.test, instead of a real one.
What you receive
Every event arrives as a POST with a JSON body shaped like this:
{
"id": "b0f6b8b0-6e0a-4f0a-9c0a-4e2b9b7b8a10",
"type": "email.delivery",
"occurredAt": "2026-07-30T12:00:00.000Z",
"contactId": "1f9d1a3c-9b0a-4b8a-9c1a-0a1b2c3d4e5f",
"data": {}
}data carries whatever detail is specific to that event type — its shape isn't fixed across events, since it mirrors what Bitelio tracks internally for that event.
Alongside the body come five custom headers — Bitelio-Signature, Bitelio-Timestamp, Bitelio-Idempotency-Id, Bitelio-Event-Type, and Bitelio-Schema-Version — covered on the next page.
In this section
| Page | Covers |
|---|---|
| Verifying signatures | Confirming a request genuinely came from Bitelio, with a runnable Node example |
| Delivery guarantees | At-least-once, unordered delivery: the retry schedule, degradation, retention, and replay |