Agents API
Create, manage, and call AI voice agents via the REST API.
Voice AI agents are persistent, reusable configurations for AI-powered phone calls. Instead of passing a systemPrompt on every call, create an agent once and place calls through it.
Agents are backed by Ultravox. Each agent maps to an Ultravox agent resource with its own prompt, voice, tools, and knowledge bases.
Create agent
POST /api/voice/agents
Create a new AI voice agent with a system prompt, optional voice, tools, and knowledge bases.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Agent name |
prompt | string | Yes | System prompt that defines agent behavior |
voice | string | No | Voice ID for TTS (e.g. "Mark") |
first_message | string | No | Initial message spoken when the call starts |
language | string | No | Language code (default: "en") |
tool_ids | array[string] | No | Tool IDs to attach to the agent |
knowledge_ids | array[string] | No | Knowledge base IDs for RAG |
max_duration | number | No | Max call duration in seconds (default: 300) |
curl -X POST https://api.trunx.io/api/voice/agents \
-H "Authorization: Bearer tk_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Appointment Confirmer",
"prompt": "You are a friendly dental office assistant confirming appointments. Verify the patient name, date, and time. If they need to reschedule, collect their preferred date.",
"voice": "Mark",
"first_message": "Hi, this is Sarah from Downtown Dental. I am calling to confirm your upcoming appointment.",
"language": "en",
"max_duration": 300
}'const res = await fetch("https://api.trunx.io/api/voice/agents", {
method: "POST",
headers: {
"Authorization": "Bearer tk_live_...",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Appointment Confirmer",
prompt:
"You are a friendly dental office assistant confirming appointments. Verify the patient name, date, and time. If they need to reschedule, collect their preferred date.",
voice: "Mark",
first_message:
"Hi, this is Sarah from Downtown Dental. I am calling to confirm your upcoming appointment.",
language: "en",
max_duration: 300,
}),
});
const data = await res.json();import requests
res = requests.post(
"https://api.trunx.io/api/voice/agents",
headers={"Authorization": "Bearer tk_live_..."},
json={
"name": "Appointment Confirmer",
"prompt": "You are a friendly dental office assistant confirming appointments. Verify the patient name, date, and time. If they need to reschedule, collect their preferred date.",
"voice": "Mark",
"first_message": "Hi, this is Sarah from Downtown Dental. I am calling to confirm your upcoming appointment.",
"language": "en",
"max_duration": 300,
},
)
data = res.json()Response
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Appointment Confirmer",
"ultravoxId": "uv_agent_xxxxxxxxxxxxxxxx"
}Get agent
GET /api/voice/agents/{id}
Retrieve agent details including the live Ultravox status.
Response
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Appointment Confirmer",
"ultravoxId": "uv_agent_xxxxxxxxxxxxxxxx",
"prompt": "You are a friendly dental office assistant...",
"voice": "Mark",
"first_message": "Hi, this is Sarah from Downtown Dental...",
"language": "en",
"max_duration": 300,
"tool_ids": [],
"knowledge_ids": [],
"createdAt": "2026-03-09T15:30:00.000Z"
}Returns 404 if the agent ID does not exist.
List agents
GET /api/voice/agents
Retrieve all agents for your account.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | number | No | Max agents to return (default: 50) |
Response
{
"agents": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Appointment Confirmer",
"ultravoxId": "uv_agent_xxxxxxxxxxxxxxxx",
"createdAt": "2026-03-09T15:30:00.000Z"
}
],
"count": 1
}Update agent
PATCH /api/voice/agents/{id}
Update an existing agent. All fields are optional — only provided fields are changed.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Agent name |
prompt | string | No | System prompt |
voice | string | No | Voice ID |
first_message | string | No | Initial message when call starts |
language | string | No | Language code |
tool_ids | array[string] | No | Tool IDs to attach |
knowledge_ids | array[string] | No | Knowledge base IDs for RAG |
max_duration | number | No | Max call duration in seconds |
Response
Returns the updated agent object. Returns 404 if the agent ID does not exist.
Delete agent
DELETE /api/voice/agents/{id}
Delete an agent. This also removes the corresponding Ultravox agent resource.
Response
{
"deleted": true,
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}Returns 404 if the agent ID does not exist.
Create agent call
POST /api/voice/agents/{id}/call
Place an outbound call using this agent's configuration. The agent's prompt, voice, tools, and knowledge bases are used automatically.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
to | string | Yes | Destination phone number in E.164 format |
from | string | No | Caller ID in E.164 format. Auto-selected if omitted. |
curl -X POST https://api.trunx.io/api/voice/agents/a1b2c3d4-e5f6-7890-abcd-ef1234567890/call \
-H "Authorization: Bearer tk_live_..." \
-H "Content-Type: application/json" \
-d '{
"to": "+14155551234"
}'const agentId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
const res = await fetch(
`https://api.trunx.io/api/voice/agents/${agentId}/call`,
{
method: "POST",
headers: {
"Authorization": "Bearer tk_live_...",
"Content-Type": "application/json",
},
body: JSON.stringify({
to: "+14155551234",
}),
}
);
const data = await res.json();import requests
agent_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
res = requests.post(
f"https://api.trunx.io/api/voice/agents/{agent_id}/call",
headers={"Authorization": "Bearer tk_live_..."},
json={
"to": "+14155551234",
},
)
data = res.json()Response
{
"id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"callId": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"status": "initiated",
"agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}Returns 404 if the agent ID does not exist.
When from is omitted, the system automatically selects the best available DID based on health score and local presence to the destination number.