Skip to content

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.

{
"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.

GET /voices

Lists the voices you can give an agent.

Terminal window
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.

GET /agents

Terminal window
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 /agents/{agent_id}

Returns one agent and its live version.

Parameter In Description
agent_id path The agent id.
Terminal window
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 /agents/{agent_id}/draft

Reads the draft configuration of an agent.

Terminal window
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).

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.

Terminal window
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_found
  • 409 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 unknown tts_voice or a malformed language.

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.
Terminal window
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).

POST /agents/{agent_id}/rollback

Goes back to the previous published version. There is no request body.

Terminal window
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).