Skip to main content
Every webhook delivery uses the common envelope with the same id, apiVersion, createdAt, type, and data fields. The sections below document only the per-event data wrapper - the part that varies by event type. The type field on the envelope determines which schema below applies.

Quick index

To see any event payload against your endpoint without waiting for live traffic, call POST /webhooks/:id/events/test with the eventType you want to inspect.

Field semantics

Two patterns appear in multiple events. Both indicate why a field may be empty rather than implying “no data exists”.

contacts.lookupStatus

Explains why contacts.ids may be empty:

participants.resolution

Explains whether participant context was resolved on call events:

Common type aliases

Several events reuse these types. Each event references them by name rather than redefining them inline.

message.received

An inbound message was received by Quo. Use this as your inbound trigger.
senderIdentifier and recipientIdentifiers are raw participant identifiers. They are usually E.164 phone numbers, but direct-number and internal flows can emit non-phone identifiers.

message.delivered

An outbound message was delivered. This is delivery confirmation, not a read receipt.
If you correlate deliveries with conversations, key on context.conversationId and context.phoneNumberId rather than links.quo.

message.failed

An outbound message failed to deliver. resource.errorCode contains the carrier or provider failure code when one is available; it is null when the failure reason is unknown.
errorCode is the raw code returned by the carrier or messaging provider. It is present only on message.failed and is absent from all other message events.

call.ringing

call.ringing fires once at the start of an incoming or outgoing call. Its context is narrower than later call events — it carries participants (the workspace and external phone numbers) so you can identify the caller at ring time, but it omits contacts and phoneNumberType, which are not resolved yet. For an incoming call the caller is in participants.external; the Quo number that was dialed is in participants.workspace.
Use participants.external to look up the related contact via the API at ring time. When participant context cannot be resolved, participants.resolution is unavailable and the arrays may be empty — treat empty arrays as unknown rather than as no participants. Later call events include richer context such as contacts and phoneNumberType.

call.menu.selected

A caller reached a routing decision in a phone menu (IVR) on an incoming call. phoneMenuSelection is the digit the caller pressed and phoneMenuSelectionName is the label of the option they chose; both are null when the menu resolved without an explicit selection — for example when it timed out and fell through to a default route. statusReason explains how the menu routed the call.
A digit was pressed and the selection was fully resolved:
When the caller makes no selection — for example the menu times out and falls through to voicemail — phoneMenuSelection and phoneMenuSelectionName are null and statusReason reflects where the call was routed. Only the differing resource is shown below; context and links are unchanged from the example above:
Possible values for statusReason:
  • phoneMenuSelection is the raw digit string the caller pressed (for example "1"), never a number.
  • statusReason is null when Quo cannot attribute a routing reason to the selection.
  • context is the full CallContext, so contacts and phoneNumberType may be degraded to unavailable/null when the call context could not be fully resolved.

call.answered

A call was connected. answeredByUserId is the Quo user associated with the answer when known. It is not the external party who answered an outgoing call.
call.answered also fires on outgoing calls when the recipient’s voicemail picks up. Treat this event as “the call connected” rather than “a person is on the line.”

call.completed

A call ended. This is the terminal lifecycle event with the final status, duration, and timestamps. Recording, summary, transcript, and voicemail artifacts (if any) arrive as separate events and may be delayed by minutes.
Possible values for status:
  • hasVoicemail indicates whether a voicemail was left. The voicemail itself arrives as a separate call.voicemail.completed event.
  • duration is in seconds and may be null for calls that never connected.

call.forwarded

An incoming call was forwarded. This event carries the phone numbers involved in the forward. Other call lifecycle events do not include forwardedFrom or forwardedTo.

call.missed

An incoming call ended without being answered. Outgoing calls do not produce this event — they terminate with call.completed and a non-answered status. The payload is intentionally minimal. To get the full call shape, look up the call by resource.id in the API.

call.recording.completed

A call recording finished processing. May arrive after call.completed. recordings is always an array — an empty [] means no recording metadata is available in this payload.
Download or persist the recording file rather than relying on the URL for long-term access.

call.summary.completed

A call summary finished processing. This is a summary readiness event, not a call-ended event — the call may have ended much earlier. Trust processingStatus, not arrival time, when interpreting summary state.
Notes:
  • summary and nextSteps are arrays when processingStatus === 'completed'; otherwise null.
  • jobs is an empty array when no AI agent job metadata is available.
  • Use callId to correlate the summary with its source call and any transcript event.

call.transcript.completed

A call transcript finished processing. Transcript and summary events are independent and may arrive in either order for the same call.
Not every dialogue line maps to a Quo user. External participants surface as identifier without a userId; internal speakers surface as userId with or without an identifier.

call.voicemail.completed

A voicemail was left and finished processing. resource.id is the voicemail activity id. resource.callId is the source call activity id, or null if the source call could not be resolved. Use resource.callId to correlate the voicemail with the rest of the call lifecycle.
  • transcript is null while processing or if transcription was not available for the voicemail.
  • Download or persist the voicemail recording rather than relying on recordingUrl for long-term access.
  • If the source call context cannot be fully resolved, participants.resolution may be unavailable and participant arrays may be empty. If the source call itself cannot be resolved, resource.callId will be null and phoneNumberType will be null.

contact.updated

A contact was created or its fields changed. Use updatedAt for ordering and freshness checks. Contact events are workspace-wide; see Subscription rules.
Notes:
  • customFields[].id is omitted when the source item has no id.
  • Invalid number or date custom field values normalize to null.
  • multi-select values are always arrays.

contact.deleted

A contact was deleted. The resource shape matches contact.updated; the event discriminator is type. Soft-deleted email addresses, phone numbers, and custom fields are removed from the payload rather than delivered with a deletion marker.
The JSON shape is identical to contact.updated, with type: "contact.deleted" on the envelope.

task.created

A task was created. resource.dueDate is null when no due date was set at creation time.

task.updated

A task’s title or description changed. updatedFields reports which fields were part of this update. updatedValue is present only when isUpdated is true; when isUpdated is false the field was unchanged and updatedValue is absent.

task.deleted

A task was deleted. Only the task ID is included in the resource.

task.completed

A task was marked complete. Only the task ID is included in the resource.

task.reopened

A completed task was reopened. Only the task ID is included in the resource.

task.assigned

A task was assigned to a user. context.actorId is the user who performed the assignment; resource.assignedTo is the user the task was assigned to.

task.unassigned

A task was unassigned. context.actorId is the user who removed the assignment; resource.unassignedFrom is the user who was previously assigned.

task.overdue

A task has passed its due date without being completed. resource.dueDate is the missed due date in ISO 8601 format.
context.actorId reflects the task owner at the time the overdue check ran.

task.linked

A task was linked to a conversation, activity, or phone number. The linked resource is reflected in context.conversationId, context.activityId, and context.phoneNumberId.

task.unlinked

A task was unlinked from a conversation. context.conversationId is the conversation the task was detached from.

task.duedate.updated

A task’s due date was set or changed. resource.updatedDueDate is the new due date in ISO 8601 format.

task.duedate.removed

A task’s due date was cleared. Only the task ID is included in the resource.

See also