Agent reference
Every field an agent accepts, what it changes, and what it defaults to. An
agent is the configuration a call runs under; nothing here is per-call except where it says
so. Create with POST /v1/agents, change with PATCH — every field is
optional on a patch.
Creating one
/v1/agentscurl -s -X POST https://voice.sphoro.com/v1/agents \
-H "Authorization: Bearer $SPHORO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Clinic reception",
"languages": [
{
"code": "en-IN",
"greeting": "Thanks for calling Acme Clinic. How can I help?",
"system_prompt": "You are the receptionist for Acme Clinic. Book, move and cancel appointments. Never give medical advice; offer to take a message for a doctor instead."
}
],
"temperature": 0.4,
"max_tokens": 400
}'{
"id": "agt_9f2c1a7d4b6e803f",
"name": "Acme Clinic reception",
"mode": "conversation",
"autonomy": "agentic",
"model": "claude-sonnet-5",
"temperature": 0.4,
"max_tokens": 400,
"languages": [
{
"code": "en-IN",
"greeting": "Thanks for calling Acme Clinic. How can I help?",
"system_prompt": "You are the receptionist for Acme Clinic…"
}
],
"created_at": "2026-09-08T09:14:22Z"
}400 naming the field. This is deliberate: silently
dropping temprature means an agent runs at a temperature nobody chose, and the
first sign of it is a call that sounds wrong weeks later.Identity and model
| Field | Type | Description |
|---|---|---|
namerequired | string | What you call it. Max 120
characters, and not required to be unique — which is why your code should hold the
id. |
descriptionoptional | string | For your own bookkeeping. Never spoken, never shown to a caller. |
modeloptional | string | The language model. Defaults to
claude-sonnet-5. A reasoning model cannot answer a telephone — see
latency. |
modelsoptional | object | The model to use on each specific vendor, keyed by
vendor name — {"anthropic": "claude-sonnet-5", "gemini": "gemini-2.0-flash"}.
Read when the chain falls through to a vendor that does not have the model named above.
Replaces the whole map. |
temperatureoptional | number, 0–2 | Defaults to 0.6. Low for
reciting a policy, higher for open conversation. 0 is a legitimate setting
and is not treated as "unset". |
max_tokensoptional | integer, 1–4000 | Defaults to 512. This is
a phone call: a long reply is one the caller talks over, so treat a high value as a bug
rather than a feature. |
voice_engineoptional | "cascaded" | "realtime" | Defaults to
cascaded — a recognizer, a model and a voice in a row, which works on every
deployment. realtime is one model that listens and replies directly in
speech, owning its own turn-taking. Setting it on a deployment with no realtime vendor is
refused rather than saved, because such an agent would answer the call in
silence. |
timezoneoptional | string, IANA | Which clock this agent's calls run against
— "Asia/Kolkata". Read by {{current_date}},
{{current_time}} and calling-hours enforcement. Empty uses the deployment's
own. |
metadataoptional | object | String keys to string values, stored and returned untouched. Yours. |
What it says, and in which language
There is no system_prompt, language, voice_id or
greeting on the agent itself. All four live on an entry in languages,
because an agent that speaks three languages needs three of each.
| Field | Type | Description |
|---|---|---|
languagesrequired | array | Every language the agent speaks, the one calls open in first. Required on any agent a model answers. Replaces the whole list — a save that omits the prompt would take it away, so that save is refused. Entry shape below. |
language_detectionoptional | "off" or "follow" | Defaults to follow
once the agent has more than one language. follow moves the agent into
whichever of its languages the caller speaks — voice, prompt and all; off
keeps every call in the language it opened in. Meaningless with one language. |
speedoptional | number, 0.25–4 | Defaults to 1. A multiplier on
whichever voice is speaking, in every language. |
pitchoptional | number, 0.25–4 | Defaults to 1. |
filler_phraseoptional | string | Spoken while a tool runs, so a slow lookup is not dead air. Does not make anything faster. |
A language entry
{
"code": "hi",
"voice_id": "hindi-voice",
"voices": { "elevenlabs": "…", "cartesia": "…" },
"tts_provider": "cartesia",
"stt_provider": "gladia",
"system_prompt": "आप एक्मे क्लिनिक की रिसेप्शनिस्ट हैं…",
"greeting": "नमस्ते, एक्मे क्लिनिक।",
"silence_prompt": "क्या आप अब भी लाइन पर हैं?",
"final_message": "आपका दिन शुभ हो।",
"keywords": ["एक्मे", "डॉक्टर शर्मा"]
}| Field | Type | Description |
|---|---|---|
coderequired | string | The language. Every code this deployment accepts is listed on language and voice. |
system_promptoptional | string | This language's instructions, max 100,000 characters. Required on the first entry — that one is the agent's prompt. A later entry may leave it empty and derive one from the first. |
greetingoptional | string | The first thing said on a call that opens in this language. Interruptible, like any other turn. |
voice_idoptional | string | The voice, for whichever vendor the chain settles on. Every entry after the first needs one; the first may leave it empty and take the deployment default. |
voicesoptional | object | One voice id per vendor. Voice ids are not portable between vendors, so this is what keeps an agent sounding the same when the chain falls through to its second choice. |
tts_provideroptional | string | Pin the voice vendor for this language. Unset lets the chain decide. See vendors. |
stt_provideroptional | string | Pin the transcription vendor for this language — worth doing where one vendor is markedly better at the accent. |
silence_promptoptional | string | What "are you still there?" says in this language. |
final_messageoptional | string | The goodbye, in this language. |
keywordsoptional | string[] | Words to bias transcription towards — product names, place names, the doctor's surname. The cheapest fix for a name that keeps coming back misheard. |
What it can do
| Field | Type | Description |
|---|---|---|
modeoptional | "conversation" | "keypad" | "normal" | "webrtc" | Defaults to
conversation. Only the first uses speech recognition or a model. See
kinds of agent. |
autonomyoptional | "agentic" | "simple" | "custom" | Defaults to
agentic. Who holds this end of the call. Read only by a
conversation agent. |
capabilitiesoptional | object | The switches behind custom:
search_knowledge, call_functions, transfer,
end_call, max_tool_rounds (1–10, 0 for the default
of 3) and retrieval_timeout_ms (50–2000, 0 for the default).
Stored on every agent, read only under custom. Replaces the whole block. |
toolsoptional | string[] | Function names, each of which must be registered.
GET /v1/functions lists them. See
tools and functions. |
tool_configsoptional | object[] | The tools this agent carries itself, as
opposed to tools above, which names functions the deployment registered.
Each entry has a kind —
calendar_availability, book_appointment,
transfer_call or custom_function — a name the model
calls, a description it decides when to call from, an optional
pre_tool_message keyed by language code, and one configuration block matching
the kind. Replaces the whole list.
A credential inside one is written as {"value": "…"} and comes back as
{"hint": "••••1234"}; leaving it out on an edit keeps what is stored, which is
how a description is changed without resending an API key. See
tools and functions. |
knowledge_base_idsoptional | string[] | Every base this agent searches,
searched together on each turn. Attaching at least one is what gives the agent the
search_knowledge_base tool — there is no separate switch, and naming that tool
in tools does nothing. Send [] to detach every base. |
knowledge_base_idoptional | string | The older single-base field. Still accepted and returned, folded into the list above. |
flowoptional | object | A conversation flow: start,
nodes[], and edges[] on each node. Validated in full, with every
problem reported at once. Send {} to take the flow away. See
flows. |
keypadoptional | object | Digits to what the agent says —
{"1": {"say": "…", "end_call": true}}, or "transfer": true to put
the caller through. Answered without the model, in either mode. See keypad and IVR. |
How it listens, and when it gives up
| Field | Type | Description |
|---|---|---|
conversationoptional | object | Barge-in, silence and duration. Every value
of 0 means "the default". Replaces the whole block. Full breakdown on
conversation behaviour. |
no_matchoptional | string | Spoken by a keypad agent when the caller presses something with no entry. Three in a row ends the call. |
no_inputoptional | string | Spoken by a keypad agent when nothing is pressed. |
max_no_inputoptional | integer | How many times a keypad agent re-reads its menu to a silent caller before giving up. |
keypad_timeout_secondsoptional | integer | How long a keypad agent waits for a press before treating it as silence. |
ring_secondsoptional | integer, 1–120 | 0 means the default of
30. How long a call rings before nobody has answered — read on the two shapes that ring
rather than answer, a webrtc agent and a simple one taking an
inbound call. Ignored elsewhere: nothing rings when the agent itself picks up. |
Telephone, recording and afterwards
| Field | Type | Description |
|---|---|---|
carrieroptional | string | Which carrier this agent dials through. Unset uses the deployment's default. See numbers and carriers. |
transfer_numberoptional | string, E.164 | Setting it gives the agent the
transfer_call tool — and this is the only number that tool
can ever dial. The model chooses when to hand off, never where. On a normal
agent it is where every caller goes, with nothing deciding. |
record_callsoptional | boolean | Defaults to false. Needs
recording storage configured on the deployment as well. See
recordings. |
transcribe_callsoptional | boolean | Transcribe a webrtc agent's
calls — the two people on them. Ignored by every other mode, which is transcribed anyway
because the recognizer is how the agent hears the caller at all. |
post_calloptional | object | Defaults to {"summary": true}. What
is worked out from the transcript once the call ends, including typed extractions. Replaces
the whole block. See post-call analysis. |
webhook_urloptional | string | Overrides your account's endpoints for this agent's events. HTTPS only. See webhooks. |
Grouping and cost
| Field | Type | Description |
|---|---|---|
project_idoptional | string | The cost centre this agent's calls are attributed to. See projects. |
group_idoptional | string | The agent group this agent belongs to. Its
defaults fill in every inheritable field this agent has not claimed in
overrides. See agent groups. |
overridesoptional | string[] | The inheritable fields whose value on this agent wins over its group's. Set for you when the agent joins a group. |
templateoptional | string | Create from a template instead of from nothing. Accepted on create only. See agent templates. |
persona_idoptional | string | The persona this agent deploys. The persona owns the prompt, voice, knowledge and flow; the agent owns the telephone. See personas. |
Replacing versus merging
Scalars merge; lists and blocks replace. That is one rule with one consequence worth stating plainly, because it is the difference between adding a language and losing two.
| Sending | Does |
|---|---|
{"temperature": 0.2} | Changes the temperature. Nothing else moves. |
{"languages": [ … one entry … ]} | Makes that the only language. Read the current list, append, send the whole thing. |
The blocks that replace whole: languages, keypad,
models, capabilities, conversation,
post_call, flow, tools,
knowledge_base_ids and overrides.
# Add a language without losing the ones already there.
CURRENT=$(curl -s https://voice.sphoro.com/v1/agents/$AGENT_ID \
-H "Authorization: Bearer $SPHORO_API_KEY")
echo "$CURRENT" | jq '{languages: (.languages + [{code:"ta", voice_id:"tamil-voice", greeting:"வணக்கம்."}])}' \
| curl -s -X PATCH https://voice.sphoro.com/v1/agents/$AGENT_ID \
-H "Authorization: Bearer $SPHORO_API_KEY" \
-H "Content-Type: application/json" -d @-Listing, duplicating and deleting
/v1/agents/v1/agents/{id}/duplicate/v1/agents/{id}/v1/agents/{id}/restoreDeleting is soft: the agent stops being listed and stops taking calls, and its finished calls
keep their transcripts and analysis. restore brings it back with the same id, which
is what you want after somebody deletes the production agent on a Friday. Duplicating is the
right way to try a prompt change against real traffic without editing the live one.
call_variables is for, and one agent means one prompt
to fix when it is wrong.Trying it before a caller does
Two things worth doing before an agent takes real traffic, both of which cost nothing:
- Place a browser call to it. No number needed — see talk from a browser.
- Run the post-call pass on a transcript you paste in, with
POST /v1/agents/{id}/post_call/test, so you find out that an extraction never fires without placing thirty calls to discover it. See post-call analysis.
Sphoro Voice also ships a set of working agents to start from rather than an empty form — see agent templates.