Skip to main content
Event routing is a matrix: rows are events, columns are destinations. Each cell answers one question — may this event, for this call, go to this destination? Set it in the dashboard at Settings → Event routing for the whole tenant, and at Campaign → Settings → Event routing for one campaign.

What is routable

Only the five call events that mean something to an external system: ai_session.*, campaign.*, agent.* and lead.completed are webhook-only by design. They have no Salesforce meaning, so they are not rows in the matrix at all rather than rows that cannot be changed.

Destinations

  • Salesforce — contribute to this tenant’s Call_Detail__c writeback. Requires a configured Salesforce connected app; without one the column is locked in the dashboard, because a checkbox that writes nowhere is worse than an absent one. Three of the five Salesforce cells have no producer at all — see below.
  • Webhook — publish to your webhook subscriptions.

Cells with no producer

Three Salesforce cells exist in the matrix but nothing reads them: Yotel’s Salesforce writeback is triggered by the disposition (call.disposition_set) and by the recording (call.recording_ready) — never by a lifecycle event. One Call_Detail__c per call, carrying the outcome the call actually had.
Turning one of these on does nothing, and turning call.disposition_set → salesforce off stops your writeback entirely. The API accepts a write to an unsupported cell and reports it back on the next GET, so it looks like it worked. If you want the mechanical outcome as well as the behavioural one, take it from the call.ended webhook — do not move the Salesforce switch onto it.
Every unsupported cell ships off, is locked in the dashboard, and is listed in the unsupported array of every routing GET — read that array rather than hard-coding this table, since it is the API’s own answer and it carries a human-readable reason.
Routing is an additional gate, never a bypass. A subscription still applies its own event filter on top. For an event to reach your endpoint it must pass both: routing says the event may go to webhook and your subscription is subscribed to that event type. Turning a routing cell on does not subscribe you to anything.

How a cell is resolved

Per cell, most specific first:
Resolution is per cell, never per matrix. A campaign override that names only call.ended leaves every other event resolving through the tenant default. And a stored false is an override, not an absence — turning a cell off at tenant level really does turn it off, it does not fall through to the shipped default. A campaign is either inheriting — it has no override at all, and tracks the tenant defaults as they change — or pinned to its own matrix. The dashboard’s Inherit tenant defaults toggle is that state. Turning it off seeds the campaign’s matrix from the values the campaign is resolving to right now, so nothing changes until you change it; turning it back on discards the override.

Shipped defaults

Chosen to reproduce the behaviour that existed before routing did. If you have never configured anything, this is what you have: Every webhook column ships on so no existing subscription goes quiet — a tenant does not notice a webhook that stops arriving, so that guarantee is pinned by a test rather than left to review. call.ended → Salesforce ships off because the Salesforce write is triggered by the disposition, not by the hangup: one Call_Detail__c per call, carrying the outcome the call actually had.

API

All four routes require the seats:manage permission (tenant admins). A GET returns the closed sets alongside the resolved matrix, and each cell carries the layer that decided it:
unsupported lists every cell that has no producer (see Cells with no producer). It is returned by all four routes and is the authoritative list — the dashboard locks its cells from it. Writing to one of those cells is not an error and the value is echoed back on the next GET; it simply never does anything. The campaign routes add campaign_id and inherits. inherits is the authoritative answer to “is this campaign pinned?” — per-cell source cannot tell you: a campaign that stored a partial override reports no campaign sources while still being pinned. A PUT body wraps the matrix:
(That example turns the recording PATCH off for this campaign. It is a cell that does something — call.ended → salesforce would be accepted and reported back while changing nothing.) Send {"event_routing": null} to clear — at campaign level that means “go back to inheriting”, at tenant level “go back to the shipped defaults”. A partial matrix is legal and is the point: what it does not name keeps resolving through the layer below. An unknown event or destination is a 422, never silently stored — a typo would otherwise resolve to the shipped default and look like it worked.