Webhooks
Subscribe to replies, sends, bounces, and other Scheduler Zero workspace events.
# Webhooks
Webhook subscriptions send workspace events to an HTTPS endpoint you control. Common events include replies, positive replies, sends, bounces, and reply-label changes.
## Create a subscription
Call `POST /v1/workspaces/{workspaceId}/webhooks` with the destination URL and the event types you want to receive. Use the same Workspace-scoped operations through MCP `manage_webhooks`. Store the returned subscription ID for updates and delivery history.
Choose all campaigns, selected campaigns, or campaign tags, and optionally filter label events by built-in or custom label IDs. Subscription and delivery lists support server-side search, filters, cursor pagination and newest/oldest sorting.
Custom headers accept your exact `Authorization` value; no authentication scheme is added. Values and complete destination URLs are encrypted in the credential vault and are not returned after save. Send a value to replace it, omit headers to keep them, or send an empty header list to clear them. Destinations must use HTTPS and pass public-address checks; redirects are rejected.
Tests send one real request with synthetic event-specific data and saved credentials. Automatic deliveries use bounded exponential backoff with jitter (up to eight attempts within three days); tests do not retry automatically. Inspect delivery history before explicitly retrying an uncertain delivery, since a recipient may already have acted on it.
## Verify signatures
Create with `signing: "generate"` to receive a signing secret once, or `signing: "unsigned"` for unsigned delivery. Secrets can be rotated; reads never return them. For signed JSON subscriptions, Scheduler Zero signs each delivery with HMAC-SHA256 over the raw request body.
```http
X-Webhook-Signature: <hex digest>
X-Webhook-Signature-Algorithm: sha256
```
Slack-formatted requests do not carry these signature headers. JSON event payloads use version `"1"` and a stable event ID; deliveries include `Idempotency-Key` and `X-Webhook-Id`. Consumers must deduplicate repeated events.
Compute the HMAC using the unparsed request bytes and your subscription secret, then compare signatures with a constant-time comparison. Reject requests with a missing or invalid signature.
See the webhook operations in the [REST API reference](/rest-api/reference) for full request and response schemas.
## Bounce classification
The `all_bounced_emails` event includes `metadata.bounceCategory`:
- `recipient`: the recipient mailbox or address was rejected, including an unknown address or a full mailbox.
- `sender`: the sending mailbox, domain, authentication, or reputation was rejected.
- `null`: the delivery notice contains insufficient evidence to determine the cause.
`metadata.bounceType` continues to describe permanence (`hard`), independently of cause.
`metadata.bounceReason` contains the recorded diagnostic when available, and
`metadata.sendHistoryId` identifies the affected campaign send. Temporary delivery
notices do not trigger the terminal bounce event.
Historical classification backfills update reporting without replaying webhooks.
Existing consumers can continue using `status: "bounced"`; campaign contact API
responses add the separate nullable `bounceCategory` field.
Campaign and inbox analytics summaries expose `recipientBounceCount`,
`senderBounceCount`, and `unclassifiedBounceCount`, plus their corresponding
`*BounceRate` percentages. These counts sum to `bounceCount` and use the same
filters as the total. Auto-pause continues using the total bounce rate, including
unclassified bounces. The legacy workspace `/campaigns/stats` bounce rate remains
a distinct-lead rate; its category counts describe sends.
## Delivery status updates
Subscribe to `all_delivery_updates` for delivery evidence changes tied to a
campaign send. `metadata.deliveryOutcome` is `pending`, `delivered`, `failed`, or
`unconfirmed`. `metadata.deliveryAction` and `metadata.deliveryEventId` identify
the notice that produced the update; `metadata.sendHistoryId` identifies the
original send. When known, metadata also includes `failureCharacter`,
`bounceCategory`, `bouncedRecipient`, and `bounceReason`. A final failure is also
eligible for the existing `all_bounced_emails` event for compatibility. Delivery
updates report evidence; they never request that the message be resent.
### Campaign auto-paused
Subscribe to `campaign_auto_paused` to receive an occurrence when an active
campaign automatically pauses at or above its configured bounce-rate threshold.
Manual pauses and other pause causes do not emit this event. Select it through
Workspace webhook settings, the webhook create/update API, or MCP
`manage_webhooks`; synthetic test delivery uses the same event value.
The version `1` envelope carries stable `eventId`, `timestamp`, `organizationId`
(Workspace UUID), `campaignId` and `campaignName`. Its `metadata` includes:
- `reason`: `high_bounce_rate`; `previousStatus`: `active`; `status`: `paused`.
- `bounceRate` and `threshold`: percentages, not fractions.
- `sentCount`, `bouncedCount`, `recipientBounceCount`, `senderBounceCount` and
`unclassifiedBounceCount`: lifetime send counts recorded at the transition.
No contact or email content is included. The event is committed with the pause,
independently of notification email; retries preserve its identity and snapshot.
Resuming and subsequently auto-pausing creates a new event. Existing Workspace,
campaign and tag subscription filters apply, along with ordinary signing,
retry and delivery-history behavior.