Skip to main content

Flows

A flow binds an agent to actual telephony. There are two kinds, set by kind at creation and immutable afterwards:

KindWhat it is
inboundA phone line. Calls to phone_number are answered by this agent
outboundA campaign. The agent dials a contact list — see Campaigns

Status​

StatusMeaning
draftConfigured but inert. Inbound does not answer; outbound does not dial
activeLive
pausedWas active, stopped
completedA campaign that has finished its list

These four are enforced by a database constraint; any other value is rejected.

List flows​

GET /api/voice/flows?kind=outbound&status=active&include_metrics=true

ParamNotes
kindinbound or outbound
statusdraft, active, paused
include_metricsAttaches a live rollup computed from the flow's calls
{
"flows": [
{
"uuid": "f1g2h3i4-j5k6-7890-abcd-ef1234567890",
"kind": "outbound",
"name": "September winbacks",
"status": "active",
"phone_number": null,
"customer_count": 240,
"branches": ["winback", "north"],
"start_date": "2026-09-01",
"end_date": "2026-09-30",
"time_window": "{\"from\":\"09:00\",\"to\":\"18:00\"}",
"config": {
"script_text": "Hi {name}, this is Aria from Pragati AI.",
"no_answer": { "max_retries": 2, "retry_interval_minutes": 120 },
"calls_per_minute": 10
},
"widget_key": null,
"embed_enabled": false,
"voice_agent": { "uuid": "a1b2...", "name": "Aria" },
"metrics": {
"calls_today": 42, "total_calls": 240, "avg_handle_time": 97,
"missed_rate": 0.39, "connect_rate": 0.61
},
"created_at": "2026-09-01T08:00:00Z",
"updated_at": "2026-09-20T09:15:44Z"
}
]
}

Get a flow​

GET /api/voice/flows/{uuid} — also accepts include_metrics.

Create a flow​

POST /api/voice/flows

{
"kind": "outbound",
"name": "September winbacks",
"voice_agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"workflow_id": "w1x2y3z4-a5b6-7890-abcd-ef1234567890",
"branches": ["winback", "north"],
"start_date": "2026-09-01",
"end_date": "2026-09-30",
"time_window": "{\"from\":\"09:00\",\"to\":\"18:00\"}",
"config": {
"script_text": "Hi {name}, this is Aria from Pragati AI.",
"no_answer": { "max_retries": 2, "retry_interval_minutes": 120 },
"calls_per_minute": 10
}
}
FieldTypeRequiredNotes
kindstring✓inbound or outbound
namestring✓
statusstring—Defaults to draft
voice_agent_idstring—The agent's UUID, resolved and checked against your org
workflow_idstring—Workflow UUID — decides handoff behaviour
phone_numberstring—Inbound only: the DID this flow answers
branchesstring[]—Outbound only: customer tags to call. Empty means the whole org
start_date / end_datedate—YYYY-MM-DD
time_windowstring—A JSON string, not an object — see below
configobject—Per-kind settings — see below
time_window is a JSON-encoded string

The column is text, so send "{\"from\":\"09:00\",\"to\":\"18:00\"}" rather than a nested object. A window that will not parse is treated as "any time" rather than refusing to call at all.

Update a flow​

PATCH /api/voice/flows/{uuid} — same fields.

Omitted fields keep their stored value

Updates are applied field by field: a field you omit, or send as null, keeps what is already stored rather than being cleared. Send a new value to change one.

Toggle a flow​

POST /api/voice/flows/{uuid}/toggle

Flips active ↔ paused. A draft flow activates. Activation is refused when the flow could not actually work:

StatusCause
400{"detail": "assign a voice agent before activating this flow"}
400{"detail": "assign a phone number before activating an inbound flow"}

Delete a flow​

DELETE /api/voice/flows/{uuid} → 204

Inbound configuration​

{
"kind": "inbound",
"name": "Support line",
"voice_agent_id": "a1b2...",
"phone_number": "+915550010000",
"status": "active",
"config": {
"greeting": "Thank you for calling Pragati AI, how can I help?",
"filler_phrase": "One moment…",
"call_intent": "Answer product questions and book demos.",
"cta_message": "Shall I book you a demo this week?"
}
}
KeyPurpose
greetingSpoken verbatim on answer, before the model is involved
filler_phrasePlayed while a slow turn is being generated
call_intentWhat this line is for; goes into the system prompt
cta_messageThe close the agent works toward
language_codeOverrides the agent's default
enable_rag / datasource_idsKnowledge-base grounding

How an inbound call is matched​

The dialled number is matched on its last ten digits, because providers disagree about the country prefix — the same line arrives as +915550010000, 915550010000, or 5550010000 depending on the trunk.

Set a greeting

With no greeting, the model produces the opening line itself — and if that line happens to contain a closing phrase such as "thank you for calling", the call is hung up immediately. A configured greeting is spoken verbatim and bypasses that check entirely. See Calls.

Outbound configuration​

KeyDefaultPurpose
script_text—The opening line. {name} and {phone} are substituted
no_answer.max_retries0Extra attempts for someone who did not pick up
no_answer.retry_interval_minutes120Minimum gap before retrying
calls_per_minute10Dialling pace

Placeholders with no value are left as written — an agent saying Hello {name} is a visible bug someone will fix, while "Hello ," just sounds broken and gets blamed on the phone line.

Flow metrics​

With include_metrics=true:

{
"date": "2026-09-22T00:00:00Z",
"calls_today": 42,
"calls_made": 240,
"total_calls": 240,
"avg_handle_time": 97,
"missed_rate": 0.39,
"connect_rate": 0.61,
"answer_rate": 0.61,
"ptp_rate": 0,
"rpc_rate": 0,
"peak_hour": 0,
"sla_breached": 0
}

calls_made and answer_rate mirror total_calls and connect_rate. ptp_rate, rpc_rate, peak_hour, and sla_breached are present on the rollup and reserved for collections reporting.

Computed live from the flow's calls rather than read from a counter, so a corrected or backfilled call is reflected immediately. A call with duration = 0 counts as missed.