Agents¶
An Agent is AiFlow's configured persona: a voice, a base system prompt,
a model, concurrency limits, and optionally a phone number, all bundled
under one id and slug. This page is the full CRUD reference for
managing that resource through the API. For the conceptual explanation of
why agent_id shows up in nearly every other endpoint, see
Agents & channels. Once an Agent
exists, Context & knowledge and
Tools cover what to configure on it next.
These are admin actions
Every endpoint on this page needs an admin session token (see Authentication), not an API key. Configuring an Agent's persona, voice, and limits is an admin task. Day-to-day traffic against an already-configured agent runs through Sending events, the widget, and phone calls instead.
Admin role required, per operation¶
Unlike most CRUD on this site, these four operations don't all require the same role tier. Creating or disabling an agent is more consequential than editing one that already exists, and the role requirements reflect that:
| Operation | Endpoint | Minimum role |
|---|---|---|
| List agents | GET /api/v1/agents |
Any logged-in admin (Owner, Admin, Editor, or Viewer) |
| Get an agent | GET /api/v1/agents/{agent_id} |
Any logged-in admin |
| Create an agent | POST /api/v1/agents |
Owner or Admin |
| Update an agent | PATCH /api/v1/agents/{agent_id} |
Owner, Admin, or Editor |
| Disable an agent | DELETE /api/v1/agents/{agent_id} |
Owner or Admin |
A request from a role below the listed minimum returns 403. See
Errors & rate limits.
Field reference¶
These fields apply to both create and update. Differences between the two are called out in the Create and Update columns.
| Field | Type | Create | Update | Description |
|---|---|---|---|---|
slug |
string | Required | Not settable | URL-safe identifier, must match ^[a-z0-9]+(-[a-z0-9]+)*$ (lowercase letters, digits, single hyphens between segments), 1-80 characters. Immutable once the agent is created; there is no endpoint that renames a slug. |
name |
string | Required | Optional | Display name, 1-120 characters. |
description |
string or null | Optional | Optional | Free-text note, up to 500 characters. Not shown to callers, admin-facing only. |
status |
string | Optional (default active) |
Optional | One of draft, active, disabled. draft and disabled agents still exist and can be edited, but see Status and reachability below for what actually reaches them. |
voice_name |
string | Optional (default "Kore") |
Optional | The Gemini Live voice this agent speaks with. |
model |
string or null | Optional | Optional | Overrides the platform's default Gemini model for this agent. Leave unset to use the deployment default. |
temperature |
float | Optional (default 0.8) |
Optional | Sampling temperature, 0.0 to 2.0. |
greeting_instruction |
string | Optional (default "Greet the caller and ask how you can help them.") |
Optional | Up to 500 characters. Instruction for how the agent should open a session, not a literal script. |
base_system_prompt |
string or null | Optional | Optional | The agent's core persona and instructions. Context documents (see Context & knowledge) are appended ahead of and after this, not a replacement for it. |
twilio_phone_number |
string or null | Optional | Optional | E.164 phone number this agent answers inbound calls on. More than one agent can share the same number; see max_concurrent_inbound_calls below. |
max_concurrent_inbound_calls |
integer | Optional (default 1) |
Optional | 1 to 50. How many simultaneous inbound calls this agent accepts before it's treated as busy. |
allowed_origins |
list of string or null | Optional | Optional | Origins permitted to embed this agent's widget. null/omitted allows any origin, see Embedding the widget. |
max_concurrent_outbound_calls |
integer | Optional (default 3) |
Optional | 1 to 50. Caps how many outbound calls this agent can have in flight at once, independent of the deployment-wide MAX_CONCURRENT_OUTBOUND_CALLS setting. |
cross_session_lookback_enabled |
boolean | Optional (default false) |
Optional | When true, the agent recalls its last completed conversation with the same caller, visitor, or email sender, and opens the next one with a short recap. Applies to phone, widget, and a connected mailbox alike. |
Status and reachability¶
status: "active" gates two of the three channels described in
Agents & channels: an inbound call to
this agent's twilio_phone_number, and a new widget session against its
slug. Unless status is exactly active, both are rejected as though
the agent didn't exist. draft and disabled agents still exist, remain
fully editable, and still appear in GET /api/v1/agents.
The events endpoint does not check status
POST /api/v1/agents/{agent_id}/events (see
Sending events) has no status gate. A draft
or disabled agent still accepts events, still evaluates its
Triggers against them, and still queues an outbound call or runs a
silent agent_task on a match. Disabling an agent stops people from
reaching it by phone or widget, but it does not stop an upstream
system's events from continuing to drive it. To stop that traffic
for good, disable or delete the Trigger(s) on that agent, or revoke
the API key sending the events, rather than relying on the agent's
own status.
The distinction between the two inactive states is purely informational:
draft signals "not finished being configured yet," and disabled
signals "was active, then turned off." Nothing in the API behaves
differently based on which of the two an inactive agent is in.
List agents¶
Returns every agent in the deployment, ordered by id, regardless of
status.
Create an agent¶
Capped by your licence
This deployment's licence sets an agents limit. Creating one more
past that cap returns 402 rather than succeeding; see
Licence limits.
curl -X POST https://api.your-domain.com/api/v1/agents \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{
"slug": "support-bot",
"name": "Support Bot",
"voice_name": "Kore",
"base_system_prompt": "You are a friendly support agent for Acme Corp.",
"greeting_instruction": "Greet the caller warmly and ask how you can help."
}'
import httpx
response = httpx.post(
"https://api.your-domain.com/api/v1/agents",
headers={"Authorization": f"Bearer {admin_token}"},
json={
"slug": "support-bot",
"name": "Support Bot",
"voice_name": "Kore",
"base_system_prompt": "You are a friendly support agent for Acme Corp.",
"greeting_instruction": "Greet the caller warmly and ask how you can help.",
},
)
response.raise_for_status()
agent = response.json()
const response = await fetch("https://api.your-domain.com/api/v1/agents", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${adminToken}`,
},
body: JSON.stringify({
slug: "support-bot",
name: "Support Bot",
voice_name: "Kore",
base_system_prompt: "You are a friendly support agent for Acme Corp.",
greeting_instruction: "Greet the caller warmly and ask how you can help.",
}),
});
const agent = await response.json();
Returns 201 Created with the full agent:
{
"id": 1,
"slug": "support-bot",
"name": "Support Bot",
"description": null,
"status": "active",
"voice_name": "Kore",
"model": null,
"temperature": 0.8,
"greeting_instruction": "Greet the caller warmly and ask how you can help.",
"base_system_prompt": "You are a friendly support agent for Acme Corp.",
"twilio_phone_number": null,
"max_concurrent_inbound_calls": 1,
"allowed_origins": null,
"max_concurrent_outbound_calls": 3,
"cross_session_lookback_enabled": false
}
A slug already in use by another agent, active or not, returns 409.
Get an agent¶
A nonexistent agent_id returns 404.
Update an agent¶
Every field is optional. Send only what you're changing; everything else
stays as-is. slug cannot be changed through this endpoint (see the field
reference above).
const response = await fetch("https://api.your-domain.com/api/v1/agents/1", {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${adminToken}`,
},
body: JSON.stringify({
cross_session_lookback_enabled: true,
max_concurrent_inbound_calls: 3,
}),
});
const agent = await response.json();
Returns the full updated agent, in the same shape as create.
Disable an agent¶
Returns 204 No Content.
This is a soft-disable, not a deletion
DELETE sets status to disabled. It does not remove the agent
row, and it does not touch any Call, Event, context document, or
tool configuration attached to it. History stays intact and
queryable; the agent simply stops being reachable by phone, widget,
or outbound trigger (see
Status and reachability above). There
is no separate hard-delete endpoint. To bring the agent back,
PATCH its status back to active.
Next¶
- Context & knowledge: upload documents and URLs the agent can draw on.
- Tools: enable built-in capabilities like
send_webhookorsearch_knowledge_base. - Agents & channels: the conceptual
model behind why
agent_idandslugshow up in almost every other endpoint on this site.