zoxaAI
Homepage
API ReferenceAgents

Create an agent

POST /api/v1/agents — create a saved agent from a full AgentConfig body.

POST /api/v1/agents

Create a saved agent. The request body is an Agent Config — there is no wrapper object. The returned uuid is the value you pass as agentId to POST /call.

Authentication

X-API-Key: zsk_...

Request body

The full field reference lives on the Agent Config schema page. The short version:

You must sendEverything else
name (1–120 chars), llm: { provider, model }, stt: { provider }, and tts: { provider }Optional — defaults are filled in: English, per-provider default models and voices, an auto-seeded end_call tool, and every knob at its platform default.

One extra field exists only on this endpoint and PATCH, as a sibling of the config fields:

FieldTypeNotes
webhookSecretstringWrite-only. Popped off before validation and stored as the HMAC key for outbound webhook signatures. Never returned by any endpoint.

Strict validation

Unknown or misspelled keys anywhere in the body are rejected (400) with the exact field path. If a request fails, the details array tells you precisely which field and why.

Response

201 Created with an agent envelope — server-managed metadata wrapping the full validated config. The config you get back is the resolved form: every default materialized, message lists cleaned, and the end_call tool present even if you didn't send one.

{
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "status": "active",
  "createdAt": "2026-08-06T10:30:00Z",
  "updatedAt": "2026-08-06T10:30:00Z",
  "config": {
    "name": "Sales bot",
    "description": "Qualifies B2B SaaS leads.",
    "languages": ["en"],
    "systemPrompt": "You qualify inbound leads. Ask about company size and budget. Be concise.",
    "timezone": "Asia/Kolkata",
    "greeting": {
      "firstMessages": ["Hi! Are you looking to evaluate our product?"],
      "interruptible": false,
      "speakFirst": "agent",
      "agentDelayS": 0.0,
      "userTimeoutS": 3.0
    },
    "llm": { "provider": "openai", "model": "gpt-5.4-mini", "temperature": 1.0, "maxTokens": 251, "prewarm": true },
    "stt": { "provider": "soniox", "model": null, "interruptionMinWords": 0, "...": "per-provider blocks" },
    "tts": { "provider": "elevenlabs", "model": null, "voice": null, "...": "per-provider blocks" },
    "tools": [
      {
        "type": "endCall",
        "name": "end_call",
        "description": "End the call when the user says goodbye, asks to stop, or the conversation is clearly finished.",
        "config": { "messageType": "custom", "customMessages": ["Goodbye!"], "audioRecordingId": null }
      }
    ],
    "...": "all remaining config sections at their defaults"
  }
}

The agent is addressed by uuid in every CRUD path; there is no integer id. webhookSecret is never present in the envelope.

Examples

A minimal create — everything not sent lands on defaults:

curl -X POST https://dashboard.zoxa.ai/api/v1/agents \
  -H "X-API-Key: zsk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sales bot",
    "description": "Qualifies B2B SaaS leads.",
    "systemPrompt": "You qualify inbound leads. Ask about company size and budget. Be concise.",
    "greeting": { "firstMessages": ["Hi! Are you looking to evaluate our product?"] },
    "llm": { "provider": "openai", "model": "gpt-5.4-mini" },
    "stt": { "provider": "soniox" },
    "tts": { "provider": "elevenlabs" }
  }'
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: "Sales bot",
    description: "Qualifies B2B SaaS leads.",
    systemPrompt: "You qualify inbound leads. Ask about company size and budget. Be concise.",
    greeting: { firstMessages: ["Hi! Are you looking to evaluate our product?"] },
    llm: { provider: "openai", model: "gpt-5.4-mini" },
    stt: { provider: "soniox" },
    tts: { provider: "elevenlabs" },
  }),
});
const agent = await res.json();
console.log("Use this with POST /call:", agent.uuid);
import httpx

resp = httpx.post(
    "https://dashboard.zoxa.ai/api/v1/agents",
    headers={"X-API-Key": "zsk_..."},
    json={
        "name": "Sales bot",
        "description": "Qualifies B2B SaaS leads.",
        "systemPrompt": "You qualify inbound leads. Ask about company size and budget. Be concise.",
        "greeting": {"firstMessages": ["Hi! Are you looking to evaluate our product?"]},
        "llm": {"provider": "openai", "model": "gpt-5.4-mini"},
        "stt": {"provider": "soniox"},
        "tts": {"provider": "elevenlabs"},
    },
)
agent = resp.json()
print("Use this with POST /call:", agent["uuid"])

A fuller create — picking voice, transcriber, languages, and a tool:

{
  "name": "Hinglish support",
  "systemPrompt": "You help {{customer_name}} with orders placed on Acme.",
  "languages": ["en", "hi"],
  "llm": { "provider": "qwen", "model": "qwen-flash" },
  "stt": { "provider": "soniox", "interruptionMinWords": 0 },
  "tts": { "provider": "sarvam", "voice": "ishita" },
  "greeting": {
    "firstMessages": ["Hi {{customer_name}}!", "Hello, thanks for calling Acme!"]
  },
  "tools": [
    {
      "type": "function",
      "name": "check_order_status",
      "description": "Look up the caller's order status by order id.",
      "config": {
        "server": { "url": "https://api.acme.com/orders/status", "method": "POST" },
        "parameters": {
          "type": "object",
          "properties": { "order_id": { "type": "string" } },
          "required": ["order_id"]
        },
        "messages": ["Let me pull that up for you."]
      }
    }
  ],
  "contextVariables": { "customer_name": "there" },
  "webhook": { "url": "https://your.app/zoxa-hook" }
}

Errors

StatusShapeWhen
400{ "detail": { "error": "invalid_agent_config", "details": [ … ] } }Any config validation failure — unknown field, bad range, unsupported language, invalid tool.
400{ "detail": "No organization selected" }API key has no organization context.

Each entry in details carries the failing path and a human message:

{
  "detail": {
    "error": "invalid_agent_config",
    "details": [
      { "loc": ["llm", "temperature"], "msg": "Input should be less than or equal to 2", "type": "less_than_equal" },
      { "loc": ["languages"], "msg": "Value error, unknown language ids: ['xx']; valid ids: […]", "type": "value_error" }
    ]
  }
}

On this page