Webhooks Overview
Get notified on your own server when things happen in a workspace.
Webhooks let Audienceful notify your server when things happen in a workspace. Each webhook endpoint is an HTTPS URL subscribed to one or more events. Deliveries are HMAC-signed and retried with backoff.
Managing endpoints requires the webhooks:read scope (to read) and webhooks:write scope (to create, update, or delete).
Create an endpoint
Create a webhook endpoint with your HTTPS URL and the events you care about. The response includes the signing secret — this is the only time it's returned, so store it.
Verify the signature
On each incoming request, verify the X-Audienceful-Signature header against the raw request body using your secret. See Verifying signatures for the scheme and code examples.
Respond 2xx
Return a 2xx status to acknowledge the delivery. Non-2xx responses are retried.
Available events
Fetch the current event vocabulary from GET /webhook-events. Events are grouped by area:
Contacts
| Event | Fires when |
|---|---|
person.created |
A new contact is added to your workspace. |
person.updated |
A contact's details change — email, name, custom fields, subscription status, etc. Automatic engagement counters (open/click counts, last activity) are excluded, so ingestion never spams you. |
person.deleted |
A contact is deleted from your workspace. |
person.unsubscribed |
A contact unsubscribes or is marked unsubscribed (the unsubscribed state goes from unset to set). A single unsubscribe also emits person.updated — subscribe to whichever you want. |
| Event | Fires when |
|---|---|
bulk_email.sent |
A bulk email finishes sending. Exactly one summary event per send, with the subject and a summary of the recipients. |
Audiences
| Event | Fires when |
|---|---|
audience.member_added |
A contact enters an audience. |
audience.member_removed |
A contact leaves an audience. |
Real-time changes only. Events fire for real-time, per-contact activity — a form signup, an API call, a manual edit, a single delete. High-volume bulk operations are intentionally webhook-silent so a large job can't emit tens of thousands of deliveries at once:
- CSV imports and bulk upserts don't fire
person.created,person.updated, oraudience.member_*. - Mass deletes don't fire
person.deleted. - A full-audience resync (e.g. after an import) doesn't fire
audience.member_added/audience.member_removed.
person.updated replaces the former person.changed event, and now fires on any meaningful change to a contact (not just audience-relevant fields). Existing endpoints subscribed to person.changed were migrated automatically.
Payload format
Every delivery is JSON with an event name and a data object:
{
"event": "person.created",
"data": { "...": "..." }
}
The shape of data depends on the event.
Delivery headers
Every delivery carries these headers:
Content-Type: application/json
X-Audienceful-Event: person.created
X-Audienceful-Delivery-Id: wYt3nKq8Zs4pLm9vRb2xJc
X-Audienceful-Signature: t=1751630400,v1=<hex hmac-sha256>
Always verify the signature before trusting a delivery's contents — see that page for the scheme and code examples in Python, TypeScript, and PHP.
Delivery, retries & auto-disable
- Deliveries are retried on any non-2xx response or network error, with exponential backoff — roughly
30s, 1m, 2m, 4m, 8m, …(each interval jittered ±15% and capped at 4 hours), up to 8 retries over about 2 hours. - Respond
2xxto acknowledge. Respond410 Goneto permanently unsubscribe — the endpoint is disabled withdisabled_reason: "410_gone". - After 20 consecutive failed deliveries (each counted only once its retries are exhausted) an endpoint auto-disables with
disabled_reason: "too_many_failures". Re-enable it withPATCH{ "is_active": true }, which also clears its failure state. - You can inspect recent attempts via the delivery log and send a test event with ping. Delivery log rows are retained for 30 days.
Delivery rate limit
Each endpoint is capped at 10 deliveries per second. When a burst of events exceeds that (say, a few hundred contacts edited in quick succession), the excess deliveries are automatically re-queued and delivered a moment later — they are never dropped. Being throttled does not count as a failed attempt, so a busy endpoint can never trip the auto-disable threshold on rate limiting alone. Sustained throughput averages the cap.
Automation "Send Webhook" action
Automations can include a Send Webhook step. When a contact reaches that step, Audienceful POSTs to the configured URL. This is separate from the webhook endpoints described here (it fires from inside an automation, not from an event subscription), but uses the same signing scheme.
{
"event": "automation.webhook",
"automation": { "id": "auto_...", "name": "Welcome series" },
"person": {
"id": "jQKdwqp3YRRtTrwqUJEp7d",
"email": "person@example.com",
"extra_data": { "plan": "pro" }
},
"event_properties": { "...": "..." }
}
Any keys configured on the action's extra payload are merged in at the top level. The request carries the same X-Audienceful-Signature and X-Audienceful-Event (automation.webhook) headers, signed with the action's own secret (shown in the action config) — verify it the same way. Delivery is fire-and-forget with retries: a slow or failing endpoint never blocks or stalls the contact's progress through the automation.