
Events
Voiceflow will send POST requests to your webhook URL on the following events.
Three older
.v2 variants are deprecated and stop firing on October 9, 2026. See Deprecated events.
Session events
Session events fire whenever a conversation starts or ends, across every project and every channel. A session is the conversation itself, the state your agent builds up as it goes, so every conversation has one. These are the events most integrations listen for. A session has to exist for these events to fire. The chat widget, voice conversations, and the Start session endpoint each create one; the deprecated legacy Interact endpoints don’t, so conversations run through those produce no session events.runtime.session.start
This event is sent when a new session is started for a user.
Example runtime.session.start payload
Example runtime.session.start payload
runtime.session.end
This event is sent when a session ends: because the conversation reached an end, because the caller hung up, or because the session sat idle past its timeout.
Example runtime.session.end payload
Example runtime.session.end payload
Call events
Call events fire on voice conversations only. They carry call-level details that exist only on a call, such as the phone numbers involved and why the call ended. A voice conversation emits call events and session events, because the two describe different things: the call is the voice connection, and the session is the conversation carried over it. Checktype to tell which event you’re handling, and use data.sessionID to join the two.
Their end events are not interchangeable either:
runtime.call.endfires when the voice connection is torn down.runtime.session.endfires when the session expires. On a normal hangup that follows within a few seconds, because hanging up marks the session as expired. If the hangup never reaches Voiceflow, the session ends on its idle timeout instead, which can be much later.
runtime.call.end for call duration and runtime.session.end for conversation lifetime.
Calls and sessions aren’t one-to-one. A caller who reaches your agent again while their previous session is still active reuses that session, so the second call emits
runtime.call.start and runtime.call.end with no new runtime.session.start. That session’s runtime.session.end arrives once the session itself expires, after the last call. On a reused session, data.sessionID and data.transcriptID are null on runtime.call.start, so that event can’t be joined to its session.runtime.call.start
This event is sent when a voice call starts: an inbound or outbound phone call, or a voice call from the web widget, including test calls in the Voiceflow editor.
Example runtime.call.start payload
Example runtime.call.start payload
runtime.call.end
This event is sent when a call is completed.
Example runtime.call.end payload
Example runtime.call.end payload
Optional fields
Several fields on the four events above are optional and nullable:versionID, transcriptID, versionVariant, projectEnvironmentID, projectEnvironmentAlias, sessionID (call events only), and endReason (on runtime.session.end).
They are populated whenever the value is available, and are null otherwise. Treat a missing or null value as “not known for this conversation” rather than as an error, and don’t require them to be present when parsing a payload.
endReason on runtime.session.end is typed as nullable but in practice always carries a value. Sessions that simply time out report "Session ended due to inactivity timeout", and a session displaced by a new one reports "Session expired because a new one was started". It’s an arbitrary string, so match on it defensively rather than treating the set as fixed.
versionID carries the same value as environmentID. It exists so that consumers written against the deprecated .v2 events, which used the name versionID, keep working after migrating.
transcriptID carries the same value as sessionID, because a conversation’s transcript is keyed on the session that produced it. It’s null when transcripts are disabled for the session, which is the case to guard against if you use it to build transcript links.
Deprecated events
Until that date the deprecated events keep firing exactly as they do today, alongside their replacements, so you can migrate at your own pace. If you subscribe to both an event and its.v2 variant during this window you’ll receive both, and should ignore one of them.
The
.v2 events existed only to add richer metadata. That metadata now ships on the standard events, so the two payloads carry the same information. Differences to handle when you migrate:
runtime.session.end.v2 has also been removed. Unlike the events above it never fired, because its trigger condition depended on data that was never recorded, so it has no subscribers and no migration path is needed. Use runtime.session.end, which now carries the fields runtime.session.end.v2 was intended to add.
Best practices
- Check
typewhen evaluating a response. There may be additional types of events in the future with a different shaped request body, as well as new properties and metadata. - Parse payloads permissively. New fields are added to existing events without introducing a new event name or version, so treat unrecognized properties as expected. A new event name is only introduced when an existing field’s meaning or shape changes.
- Don’t require the optional fields to be present. Handle
null. data.metadatais only included on call events. Checkdata.platformbefore reading it, as the available fields vary by platform (eg:data.metadata.callSidisn’t present on a"web-voice"call).- Join call events to session events on
data.sessionIDwhere it’s present, and don’t assume one call per session. See Call events. - If you’re using the same webhook URL across multiple projects, check
data.projectIDto tell them apart.