Skip to main content

Call flow and routing

This guide walks the full request path of a masked voice call and shows where each millisecond goes.

Primary call flow​

The agent taps "Call customer" in your app. The SDK already knows the active session and proxy number. From there:

sequenceDiagram
participant Agent as Agent (Driver app)
participant SDK as Relavoi SDK
participant Dialer as Native dialer
participant AT as Telephony Provider
participant WH as Webhook Handler
participant CR as Call Router
participant Redis
participant Customer as Customer phone

Agent->>SDK: tap "Call customer"
SDK->>Dialer: open tel://+2348000000001
Dialer->>AT: PSTN call to proxy
AT->>WH: POST /v1/webhooks/cpaas/voice (HMAC signed)
WH->>WH: verify signature + dedupe event_id
WH->>CR: route(proxy, caller)
CR->>Redis: HGET session:proxy:+2348000000001
Redis-->>CR: session sess_a1b2c3d4 (state=ACTIVE)
CR->>CR: caller matches party_a -> forward to party_b
CR-->>WH: <Dial>+2348087654321</Dial>
WH-->>AT: 200 OK + dial XML
AT->>Customer: ring with caller ID +2348000000001
Customer-->>AT: answer
AT->>WH: call.answered event
WH->>Redis: SET session.activated_at

When recordingEnabled = true, the Call Router prepends a <Say> or <Play> action before the <Dial>. The bridge does not happen until the consent prompt finishes.

sequenceDiagram
participant AT as Telephony Provider
participant WH as Webhook Handler
participant CR as Call Router
participant Customer as Customer phone

AT->>WH: POST incoming call
WH->>CR: route(proxy, caller)
CR->>CR: recording_enabled=true, consent_prompt=DEFAULT
CR-->>WH: <Say>This call may be recorded...</Say><Dial>+2348087654321</Dial><Record/>
WH-->>AT: 200 OK
AT->>AT: play consent text-to-speech
AT->>Customer: ring
Customer-->>AT: answer (recording begins)

If consentPrompt = CUSTOM, the router emits <Play>https://cdn.tenant.com/consent.mp3</Play> instead.

Latency budget​

Our routing SLO is p99 < 500 ms from webhook receipt to dial response. The budget breakdown:

StageBudgetNotes
TLS + HTTP parse20 msFastify with HTTP/2
HMAC signature verify5 mscrypto.timingSafeEqual
Redis dedup lookup5 msGET webhook:dedup:{event_id}
Redis session lookup10 msHGETALL session:proxy:{number}
Routing decision5 msPure CPU, no I/O
Response serialization10 msXML build
Network back to CPaaS30 msSame-region (Cape Town)
Total p50~85 ms
Total p99 headroom415 msAbsorbs GC pauses, retries

Anything that pushes routing onto PostgreSQL violates the budget. The Call Router is explicitly designed to never hit Postgres on the hot path.

:::tip Observe your own latency The routing.latency.ms metric is surfaced per tenant in Analytics. Set an alert at p99 > 400 ms to catch degradation before customers notice. :::