> ## Documentation Index
> Fetch the complete documentation index at: https://docs.12m.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks overview

> Configure one URL per org, signed with HMAC-SHA256. Retries, pause, rotation.

We POST to your URL on every customer-observable event in your org. One
endpoint per org; the identity is identified inside the payload (`data.identity`)
since one receiver typically handles all your identities.

The data is identical to the [events stream](/api-reference/events).
Each delivery carries the same `seq` and `id` as the corresponding
events-stream entry, so you can mix push and pull without duplicating
work.

## Configure

From the dashboard at [inboxbase.ai/app/developers](https://inboxbase.ai/app/developers):

1. **Endpoint URL** — where we POST. HTTPS only.
2. **Signing secret** — `whsec_...`, shown once at creation. Paste it
   into your verifier (see [Verify](/api-reference/webhooks-verify)).
3. **Status** — `active` or `paused`. Returning `410 Gone` from your
   endpoint pauses delivery automatically; you re-set from the dashboard.
4. **Rotate secret** — invalidates the old secret. Copy the new one
   before saving. There is no overlap window — flip your verifier the
   instant you save.

## Retries

Three attempts at `0s / 5s / 30s` after the trigger. Per-attempt
timeout: 10 seconds. We treat any 2xx as success.

| Response                  | What we do                              |
| ------------------------- | --------------------------------------- |
| `2xx`                     | Stop retrying.                          |
| `410 Gone`                | Pause the endpoint. No further retries. |
| Anything else (incl. 5xx) | Retry per the schedule above.           |
| Network error / timeout   | Retry per the schedule above.           |

After three failed attempts we give up on that event. Your recovery
path is to walk the [events stream](/api-reference/events) from the
last `seq` you persisted — it carries every event we tried to push.

## Idempotency

Webhooks are at-least-once. If your handler crashes after doing work
but before responding 2xx, you'll see the same event again on retry.
Dedup by `id` (or `seq`):

```js theme={null}
if (await db.events.exists(ev.id)) return res.sendStatus(200);
await handleEvent(ev);
await db.events.insert({ id: ev.id, seq: ev.seq, handledAt: Date.now() });
res.sendStatus(200);
```

The trade-off vs. doing the dedup outside the handler: the inside
version costs you one round-trip to the DB on every webhook. For
high-volume orgs you'll want to keep `seq` per identity in memory and
fall back to the DB on cold starts.

## Headers we send

```http theme={null}
Content-Type:        application/json
X-Inboxbase-Event-Type:    email.replied
X-Inboxbase-Event-Id:      evt_01HF6YJZ4N0K3W2X9CPM7B8DG2
X-Inboxbase-Signature:     t=1777278929,v1=<hex(hmac_sha256(secret, "<t>.<body>"))>
```

Always read the **raw body** for signature verification. Frameworks
that auto-parse JSON before your handler sees the bytes will break the
verifier; use `express.raw({type: 'application/json'})` or your stack's
equivalent.
