Call Flows Integration
Call Flows define the automated routing logic for incoming and outgoing calls. They are built and managed visually using the graphical designer inside the Firetell Console.
Since Call Flow management and structure manipulation are internal resources, no public REST API endpoints are exposed. Instead, developers can integrate custom database lookups and dynamic routing logic directly into their own servers using two key integration nodes: Contact Lookup and JSON Call Action (JCA).
Contact Lookup Node (contact_lookup)โ
The Contact Lookup node allows Firetell to query your CRM, database, or API endpoint to resolve the caller's identity before routing the call.
How it Worksโ
- Internal Fallback (Optional): If the Request URL parameter is left empty, Firetell will automatically search the internal Firetell Call Center database for a contact matching the caller's phone number.
- Webhook Query: If a Request URL is provided, Firetell issues an HTTP
POSTrequest to your configured webhook URL. The request body contains the call context:event(string):"call_flow.contact_lookup".call_id(string): Unique call ID.caller_number(string): The caller's E.164 phone number.destination_number(string): The dialed inbound DID/phone number.workspace_id(string): Workspace identifier.call_flow_id(string): Call flow identifier.
- Your server must respond with a
200 OKJSON payload containing the contact's name.
Expected JSON Responseโ
Firetell scans the response object for a display name using the following fields (in order of priority):
display_namenamefullNamedisplay_titletitle
Example response:
{
"id": "customer_99182",
"display_name": "John Doe",
"email": "john.doe@example.com",
"tier": "VIP"
}
Telephony Behaviorโ
- Caller ID Display Name: If a name is found, Firetell injects it into the SIP headers. Softphones, WebRTC clients, and physical IP phones will display the contact's name instead of a raw phone number.
- Session Variable: The raw JSON response is saved into the call's channel variable (
firetell_contact_data). This data can be referenced later during the call flow or accessed inside subsequent JCA webhooks.
JSON Call Action Node (json_call_action)โ
The JSON Call Action (JCA) node allows your server to dynamically direct and control the call flow in real-time by returning a sequence of execution actions.
How it Worksโ
- When a call reaches this node, Firetell sends an HTTP
POSTrequest to your configured backend webhook URL. - Firetell sends the call context (caller number, dialed destination, workspace ID, call flow ID, call ID, and contact lookup data) in the body.
- Your server should process the business logic and respond with a
200 OKcontaining a list ofactionsto execute next.
HTTP POST Request Bodyโ
{
"event": "call_flow.jca",
"call_id": "call_e9a0c7eb1a744b55a0ee65",
"workspace_id": "ws_abc123",
"call_flow_id": "cf_xyz987",
"caller_number": "14155550100",
"destination_number": "18005550199",
"contact_data": "{\"display_name\":\"John Doe\",\"tier\":\"VIP\"}" // Stringified response from preceding Contact Lookup node
}
Expected Responseโ
Your server must return a JSON response containing an array of compiled call actions.
Example response (Welcome user, and then bridge to an operator):
{
"actions": [
{
"action": "play",
"params": {
"audio_id": "au_welcome_vip",
"answer_call": true,
"continue_on_play": false
}
},
{
"action": "to_operator",
"params": {
"phone_number": "+14155550123",
"timeout": 45
}
}
]
}
Supported Webhook Actionsโ
The following action objects can be returned in the actions array:
1. Play Audio (play)โ
Plays a pre-recorded audio file stored in the workspace cache.
audio_id(string, required): ID of the audio metadata resource.answer_call(boolean, optional): Iffalse, streams the audio as early media (SIP 183 Progress) without answering/billing the call. Default:true.continue_on_play(boolean, optional): Iftrue, runs the audio in the background and immediately moves to the next action. Iffalse, blocks call flow until playback ends. Default:false.
2. Text to Speech (tts)โ
Plays a text announcement using synthetic voice.
text(string, required): The text content to read (max 1000 characters).voice(string, optional): Selected TTS voice identifier.speed(number, optional): Playback speed rate.
3. Collect Digits (ivr)โ
Waits for the caller to press DTMF keys.
timeout(number, optional): Time in seconds to wait for input. Default:5.max_digits(number, optional): Maximum digits to collect (between 1 and 10). Default:1.
4. To Operator (to_operator)โ
Bridges the call to an external phone number or direct SIP URI.
to(object, required): Destination call party object:number(string, required): Destination E.164 phone number or SIP URI.name(string, optional): Destination display name.
from(object, optional): Custom outbound caller ID object:number(string, required): Outbound caller ID / DID number owned by your workspace. If omitted, the call flow's assigned DID is used.name(string, optional): Caller ID name displayed on the callee's device.
gateway_id(string, optional): Specific outbound gateway ID to route the call through.timeout(number, optional): Max ringing time in seconds before failover (max 300). Default:30.
5. Connect Agent (connect_agent)โ
Routes the call to a registered Firetell agent.
agent_id(string, required): Unique agent ID.timeout(number, optional): Max ringing time in seconds. Default:30.
6. Connect Team (connect_team)โ
Bridges the call to a team queue.
team_id(string, required): Unique team ID.strategy(string, optional): ACD strategy (ring-all,round-robin,sequential,random). Default:ring-all.timeout(number, optional): Ring timeout in seconds. Default:30.
7. Voicemail (voicemail)โ
Records a voicemail message from the caller and uploads it to the S3 workspace cloud.
greeting_audio_id(string, optional): Audio ID to play before recording. If omitted, plays default greeting "Please leave your message after the tone."max_duration(number, optional): Max message recording length in seconds. Default:60.beep(boolean, optional): Play a tone beep sound before recording starts. Default:true.
8. Disconnect (disconnect)โ
Ends the call session immediately.
cause(string, optional): SIP hangup cause (e.g.NORMAL_CLEARING).
9. Audio Stream Fork (stream)โ
Fork the call audio in real-time to your own WebSocket server. Your server receives raw PCM audio and can optionally send audio back to be played to the caller.
ws_url(string, required): Destination WebSocket URL (wss://orws://). Must be a publicly routable server.track(string, optional): Which legs to fork โboth(stereo, default),inbound(caller only),outbound(callee only).sample_rate(number, optional): PCM sample rate in Hz โ8000(default) or16000.bidirectional(boolean, optional): Iftrue, audio received from your WS server is played into the call in real-time. Default:false.answer_call(boolean, optional): Answer the call before starting the stream. Default:true.headers(object, optional): Custom headers for the WS handshake, e.g.{ "Authorization": "Bearer token" }. Max 10 entries.
Use Cases & Real-World Examplesโ
[Driver / Courier] (PSTN)
โ
โ 1. Calls Virtual Proxy Number (DID)
โผ
[Firetell Platform]
โ
โ 2. Triggers JCA Webhook POST
โผ
[Partner Server] โโโบ 3. Looks up active delivery order for caller_number
โ
โ 4. Responds with to_operator action
โผ
[Firetell Platform] โโโบ 5. Bridges to customer's real phone number
(Caller ID displayed is the Virtual Proxy Number)
Step 1: Firetell sends JCA Webhookโ
{
"event": "call_flow.jca",
"call_id": "call_9c8df104a8b27e",
"workspace_id": "ws_ecommerce_app",
"call_flow_id": "cf_number_masking",
"caller_number": "14155550144",
"destination_number": "18005550199",
"contact_data": null
}
Step 2: Your Server returns Dynamic Bridge Actionsโ
{
"actions": [
{
"action": "play",
"params": {
"audio_id": "au_connecting_mask_notice",
"answer_call": true,
"continue_on_play": true
}
},
{
"action": "to_operator",
"params": {
"to": {
"number": "+14155550188",
"name": "Customer John"
},
"from": {
"number": "+18005550199",
"name": "Firetell Proxy"
},
"timeout": 30
}
}
]
}
Two-Way Privacy:
Both the caller (driver) and the callee (customer) only see the Virtual Proxy Number on their phone screens, ensuring strict compliance with user privacy regulations and personal data protection standards.
Use Case 2: VIP Customer Dynamic Priority Routingโ
Route callers dynamically based on their customer tier in your CRM or database.
Scenario:โ
- When a call arrives, the Contact Lookup node queries your CRM with
caller_number. - The JSON Call Action receives the caller's tier (
VIPvsStandard). - If
VIP, the call is immediately routed to their dedicated Account Manager. IfStandard, it is routed to the general support queue.
Your Webhook Response (VIP Customer):โ
{
"actions": [
{
"action": "play",
"params": {
"audio_id": "au_vip_welcome_greeting",
"answer_call": true,
"continue_on_play": false
}
},
{
"action": "connect_agent",
"params": {
"agent_id": "agt_account_mgr_alex",
"timeout": 45
}
}
]
}
Your Webhook Response (Standard Customer):โ
{
"actions": [
{
"action": "connect_team",
"params": {
"team_id": "team_general_support",
"timeout": 30
}
}
]
}
Use Case 3: Expired Order or Invalid Access Handlingโ
If a customer or driver calls after an order has already been completed or canceled, your server can reject the connection gracefully without consuming unnecessary call duration or operator time.
Your Webhook Response:โ
{
"actions": [
{
"action": "play",
"params": {
"audio_id": "au_order_completed_notice",
"answer_call": true,
"continue_on_play": false
}
},
{
"action": "disconnect",
"params": {
"cause": "NORMAL_CLEARING"
}
}
]
}
Use Case 4: After-Hours On-Call Duty Escalationโ
Route after-hours emergency calls dynamically based on your company's live on-call schedule (e.g. Opsgenie, PagerDuty, or internal roster).
Your Webhook Response:โ
{
"actions": [
{
"action": "play",
"params": {
"audio_id": "au_after_hours_emergency",
"answer_call": true,
"continue_on_play": false
}
},
{
"action": "to_operator",
"params": {
"phone_number": "+14155550177",
"timeout": 40
}
}
]
}
Use Case 5: Two-Factor Authentication & Voice OTP (2FA)โ
Voice OTP delivery provides higher deliverability and enhanced protection compared to standard SMS. You can implement Voice OTP in two ways:
Option A: Inbound Reverse Voice OTP (User Calls Verification Number)โ
- The user requests 2FA verification on your website/app and is prompted to call your verification number from their registered phone.
- The incoming call hits the Call Flow and triggers the JSON Call Action with
caller_number. - Your server looks up the pending 2FA login session for that phone number and responds with a sequence of pre-recorded audio digit clips (e.g.
0โ9) or dynamic TTS. - Firetell answers, plays the greeting, sequentially plays each OTP digit, and hangs up immediately.
{
"actions": [
{
"action": "play",
"params": {
"audio_id": "au_greeting_2fa",
"answer_call": true,
"continue_on_play": false
}
},
{
"action": "play",
"params": {
"audio_id": "au_digit_9",
"continue_on_play": false
}
},
{
"action": "play",
"params": {
"audio_id": "au_digit_2",
"continue_on_play": false
}
},
{
"action": "play",
"params": {
"audio_id": "au_digit_8",
"continue_on_play": false
}
},
{
"action": "play",
"params": {
"audio_id": "au_digit_4",
"continue_on_play": false
}
},
{
"action": "disconnect",
"params": {
"cause": "NORMAL_CLEARING"
}
}
]
}
Option B: Outbound Voice OTP (Automated Call Broadcast)โ
Your server proactively triggers an outbound call to the user's phone via the Make Call API passing the exact same OTP action chain with max_duration: 45.
Use Case 6: Real-Time Audio Streaming to Your Serverโ
Use the stream action to fork live call audio to your own WebSocket server during the call. This enables two powerful patterns:
Option A: One-Way Monitoring (Transcription / Compliance)โ
Stream audio to your server for real-time transcription, sentiment analysis, keyword spotting, or compliance recording โ without interrupting the call.
[Caller] โโโบ [Firetell] โโโบ Your WS Server (receives PCM audio)
โ โ
โ โโ STT / Analytics / Compliance engine
โ
โโโโบ [Agent / Operator] (normal call continues)
JCA Webhook Response:
{
"actions": [
{
"action": "play",
"params": {
"audio_id": "au_call_recorded_notice",
"answer_call": true,
"continue_on_play": false
}
},
{
"action": "stream",
"params": {
"ws_url": "wss://analytics.your-server.com/call-stream",
"track": "both",
"bidirectional": false,
"headers": {
"Authorization": "Bearer YOUR_INTERNAL_TOKEN"
}
}
}
]
}
Your WebSocket server receives binary PCM audio frames continuously while the call is active. When the call ends, the connection closes automatically.
Option B: Full-Duplex Custom AI Voice Botโ
Connect the caller to your own AI voice bot โ built with any model or TTS provider of your choice. Your server receives the caller's speech and sends synthesized audio back in real-time, which Firetell plays directly into the call.
[Caller] โโโโบ [Firetell] โโโโบ Your WS Server
โ
โโ STT (caller speech โ text)
โโ LLM (generate response)
โโ TTS (text โ PCM audio โ caller)
JCA Webhook Response:
{
"actions": [
{
"action": "stream",
"params": {
"ws_url": "wss://bot.your-server.com/voice-session",
"track": "both",
"sample_rate": 8000,
"bidirectional": true,
"headers": {
"Authorization": "Bearer YOUR_BOT_TOKEN",
"X-Session-Context": "support"
}
}
}
]
}
WebSocket protocol your server must implement:
| Direction | Frame type | Content |
|---|---|---|
| Firetell โ Your server | Binary | PCM audio at sample_rate Hz โ caller's speech |
| Your server โ Firetell | Binary | PCM audio at sample_rate Hz โ bot speech to play to caller |
| Your server โ Firetell | Text (JSON) | {"type":"killAudio"} โ stop queued bot audio immediately (barge-in) |
call_id, workspace_id, caller_number, caller_name, destination_number, and stream_type=firetell_stream are automatically appended to your ws_url as query parameters so your server can identify the call and caller without needing custom headers.
bidirectional: true requires the call to be answered before audio can flow in both directions. The stream action handles this automatically when answer_call: true (the default).