Skip to main content

Delivery, signatures & retries

Every event is delivered as an HTTP POST 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 (see is_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:
Email bodies, attachments, device information, and internal routing metadata are not included. Delivery retries of the same incoming event use a stable idempotency key; separate opens and clicks remain separate events. Consumers should still deduplicate deliveries using the svix-id header and use data.timestamp because events may arrive out of order.