Skip to main content
The Quo webhook API delivers real-time events to an HTTPS endpoint you control. Use it to:
  • Update your CRM when an inbound SMS arrives (message.received).
  • Log calls in your system as soon as they end (call.completed), with AI summaries and transcripts following (call.summary.completed, call.transcript.completed).
  • Sync contact changes between Quo and your system (contact.updated).
If you want to ship a working integration first and read concepts second, jump to the Quickstart.
You can send a real, signed test delivery without writing any handler code using POST /webhooks/:id/events/test. See Send a test event.

Common envelope

Every webhook delivery uses the same top-level shape. The type field determines the schema of data.
Treat all event, delivery, and resource ids as opaque strings.

Versioning policy

Webhooks follow the same dated versioning as the rest of the API, with one addition: a subscription pins its payload version at creation.
  • Pinning. Set Quo-Api-Version when calling POST /webhooks. That version is recorded for the subscription and used for every delivery it makes.
  • Stability. A webhook’s version never changes after creation. Existing deliveries keep the shape they were created with, even after newer versions ship.
  • Adopting a new version. Create a new subscription on the newer version. Deliveries to the existing subscription are unaffected.

Subscription rules

  • message.* and call.* events accept a resourceIds filter (phone number ids, or ["*"]).
  • contact.* events are workspace-wide. For contact-only webhooks, omit resourceIds.
  • Mixed subscriptions are supported. resourceIds filters the activity events above; contact events are always delivered workspace-wide.
  • A workspace can have at most 50 webhooks.

Delivery semantics

Idempotency

Quo will retry deliveries on any non-2xx response, so your endpoint must tolerate seeing the same delivery more than once. Use the webhook-id header as an idempotency key — it is stable across retries and distinct from the envelope id.
Store processed ids for at least 28 hours - long enough to cover the full retry window.

Retries

Failed deliveries are retried automatically. Any 2xx response marks a delivery accepted; any non-2xx response triggers the next retry. After all retries fail, the delivery is marked failed and not retried further. If every attempt fails, the delivery is marked failed after roughly 27 hours and 35 minutes from the initial attempt. You can also trigger a manual retry with POST /webhooks/:id/events/:eventId/retry.

Ordering

Delivery order is not guaranteed. Events can arrive out of order across event families and, occasionally, within a single resource — for example, a call.transcript.completed may arrive before the corresponding call.summary.completed. Design handlers to be order-independent:
  • Compare data.resource.updatedAt (or createdAt for terminal events) against your stored state and ignore stale events.
  • Don’t drive state machines from arrival order. Treat each event as a freshness check on the resource it references.

Headers on every delivery

Always verify the signature before trusting the body. See Validate webhook signatures.

See also