Voices and agents
A voice agent has a live version and a draft. You edit the draft, then publish it to make it live. Nothing you save to a draft changes live calls until you publish.
The API works on existing agents. It does not create or archive agents. Create an agent in the Sahay dashboard, then manage it here.
The agent object
Section titled “The agent object”{ "id": "agent_8f3a1c", "name": "Front desk", "kind": "custom", "status": "active", "published_version_id": "ver_21d4e0", "published_version_no": 3, "has_unpublished_changes": false, "created_at": "2026-10-20T08:00:00", "updated_at": "2026-11-04T09:00:00"}| Field | Description |
|---|---|
id |
The agent id. Use it as agent_id in other calls. |
name |
The agent’s name. |
kind |
custom for agents you can edit. Agents managed by Sahay have another kind and cannot be edited or called through the API. |
status |
active for an agent in use. An archived agent cannot be edited. |
published_version_id, published_version_no |
The live version. null until the agent is first published. |
has_unpublished_changes |
true when the draft differs from the live version. |
created_at, updated_at |
Timestamps. |
List voices
Section titled “List voices”GET /voices
Lists the voices you can give an agent.
curl "$BASE_URL/voices" \ -H "Authorization: Bearer $SAHAY_API_KEY"Response 200:
{ "voices": [{ "id": "anushka", "label": "Anushka", "gender": "female" }], "murf_voices": [ { "id": "en-IN-aarav", "label": "Aarav", "gender": "male", "languages": ["en-IN", "hi-IN"] } ], "murf_voice_needs_english": true, "murf_language_aliases": {}}Use an id from voices as tts_voice when you save a draft. The murf_voices list is for organizations that Sahay has enabled to choose a Murf voice provider. Its ids are set through settings.murf_voice, never through tts_voice.
Errors: none beyond the common errors.
List agents
Section titled “List agents”GET /agents
curl "$BASE_URL/agents" \ -H "Authorization: Bearer $SAHAY_API_KEY"Response 200:
{ "data": [ { "id": "agent_8f3a1c", "name": "Front desk", "kind": "custom", "status": "active", "published_version_id": "ver_21d4e0", "published_version_no": 3, "has_unpublished_changes": false, "created_at": "2026-10-20T08:00:00", "updated_at": "2026-11-04T09:00:00" } ]}Get an agent
Section titled “Get an agent”GET /agents/{agent_id}
Returns one agent and its live version.
| Parameter | In | Description |
|---|---|---|
agent_id |
path | The agent id. |
curl "$BASE_URL/agents/agent_8f3a1c" \ -H "Authorization: Bearer $SAHAY_API_KEY"Response 200: an agent object, as above.
Errors: 404 agent_not_found.
Get the draft
Section titled “Get the draft”GET /agents/{agent_id}/draft
Reads the draft configuration of an agent.
curl "$BASE_URL/agents/agent_8f3a1c/draft" \ -H "Authorization: Bearer $SAHAY_API_KEY"Response 200:
{ "agent_id": "agent_8f3a1c", "version_id": "ver_9ac402", "version_no": 4, "greeting": "Hello, this is the front desk at Acme. How can I help?", "conduct": "", "instructions": "Answer questions about opening hours and book appointments.", "language": "en-IN", "tts_voice": "anushka", "settings": {}, "kb_ids": [], "updated_at": "2026-11-04T09:10:00"}settings is returned as stored, so it can be an empty object or contain the fields described under Replace the draft.
Errors: 404 agent_not_found, 409 no_draft (the agent has no editable draft).
Replace the draft
Section titled “Replace the draft”PUT /agents/{agent_id}/draft
Replaces the whole draft. Send every field you want to keep, because a draft is replaced, never patched. Nothing goes live until you publish.
| Field | Type | Required | Description |
|---|---|---|---|
language |
string | yes | A language tag such as en-IN or hi-IN. To let the caller choose between several languages, send a comma separated list such as en-IN,hi-IN. The call opens in the first one. At most 10. |
tts_voice |
string | yes | A voice id from GET /voices voices. Case is ignored. |
instructions |
string | no | What the agent should do. Up to 40000 characters. It must not be empty when you publish. |
greeting |
string or null | no | The opening sentence for inbound calls. Up to 600 characters. |
conduct |
string | no | Phone conduct rules added in front of instructions. Up to 4000 characters. Default is empty. |
kb_ids |
array of strings | no | Knowledge base ids the agent may search. Ids that do not exist in your organization are dropped silently. |
settings |
object | no | Call behaviour, described below. |
settings fields, all optional:
| Field | Type | Description |
|---|---|---|
nudges |
array | Lines the agent says when the caller goes quiet. Each item is {"after_secs": 25, "text": "Hello? Are you still there?"}. after_secs is 3 to 120, text is 1 to 200 characters. At most 3 nudges. |
hang_up_after_nudges |
boolean | Hang up when the nudges go unanswered. Default true. |
max_call_seconds |
integer | Longest call length in seconds. At least 60, at most 1800. Default 600. |
voicemail_enabled |
boolean | Leave a message on voicemail. Default false. Needs voicemail_message. |
voicemail_message |
string or null | The voicemail message, up to 400 characters. |
barge_in_enabled |
boolean | Let the caller interrupt the agent. Default true. |
barge_in_min_words |
integer | Words the caller must say to interrupt, 1 to 15. Default 4. |
farewell_text |
string or null | Spoken before hanging up on silence. Up to 300 characters. |
opener_instruction |
string or null | For outbound calls, what the opening sentence must cover. Up to 600 characters. |
outbound_caller_first |
boolean or null | Wait for the person to say hello before the agent speaks. |
tts_pace |
number or null | Speaking speed from 0.5 to 2.0, where 1.0 is normal. |
If you leave a field out of settings, the API uses its default. Because the draft is replaced whole, send the full settings object each time if you rely on any of these.
curl -X PUT "$BASE_URL/agents/agent_8f3a1c/draft" \ -H "Authorization: Bearer $SAHAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"greeting": "Hello, this is the front desk at Acme. How can I help?", "conduct": "", "instructions": "Answer questions about opening hours and book appointments.", "language": "en-IN", "tts_voice": "anushka", "settings": {}, "kb_ids": []}'Response 200: the draft, plus a warnings array. A warning appears when the greeting or instructions contain a {{token}} that will be blank on a call.
{ "agent_id": "agent_8f3a1c", "version_id": "ver_9ac402", "version_no": 4, "greeting": "Hello, this is the front desk at Acme. How can I help?", "conduct": "", "instructions": "Answer questions about opening hours and book appointments.", "language": "en-IN", "tts_voice": "anushka", "settings": {}, "kb_ids": [], "updated_at": "2026-11-04T09:10:00", "warnings": []}Errors:
404 agent_not_found409 archived: the agent is archived.409 managed_by_sahay: the agent is not a custom agent.409 murf_voice_required,409 murf_voice_cannot_speak,409 tts_provider_cannot_speak,409 speech_provider_locked: voice provider rules. Choosing a provider is enabled per organization by Sahay.409 draft_moved: the draft changed under you. Read it again and retry.422 invalid_request: a field failed validation, for example an unknowntts_voiceor a malformedlanguage.
Publish an agent
Section titled “Publish an agent”POST /agents/{agent_id}/publish
Makes the draft the live version. The body is optional.
| Field | Type | Description |
|---|---|---|
change_note |
string | Optional note for this version. Up to 400 characters. |
curl -X POST "$BASE_URL/agents/agent_8f3a1c/publish" \ -H "Authorization: Bearer $SAHAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"change_note": "Updated Saturday hours"}'Response 200: the agent object, with the new version.
{ "id": "agent_8f3a1c", "name": "Front desk", "kind": "custom", "status": "active", "published_version_id": "ver_9ac402", "published_version_no": 4, "has_unpublished_changes": false, "created_at": "2026-10-20T08:00:00", "updated_at": "2026-11-04T09:15:00"}Errors: 404 agent_not_found, 409 managed_by_sahay, 409 empty_instructions (write the instructions before publishing).
Roll back an agent
Section titled “Roll back an agent”POST /agents/{agent_id}/rollback
Goes back to the previous published version. There is no request body.
curl -X POST "$BASE_URL/agents/agent_8f3a1c/rollback" \ -H "Authorization: Bearer $SAHAY_API_KEY"Response 200: the agent object.
Errors: 404 agent_not_found, 409 managed_by_sahay, 409 nothing_to_rollback (there is no earlier published version).