data changes. Envelope, headers, signing and retry behaviour are in Webhooks Overview. This page is the field-level reference.
campaign_id is null for events that are not about a campaign — most often a reply to a direct contact email, which has no campaign.
Status at a Glance
A subscription naming any of these validates with a
201. The last three groups simply never produce a delivery.
Replies
reply_received
Category replies. Fires when an inbound email is matched to a conversation.
Delivery
email_sent
Category delivery. Fires once per outbound email accepted by the sending provider.
message_id here is the Azure Communication Services identifier, not the RFC Message-ID header. It will not match the <direct_…@domain> value returned by POST /contacts/{id}/send-email, and it is not what reply threading keys on. Do not use it to correlate a send with a reply.email_delivered
Category delivery. Fires on the provider’s delivery confirmation.
The provider’s own message ID is recorded internally but is not exposed in
data.
email_bounced
Category delivery. Fires on a hard or soft bounce.
Engagement
email_opened
Category engagement. Fires when the tracking pixel loads.
link_clicked
Category engagement. Fires per tracked link.
Lifecycle
lead_status_changed
Category lifecycle. Fires on every lead status transition.
Statuses:
PENDING, QUEUED, SENT, OPENED, REPLIED, FAILED, BOUNCED, UNSUBSCRIBED, EXTERNAL, BLOCKED.
lead_unsubscribed
Category lifecycle. Fires once per lead.
subsequence_entered
Category lifecycle. New. Fires when a lead enters a flow branch, either by an automatic trigger or a manual move.
trigger_reason has two formats depending on how the lead got there:
entry_node_id is the value GET /campaigns/{id}/leads/{lead_id}/journey reports as the branch’s node, and the value move-to-subsequence takes as target_subsequence_node_id. It is not the subsequence_id used by PUT and DELETE /flow/subsequences/{id} — that is the entry edge id. The internal entry_edge_id is recorded but deliberately not exposed, since the public flow API addresses branches by node.lead_id + entry_node_id, mirroring the uniqueness the enrollment table already enforces. One enrollment produces exactly one event, and a re-delivered trigger message cannot double-emit.
subsequence_completed
Category lifecycle. New. Fires when a lead reaches the end of a branch.
entry_node_id identifies which branch completed — a lead can be enrolled in more than one. Same dedupe shape as subsequence_entered, so the two events pair up one-to-one per lead per branch.
campaign_completed — reserved, does not fire
Category lifecycle. The API accepts this name in event_types, and the dispatcher knows how to map it, but nothing writes it to the event stream. Campaign completion is currently surfaced only as an in-app notification inside the ColdSend UI.
A subscription naming it is created successfully and then never delivers. It is listed here so the silence is expected rather than mysterious.
Accepted but Not Delivered
These five validate at subscription time and never arrive. Listed so you do not debug a handler that was never going to be called.Categories
Subscribing by category is an alternative to naming types. The mapping is:Ordering and Guarantees
- At-least-once. Retries and manual redeliveries can produce the same
event_idmore than once. Deduplicate on it. - Not ordered.
email_deliveredcan arrive beforeemail_sent, andsubsequence_enteredbefore thereply_receivedthat caused it. Never assume sequence from arrival order — useoccurred_at. - Best-effort emission. Every emit site is wrapped so that a failure to record an event never fails the business operation it describes. Events can be missing. For anything you must not lose, reconcile against the REST API.
- Same transaction as the write. The subsequence events are written inside the enrollment’s own transaction, so they never survive a rollback of the change they describe.
Next Steps
Webhooks Overview
Signing, retries and the delivery log.
Subsequences
What causes the two branch events.
Direct Email
Sends that produce
campaign_id: null.API Scopes
The
webhooks:* family.
