Skip to content

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.

https://sales.sahay.io/api/public/v1

Every path in these docs is relative to this base URL. The examples use $BASE_URL and $SAHAY_API_KEY:

Terminal window
export BASE_URL="https://sales.sahay.io/api/public/v1"
export SAHAY_API_KEY="sk_live_..."
  1. Access to the Developer API is enabled per organization by the Sahay team. To request it, email sales@sahay.io.
  2. 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.
  3. 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.

List your agents:

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

Terminal window
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 os
import 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_..., queued

And 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_..., queued

The call starts out queued. Read it with GET /calls/{call_id} or subscribe to the call.completed webhook to get the result.

  • Request and response bodies are JSON. Send Content-Type: application/json on 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 is null on the last page. Pass it back as cursor to get the next page.
  • Every error uses one shape. See Errors.