Quickstart
From zero to a placed call in 5 minutes. Auth, create an agent, place a call, fetch the transcript.
This is the shortest possible path from zero to a real voice call. We'll:
- Get an API key
- Create an agent
- Place an outbound call to your own phone
- Fetch the transcript when it ends
You need: a phone, a Twilio account with one phone number and the AccountSid + AuthToken handy.
Get an API key
Dashboard → API Keys → Create API Key. The full zsk_... value is never displayed — use the copy button, now or anytime later from the API Keys page.
X-API-Key: zsk_...Create an agent
The minimum useful agent: a system prompt, the LLM, voice, and STT.
AGENT_UUID=$(curl -s -X POST https://dashboard.zoxa.ai/api/v1/agents \
-H "X-API-Key: zsk_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Hello-world bot",
"systemPrompt": "You are a friendly voice agent. Ask the caller how their day is going. Keep replies short.",
"greeting": { "firstMessages": ["Hi! How is your day going?"] },
"llm": { "provider": "openai", "model": "gpt-5.4-mini" },
"tts": { "provider": "elevenlabs", "voice": "7qBNUtXRGP0jPi0H4r8k" },
"stt": { "provider": "soniox" }
}' | jq -r .uuid)
echo "Agent: $AGENT_UUID"const res = await fetch("https://dashboard.zoxa.ai/api/v1/agents", {
method: "POST",
headers: { "X-API-Key": "zsk_...", "Content-Type": "application/json" },
body: JSON.stringify({
name: "Hello-world bot",
systemPrompt: "You are a friendly voice agent. Ask the caller how their day is going. Keep replies short.",
greeting: { firstMessages: ["Hi! How is your day going?"] },
llm: { provider: "openai", model: "gpt-5.4-mini" },
tts: { provider: "elevenlabs", voice: "7qBNUtXRGP0jPi0H4r8k" },
stt: { provider: "soniox" },
}),
});
const agent = await res.json();
console.log("Agent:", agent.uuid);import httpx
agent = httpx.post(
"https://dashboard.zoxa.ai/api/v1/agents",
headers={"X-API-Key": "zsk_..."},
json={
"name": "Hello-world bot",
"systemPrompt": "You are a friendly voice agent. Ask the caller how their day is going. Keep replies short.",
"greeting": { "firstMessages": ["Hi! How is your day going?"] },
"llm": {"provider": "openai", "model": "gpt-5.4-mini"},
"tts": {"provider": "elevenlabs", "voice": "7qBNUtXRGP0jPi0H4r8k"},
"stt": {"provider": "soniox"},
},
).json()
print("Agent:", agent["uuid"])The response includes uuid (you'll pass this as agentId next) and id (used by GET/PUT/DELETE /agents/{id}).
Place a call to your phone
API-first outbound — Twilio creds inline, the agent we just created, your phone number as the dialed target.
CALL_ID=$(curl -s -X POST https://dashboard.zoxa.ai/api/v1/call \
-H "X-API-Key: zsk_..." \
-H "Content-Type: application/json" \
-d "{
\"type\": \"outbound\",
\"callConfig\": {
\"provider\": \"twilio\",
\"phoneNumber\": \"+1...your twilio number\",
\"auth\": { \"accountSid\": \"AC...\", \"authToken\": \"...\" }
},
\"toNumber\": \"+1...your personal phone\",
\"agentId\": \"$AGENT_UUID\"
}" | jq -r .callId)
echo "Call: $CALL_ID"const call = await fetch("https://dashboard.zoxa.ai/api/v1/call", {
method: "POST",
headers: { "X-API-Key": "zsk_...", "Content-Type": "application/json" },
body: JSON.stringify({
type: "outbound",
callConfig: {
provider: "twilio",
phoneNumber: "+1...your twilio number",
auth: { accountSid: "AC...", authToken: "..." },
},
toNumber: "+1...your personal phone",
agentId: agent.uuid,
}),
}).then((r) => r.json());
console.log("Call:", call.callId);call = httpx.post(
"https://dashboard.zoxa.ai/api/v1/call",
headers={"X-API-Key": "zsk_..."},
json={
"type": "outbound",
"callConfig": {
"provider": "twilio",
"phoneNumber": "+1...your twilio number",
"auth": {"accountSid": "AC...", "authToken": "..."},
},
"toNumber": "+1...your personal phone",
"agentId": agent["uuid"],
},
).json()
print("Call:", call["callId"])Your phone should ring within a few seconds. The agent will speak first.
Fetch the transcript after you hang up
Either poll, or set agent.webhook and react when the call.ended event arrives.
curl https://dashboard.zoxa.ai/api/v1/calls/$CALL_ID \
-H "X-API-Key: zsk_..." | jq '{
status,
connectionStatus,
endedReason,
durationSeconds,
cost,
transcript: .transcript.url,
recording: .recordings[0].url
}'Once status is "completed", transcript.url and recordings[].url are signed URLs (valid ~1 hour) you can download.
What you just built
A complete outbound voice agent pipeline:
- One agent stored in your org — reusable across thousands of calls
- One API call to dial — credentials are passed inline, never stored after the call ends
- Full call history — transcript, recording, cost, latency, per-turn breakdown, all at
GET /calls/{callId}
Next steps
| Goal | Read |
|---|---|
| Add tools (HTTP calls, end call, transfer, KB) | Tool types |
| Receive inbound calls instead of dialing out | Inbound flow |
| Run the agent in a browser (no phone) | Browser flow |
| Bulk-dial a list of contacts | Campaigns overview |
| Get webhook events instead of polling | Webhook events |
| Decide between WebSocket / WebRTC / phone | Choosing a transport |