Delivery, signatures & retries
Every event is delivered as an HTTPPOST with a JSON body using the standard envelope { event, organization_id, data }. Endpoints are managed in Settings → Webhooks in the dashboard, which also gives you a self-serve portal with full delivery history, per-attempt response codes, and manual replay.
Headers. Each delivery carries three headers:
Signature verification. Every endpoint has its own signing secret, available from the webhooks portal. Verify the signature on every delivery before trusting the payload; reject anything that fails verification or falls outside your timestamp tolerance.
Retries. A delivery is considered failed on any non-2xx response or timeout. Failed deliveries are retried automatically with exponential backoff over several hours; after the automatic schedule is exhausted, manual replay remains available from the portal. Return a 2xx as fast as possible — enqueue the event and process it asynchronously rather than doing work inline.
Idempotency. Dedupe on the
svix-id header. Phone-call events additionally carry a producer-side idempotency key of the form <event_type>.<session_id> — each phone event type fires at most once per session.
conversation.started suppresses duplicate publication attempts for the same organization and session for three days. Delivery retries can still reach your endpoint more than once; dedupe using the header above.
Ordering is not guaranteed. Retries mean events can arrive out of order; use the timestamp field in the payload, not arrival order.
conversation.started
This event is triggered when a new live session is created, regardless of the channel. It does not wait for a contact message. Historical import jobs do not emit it, and later messages or reopening an existing session do not emit it again.
first_message contains the first stored message available at creation, or an empty string ("") if none exists yet. timestamp is the session creation time. channel identifies the session’s actual channel; phone sessions use phone_voice.
conversation.closed
This event is triggered when a conversation is closed, a conversation can be marked as resolved or not resolved, and can be closed by a human or an AI.
closed_by identifies what triggered the close, while assignee identifies who the conversation was assigned to at close time. They are independent: a workflow or AI close on a conversation assigned to a human agent still carries that agent’s details in assignee, so you can attribute the resolution to the assigned agent even when they didn’t close it themselves.
conversation.handoff_to_human
This event is triggered when a conversation is handed off to a human agent. Note that a handoff event can happen for multiple reasons; for example, a human agent might decide to take over the conversation, or the AI might decide to hand off the conversation to a human agent.
conversation.contact_response
When a contact sends a message to your inbox.
conversation.ai_response
When an AI responds to a contact message,
conversation.agent_response
When a human agent responds from the Open inbox,
conversation.third_party_agent_response
When a third-party software agent responds to a previously handed-off conversation, Open will notify you. In some configurations, Open might also initiate the handoff to a third-party agent, such as Zendesk or Intercom. Even while the third-party agent handles the conversation, Open continues to monitor and send events to your webhooks.
conversation.handoff_to_third_party_failed
Triggered when a handoff to a third-party platform (e.g. Zendesk, Intercom) fails.
workflow.run.succeeded
Triggered when a workflow run completes successfully.
workflow.run.failed
Triggered when a workflow run fails.
workflow.dispatch_failed
Triggered when a phone call could not hand one of its lifecycle stages to the workflow engine at all — so the workflows listening on those triggers never ran, and no workflow.run.failed can follow (there is no run). This is the only signal for that failure mode; a stage whose workflows did run but failed produces workflow.run.failed instead, never both.
csat.requested
Triggered when a CSAT survey is sent to a contact (via email, SMS, WhatsApp, Slack, or shown in the web widget).
csat.score_submitted
Triggered when a customer submits or updates a CSAT score for a conversation.
Phone Call Events
These events are fired for phone calls — both calls handled by AI phone agents and human-only direct-ring calls (seeis_human_call). They apply to both inbound and outbound calls. conversation.started fires when the phone session is created; phone_call.started fires when the call starts. Additional calls on an existing session do not emit another conversation.started. Phone calls do not emit conversation.closed — its session message fields (closed_by, is_from_third_party, contact_reason, …) don’t map cleanly to a phone interaction. Listen on phone_call.ended instead, which carries the equivalent resolution and context fields below.
phone_call.started
Triggered when a phone call is initiated — either an inbound call is received or an outbound call is placed.
The session’s conversation.started event is emitted separately at session creation.
phone_call.answered
Triggered when the remote party picks up the call (SIP participant joined).
Phone call ended
phone_call.ended
Triggered when a phone call ends. This event carries all end-of-call context — resolution status, who hung up, the resolved contact, the agent’s configured languages, the call summary, and the session’s custom_data (SIP X-headers + call metadata) — so listeners do not need a separate conversation.closed event for phone calls.
For AI calls using the post-call workflow path, this event is sent after Call Finished workflows finish, so their updates to session custom data can be included. Timing and available fields vary by calling path; do not assume that every human-call event runs those workflows or contains a final summary. See Call lifecycle and outcomes.
When
custom_data.knowledge_snapshot is present, scoped snapshots include instruction_scope, knowledge_scope, and captured_at. For compatibility, the call-ended webhook also supplies top-level instruction_ids and directory_ids arrays inside the snapshot, defaulting missing arrays to []. Existing legacy arrays retain their values. These empty compatibility arrays do not mean the agent had no instructions; read the scope fields for configured selections. A scope with mode: "all" can have empty selection arrays. Scope selections do not identify which instructions were used during the call.
phone_call.transferred
Triggered when the AI agent transfers a call to another destination (phone number, SIP URI, or graceful hangup).
phone_call.failed
Triggered when a phone call fails to connect. The failure_category field indicates the type of failure.
Email events
Email events cover broadcasts, sequences, session emails, and organization notifications sent through the existing tracked email paths from your verified sending domains. Each event is delivered only to webhook endpoints belonging to the sending organization. Configure subscriptions in Settings → Webhooks.
Only opens and clicks require their respective tracking settings. Subscribing to events does not enable tracking. An open detection is not proof that a person read the email. Events describe newly reported activity; subscribing does not replay historical events. Untagged emails and mail sent through other delivery integrations are outside this event stream.
All 10 events use the same payload:
svix-id header and use data.timestamp because events may arrive out of order.