Webhook events
Events are the integration backbone — every state change a customer might
care about lands in /v1/events and fans out to subscribed
webhook endpoints.
Payload shape
{
"id": "evt_01HZ...",
"object": "event",
"type": "message.replied",
"created_at": "2026-04-29T10:00:00Z",
"workspace_id": "wsp_...",
"data": { /* snapshot of the resource at event time */ },
"metadata": { /* the originating send's metadata, if applicable */ },
"previous": { /* prior values for changed fields, on .updated events */ }
}
Subscribing is one POST — see the webhooks
guide for registration, signing, and retry policy.
The wildcard subscription
enabled_events: ["*"] subscribes to every event below — including
high-volume ones like message.opened,
message.clicked, and campaign.contact.advanced. We
considered carving them out of the wildcard but decided a wildcard that
silently drops events is a worse footgun than a loud wildcard. Pick events
explicitly unless you really do want all of them.
Event types
Inbox
| Type | Description |
| inbox.connected | An inbox finished OAuth or SMTP credential verification and is ready to send. |
| inbox.disconnected | An inbox was removed by the customer or revoked at the provider. |
| inbox.token_expired | OAuth refresh failed; the inbox needs reconnecting before sends resume. |
| inbox.health_changed | Health tier transitioned (good ↔ degraded ↔ paused). Fires on tier changes only, not on every score tick. |
| Type | Description |
| contact.created | A new contact was created (single, batch, or CSV import). |
| contact.updated | A contact's email, attributes, or global_status was modified. |
| contact.deleted | A contact was hard-deleted. Historical sends keep their email snapshot. |
| contact.unsubscribed | Global status flipped to `unsubscribed`. Carries the source: `campaign_id` or `manual`. |
| contact.bounced | Global status flipped to `bounced` after a hard bounce. |
| contact.lead_status_changed | A lead was classified in the unified inbox (e.g. `interested`, `meeting_booked`). Carries `lead_status` and `previous_lead_status`. |
Send
| Type | Description |
| send.scheduled | Send row written; worker job enqueued. |
| send.sending | Worker has the lock; provider call is in flight. |
| send.sent | Provider accepted the message. |
| send.failed | Provider rejected the message or the worker timed out. |
| send.canceled | Customer canceled a `scheduled` send via `POST /v1/sends/{id}/cancel`. |
Message
| Type | Description |
| message.received | Any inbound message landed in a connected inbox. |
| message.replied | An inbound message was classified as a real reply to one of our sends. |
| message.bounced | An inbound bounce report tied to one of our sends. Carries `bounce_type` (`hard`, `soft` or `unknown`), `bounce_status` (the DSN status code, e.g. `5.1.1`) and `bounce_diagnostic`. Hard bounces also suppress the address; soft bounces don't. |
| message.autoresponse | An inbound autoresponder (out-of-office, ticket-system ack). |
| message.opened
high-volume
| Tracking pixel hit. **High-volume.** Excluded from the recommended starter wildcard. |
| message.clicked
high-volume
| Tracked link clicked. Carries `url`. **High-volume.** |
Campaign
| Type | Description |
| campaign.started | Campaign transitioned `draft → running`. |
| campaign.paused | Campaign transitioned `running → paused`. |
| campaign.resumed | Campaign transitioned `paused → running`, or `completed → running` because new contacts were attached. |
| campaign.completed | No contact is still active in the sequence (all finished, replied, bounced, unsubscribed or failed); campaign is `completed`. Attaching new contacts reopens it. |
| campaign.contact.advanced
high-volume
| A contact moved to the next sequence step. **High-volume.** |
Import
| Type | Description |
| import.completed | A CSV / suppression import finished successfully. |
| import.failed | A CSV / suppression import failed with per-row errors. |
Workspace
| Type | Description |
| usage.threshold_reached | 80% or 100% of a plan-level quota (sends, contacts, webhooks). |
Webhook
| Type | Description |
| webhook.delivery.failed | An endpoint exhausted its retry schedule (the first attempt plus six retries after 1m → 5m → 30m → 2h → 8h → 24h) without ever 2xx-ing. Delivered to *other* endpoints of the same mode in the workspace — a failing endpoint can't notify itself. |