Agents
Public API endpoints for configuring realtime voice agents.
Agents define prompt behavior, tool execution, voice settings, and phone number routing.
All endpoints on this page use API key authentication:
Authorization: Bearer $ORBITALI_API_KEY
Public agent endpoints
| Method | Path | Description |
|---|---|---|
GET | /public/v1/agents | List agents |
POST | /public/v1/agents | Create an agent |
PUT | /public/v1/agents/{agent_id} | Replace an agent |
PATCH | /public/v1/agents/{agent_id} | Partially update an agent |
GET | /public/v1/agents/{agent_id}/tools | List custom tools |
POST | /public/v1/agents/{agent_id}/tools | Create a custom tool |
POST | /public/v1/agents/{agent_id}/realtime-sessions | Create a short-lived WebSocket voice session |
Active-agent limits
Agent limits apply to active agents only. Draft and inactive agents can be saved without consuming the active-agent allowance, but only active agents can receive calls or start realtime sessions.
The trial allowance is 1 active agent. Paid plan allowances are controlled by the account's current plan; current defaults are Launch: 2 active agents, Studio: 10 active agents, and Agency: unlimited active agents. If an activation would exceed the current allowance, PUT and PATCH return 409 with code: "AGENT_LIMIT_EXCEEDED".
Create an agent
Creates a draft static, webhook, or http agent and its prompt configuration.
The create endpoint always creates a draft agent. To make an agent available for calls or realtime sessions, update it later with status: "active" using PUT /public/v1/agents/{agent_id} or PATCH /public/v1/agents/{agent_id}.
curl https://api.orbitali.ai/public/v1/agents \
-X POST \
-H "Authorization: Bearer $ORBITALI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Receptionist",
"agentType": "static",
"language": "en-US",
"voiceName": "Aoede",
"phoneNumberAssignments": [
{
"phoneNumberId": "40000000-0000-4000-8000-000000000001",
"handoffPhoneNumber": "+15551234567"
}
],
"serverUrl": null,
"serverHeaders": {},
"serverSecret": null,
"handoffPhoneNumber": null,
"promptType": "static",
"identity": "You are the front desk receptionist for Acme Clinic.",
"instructions": "Answer caller questions clearly and offer to take a message.",
"greetingType": "static",
"staticGreeting": "Thanks for calling Acme Clinic. How can I help?"
}'
Response:
{
"id": "9dbf0d0b-2b55-45de-8c41-7a2d9b0ce8e9"
}
Agent request fields
| Field | Type | Description |
|---|---|---|
name | string | Display name for the agent |
agentType | static | webhook | http | Agent integration mode. Defaults to webhook when omitted |
language | string | Locale used by the realtime session, for example en-US |
voiceName | string | Realtime voice name used for generated speech |
phoneNumberAssignments | object[] | Claimed phone number assignments. Each item includes phoneNumberId and optional handoffPhoneNumber used when that inbound number transfers to a human |
phoneNumberIds | string[] | Legacy-compatible claimed phone number IDs assigned to the agent. New integrations should use phoneNumberAssignments |
serverUrl | string | null | Webhook agent endpoint. Required for dynamic prompts and webhook tools. Use null for static and HTTP agents |
serverHeaders | object | Extra string headers sent to the Server URL |
serverSecret | string | null | Secret used to sign Server URL webhook requests. When present, Orbitali sends x-orbitali-signature as sha256= plus the lowercase hex HMAC-SHA256 digest of the raw JSON body |
handoffPhoneNumber | string | null | Legacy fallback phone number used by transfer_call when no per-number handoff applies |
promptType | static | dynamic | Whether instructions are stored directly or fetched from the Server URL. Dynamic prompts are only supported by webhook agents |
identity | string | Who the agent is and what role it should play |
instructions | string | Static prompt instructions. Required when promptType is static |
greetingType | static | dynamic | none | How the first assistant message is selected |
staticGreeting | string | null | Required when greetingType is static |
Full PUT and partial PATCH updates also accept status, which can be draft, active, or inactive. Activating an agent counts against the account's active-agent allowance.
Agent types:
static: saved identity, instructions, and greeting only. Static agents do not support custom tools.webhook: saved or dynamic prompt config. Dynamic prompts useserverUrlandagent:assistant-request. Custom tools useserverUrlandagent:tool-callevents.http: saved prompt config. Custom tools call their owntoolUrlwithtoolMethodandtoolHeaders.
Dynamic prompts require serverUrl and agentType: "webhook". Dynamic greetings require promptType to be dynamic.
Verify signed Server URL requests with the raw request body. Re-serialized or parsed JSON can produce a different HMAC.
Partially update an agent
Updates only the supplied fields and keeps the current phone assignments, prompt fields, greeting fields, and server config unless they are included in the request.
curl https://api.orbitali.ai/public/v1/agents/9dbf0d0b-2b55-45de-8c41-7a2d9b0ce8e9 \
-X PATCH \
-H "Authorization: Bearer $ORBITALI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"expectedUpdatedAt": "2026-05-14T12:00:00.000Z",
"serverUrl": "https://crm.orbitali.ai/api/orbitali/agents"
}'
The Server URL signing secret can also be updated without resending the full agent:
{
"expectedUpdatedAt": "2026-05-14T12:00:00.000Z",
"serverSecret": "..."
}
Response:
{
"id": "9dbf0d0b-2b55-45de-8c41-7a2d9b0ce8e9"
}
PATCH accepts the same fields as a full update, but every field except expectedUpdatedAt is optional. Use the agent's current updatedAt value from GET /public/v1/agents as expectedUpdatedAt. If the agent changed after that timestamp, PATCH returns 409 so the client can refetch and retry. The merged result must still be valid. For example, promptType: "dynamic" still requires an existing or supplied serverUrl, and greetingType: "dynamic" still requires dynamic prompt mode.
If the update changes a draft agent to status: "active" and the account is already at its active-agent limit, the API returns 409:
{
"error": "Active agent limit exceeded",
"code": "AGENT_LIMIT_EXCEEDED",
"maxAgents": 2
}
Deactivate another active agent or upgrade the account plan before retrying. maxAgents is the active-agent cap for the current plan or trial.
List agents
curl https://api.orbitali.ai/public/v1/agents \
-H "Authorization: Bearer $ORBITALI_API_KEY"
Response:
[
{
"id": "9dbf0d0b-2b55-45de-8c41-7a2d9b0ce8e9",
"name": "Receptionist",
"status": "draft",
"agentType": "webhook",
"language": "en-US",
"voiceName": "Aoede",
"serverUrl": "https://example.com/orbitali/webhook",
"serverHeaders": {},
"serverSecret": null,
"handoffPhoneNumber": null,
"phoneNumber": null,
"toolCount": 1,
"knowledgeDocumentCount": 0,
"callsToday": 0,
"successRate": 0,
"updatedAt": "2026-05-14T12:00:00.000Z"
}
]
Create a tool
Creates a custom tool for an existing webhook or HTTP agent. Static agents do not support custom tools.
For webhook agents, the agent must have a saved serverUrl. Tool executions are delivered to that Server URL as agent:tool-call events.
For HTTP agents, each tool must include toolUrl. Tool executions call that URL with toolMethod and toolHeaders.
curl https://api.orbitali.ai/public/v1/agents/9dbf0d0b-2b55-45de-8c41-7a2d9b0ce8e9/tools \
-X POST \
-H "Authorization: Bearer $ORBITALI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "lookup_customer",
"description": "Find a customer record by phone number.",
"parameterSchema": {
"type": "object",
"properties": {
"phone": { "type": "string" }
},
"required": ["phone"]
},
"responseSchema": null,
"timeoutMs": 5000,
"onError": "return_error",
"enabled": true,
"toolUrl": null,
"toolMethod": "POST",
"toolHeaders": {}
}'
Response:
{
"id": "df0ee766-7903-4c13-8547-f6bd65de392a"
}
Tool request fields
| Field | Type | Description |
|---|---|---|
name | string | Tool name exposed to the model |
description | string | What the tool does and when to call it |
parameterSchema | object | JSON Schema object for tool arguments |
responseSchema | object | null | Optional JSON Schema object for the tool response |
timeoutMs | number | Timeout from 1000 to 30000 milliseconds |
onError | return_error | return_empty | Behavior when the Server URL returns an error |
enabled | boolean | Whether the tool is available during calls |
toolUrl | string | null | Required for HTTP agents. Use null for webhook tools |
toolMethod | GET | POST | PUT | DELETE | PATCH | HEAD | OPTIONS | HTTP method used with toolUrl; defaults to POST |
toolHeaders | object | String headers sent to toolUrl; defaults to {} |
Webhook tool executions are delivered to the agent's Server URL as agent:tool-call events. HTTP tool executions call the tool's own toolUrl.
List tools
curl https://api.orbitali.ai/public/v1/agents/9dbf0d0b-2b55-45de-8c41-7a2d9b0ce8e9/tools \
-H "Authorization: Bearer $ORBITALI_API_KEY"
Response:
[
{
"id": "df0ee766-7903-4c13-8547-f6bd65de392a",
"name": "lookup_customer",
"description": "Find a customer record by phone number.",
"parameterSchema": {},
"responseSchema": null,
"timeoutMs": 5000,
"onError": "return_error",
"enabled": true,
"toolUrl": null,
"toolMethod": "POST",
"toolHeaders": {}
}
]
WebSocket voice sessions
Use WebSocket sessions when a website needs to talk to an active agent directly instead of calling through a phone number. Create the session from your backend with an Orbitali API key, then pass the returned websocketUrl to browser code. Do not expose long-lived API keys in the browser.
curl https://api.orbitali.ai/public/v1/agents/9dbf0d0b-2b55-45de-8c41-7a2d9b0ce8e9/realtime-sessions \
-X POST \
-H "Authorization: Bearer $ORBITALI_API_KEY"
Response:
{
"token": "eyJ...signature",
"expiresAt": "2026-05-15T12:00:00.000Z",
"websocketUrl": "wss://agent.orbitali.ai/ws/agents/9dbf0d0b-2b55-45de-8c41-7a2d9b0ce8e9?token=eyJ...signature",
"protocol": {
"inputAudio": { "encoding": "pcm16", "sampleRate": 16000 },
"outputAudio": { "encoding": "pcm16", "sampleRate": 24000 }
}
}
The WebSocket accepts JSON messages:
{ "type": "input_audio", "audio": "<base64 pcm16 16kHz mono audio>" }
Close the conversation with:
{ "type": "stop" }
The server sends:
{ "type": "session.started", "callId": "..." }
{ "type": "output_audio", "audio": "<base64 pcm16 24kHz mono audio>", "encoding": "pcm16", "sampleRate": 24000 }
{ "type": "transcript", "role": "assistant", "text": "..." }
{ "type": "clear" }
Session tokens expire after 60 seconds. Once connected, the WebSocket remains active until the conversation ends or the socket closes.
Errors
| Status | Code | Meaning |
|---|---|---|
400 | - | Invalid request body, invalid agent ID, unsupported static-agent tool, missing webhook serverUrl, or missing HTTP toolUrl |
401 | - | Invalid or missing API key |
404 | - | Agent not found |
409 | AGENT_LIMIT_EXCEEDED | Updating an agent to active would exceed the account's active-agent allowance. The response includes maxAgents |
409 | - | Agent update conflict, or agent must be active before creating a realtime session |
500 | - | Realtime WebSocket URL or token signing is not configured |