Introduction
The Sahay Developer API lets your own systems create and run AI voice agents on Sahay. You can:
- List the voices and agents in your organization, edit an agent’s draft, publish it, or roll it back.
- Place a single AI call to a phone number and read back the transcript, summary, outcome and recording.
- Create calling campaigns, add contacts, start, pause, resume and stop them, and read the results.
- Receive webhooks when a call ends, when a campaign contact reaches a result, and when a campaign finishes.
All requests are JSON over HTTPS.
Base URL
Section titled “Base URL”https://sales.sahay.io/api/public/v1Every path in these docs is relative to this base URL. The examples use $BASE_URL and $SAHAY_API_KEY:
export BASE_URL="https://sales.sahay.io/api/public/v1"export SAHAY_API_KEY="sk_live_..."Getting access
Section titled “Getting access”- Access to the Developer API is enabled per organization by the Sahay team. To request it, email sales@sahay.io.
- Once it is enabled, an organization admin creates an API key in the Sahay dashboard under Developers. The full key is shown once, when you create it. Copy it then.
- Your plan must include AI voice calling. Without it, the API answers
404 voice_not_entitled.
Keys are tied to your organization. All keys of one organization share the same rate limit.
Quickstart
Section titled “Quickstart”List your agents:
curl "$BASE_URL/agents" \ -H "Authorization: Bearer $SAHAY_API_KEY"Place a call with a published agent. The Idempotency-Key header makes a retry safe: repeating the same key returns the original call instead of dialing again.
curl -X POST "$BASE_URL/calls" \ -H "Authorization: Bearer $SAHAY_API_KEY" \ -H "Idempotency-Key: order-A-1042" \ -H "Content-Type: application/json" \ -d '{"agent_id": "agent_8f3a1c", "to_number": "+919876543210", "metadata": {"order_id": "A-1042"}}'The same call in Python:
import osimport requests
BASE_URL = "https://sales.sahay.io/api/public/v1"headers = { "Authorization": f"Bearer {os.environ['SAHAY_API_KEY']}", "Idempotency-Key": "order-A-1042",}
resp = requests.post( f"{BASE_URL}/calls", headers=headers, json={ "agent_id": "agent_8f3a1c", "to_number": "+919876543210", "metadata": {"order_id": "A-1042"}, }, timeout=30,)resp.raise_for_status()call = resp.json()print(call["id"], call["status"]) # call_..., queuedAnd in Node.js (version 18 or newer):
const BASE_URL = "https://sales.sahay.io/api/public/v1";
const resp = await fetch(`${BASE_URL}/calls`, { method: "POST", headers: { Authorization: `Bearer ${process.env.SAHAY_API_KEY}`, "Idempotency-Key": "order-A-1042", "Content-Type": "application/json", }, body: JSON.stringify({ agent_id: "agent_8f3a1c", to_number: "+919876543210", metadata: { order_id: "A-1042" }, }),});if (!resp.ok) throw new Error(JSON.stringify(await resp.json()));const call = await resp.json();console.log(call.id, call.status); // call_..., queuedThe call starts out queued. Read it with GET /calls/{call_id} or subscribe to the call.completed webhook to get the result.
Conventions
Section titled “Conventions”- Request and response bodies are JSON. Send
Content-Type: application/jsonon requests with a body. - Phone numbers are in E.164 format: a plus sign, the country code and the number, for example
+919876543210. - Timestamps are ISO 8601 strings.
- Lists are returned newest first. Paged lists return a
next_cursor, which isnullon the last page. Pass it back ascursorto get the next page. - Every error uses one shape. See Errors.