Documentation

Ciabatta API

Ciabatta runs complete voice agents: speech recognition with echo cancellation, a language model with tools and memory, and a natural voice, all billed per turn. Base URL: https://api.getciabatta.com.

Quickstart

  1. Create an account and add credits.
  2. Build an agent in the dashboard (or with POST /v1/agents) and talk to it in the playground.
  3. Create an API key and call the agent from your server:
curl https://api.getciabatta.com/v1/agents/support/chat \
  -H "Authorization: Bearer $CIABATTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Hi, where is my order?", "customer_id": "cust_123"}'

Authentication

Send your secret key as Authorization: Bearer sk_live_... (or sk_test_...). Keys are for servers only. Browsers and phones join voice calls with a short-lived session token instead (see Voice sessions).

Agents

MethodPathWhat it does
GET/v1/agentsList agents.
POST/v1/agentsCreate: {name, description?, profile?, ...settings}.
GET / PATCH / DELETE/v1/agents/{id_or_name}Read, update (settings merge; null resets a field), delete.
GET/v1/agents/{id}/versionsVersion history; restore with POST .../versions/{n}/restore.
GET/v1/agents/schemaEvery setting with its type, range and default (public).
GET/v1/modelsLanguage models with per-turn prices (public).

Key settings: model or tier (S, M, L), system_prompt, personality_prompt, safety_prompt, voice, tts.engine (bread-1 or bread-premium-1), tools, first_message, first_speaker, memory, retention, barge_in and call limits. Prompts may use {{variables}} filled per request.

The system prompt is assembled in this order: system prompt, personality, voice rules, safety rules (stated to override everything above), then the customer's memory summary. Every new agent starts with sensible defaults for all three prompts.

Text turns

POST /v1/agents/{id_or_name}/chat
{"message": "...", "customer_id": "cust_123", "variables": {"name": "Ana"},
 "conversation": "continue" | "new" | "conv_...", "stream": false}

Returns {status, reply, conversation_id, tool_calls, usage, billing, warnings}. With "stream": true the response is server-sent events: turn, text {delta}, tool_call, then done (the full turn) or error. When status is requires_action, run the client tools and send {"tool_results": [{"id", "result"}], "conversation": "conv_..."}.

Voice sessions

Your server creates a session, then hands ws_url and token to the browser or app, which connects within 5 minutes. Your API key never leaves your server.

POST /v1/agents/{id_or_name}/sessions
{"customer_id": "cust_123", "variables": {...}, "conversation": "continue",
 "overrides": {"voice": "...", "system_prompt": "..."}}

-> {"id": "ses_...", "ws_url": "wss://api.getciabatta.com/v1/sessions/ses_.../stream",
    "token": "st_...", "token_expires_at": 1760000000, "conversation_id": "conv_...", ...}
const ws = new WebSocket(session.ws_url, ["ciabatta", session.token]);
ws.binaryType = "arraybuffer";
ws.onopen = () => ws.send(JSON.stringify({type: "start", encoding: "pcm_s16le", sample_rate: 16000, played_acks: true}));

GET /v1/sessions/{id} returns status, turns and cost; DELETE /v1/sessions/{id} ends a live call. Each session pins the agent version and prices in force when it was created. If the connection drops, create a new session with "conversation": "conv_..." to continue where you left off.

Session protocol

Text frames are JSON. Binary frames are audio: mic audio from you (the start encoding), agent audio to you (pcm_s16le 24 kHz, or 8 kHz mu-law/a-law when you send telephony audio).

You sendMeaning
start {encoding?, sample_rate?, played_acks?}First message. Encodings: pcm_s16le, mulaw, alaw.
binaryMicrophone audio, 20-100 ms per frame.
text {content}A typed user turn.
tool_result {id, result}Answer to a client tool_call (30 s timeout).
played {pos}Playback progress, see Playback acks.
interrupt, end, ping {t}Stop the agent talking, hang up, keep-alive/RTT.
You receiveMeaning
ready {session_id, conversation_id, output}Connected; output is the agent audio format.
transcript {text, final}What the caller is saying.
turn {state}listening, thinking, tool, speaking.
agent_text {turn_id, delta}The reply text as it is generated.
audio_start {turn_id, pos}, binary, audio_end {turn_id, samples, pos}The spoken reply.
clear {turn_id, reason, pos}Barge-in: stop playback immediately and drop queued audio.
duckOptional: lower the agent volume while the caller talks over it.
tool_call {id, name, arguments}Run a client tool and reply with tool_result.
usage {turn_id, billing, session_cost_usd, ...}Per-turn tokens, characters and cost.
warning {code, message}, error {code, message}Non-fatal notice / failure.
closed {reason}The call ended (client_end, end_call, max_duration, idle_hangup, insufficient_balance, ...).

Playback acks

Echo cancellation and barge-in work best when Ciabatta knows what the caller actually heard. Positions (pos) count agent output samples sent since the session started. Send start {played_acks: true}, then about every 100 ms send played {pos} with the number of samples your speaker has played. On clear, stop playback and jump your counter to clear.pos. Interrupted replies are then saved to history cut to what was heard. Without acks Ciabatta estimates playback from arrival time and sends warning played_acks_missing; if acks stop for 2 s it falls back and sends played_acks_stopped.

Tools

Add tools to an agent's tools list. Webhook tools are called by Ciabatta with a signed POST (Ciabatta-Signature: HMAC-SHA256 of "<t>.<body>" with the agent's webhook_secret). Client tools are sent to your app (tool_call) to run locally. Built-ins: end_call and wait. max_tool_rounds caps tool calls per turn.

Customers and memory

Pass a stable, opaque customer_id (never an email or phone number). With "conversation": "continue" the customer's latest conversation continues; "new" starts fresh. Turn on memory.summary to keep a rolling per-customer summary that is added to the prompt. Transcript retention defaults to 7 days (up to 90, or none); zero data retention keys and accounts store nothing.

Voices

GET /v1/voices lists stock voices and your clones with the engines each supports. Clone voices in the dashboard. bread-1 is the fastest engine; bread-premium-1 adds expressive tags such as laughs and sighs.

Pricing

Prepaid credits, billed per turn by model tier (see GET /v1/models for exact prices and per-turn allowances). Each turn includes generous prompt, reply and speech allowances; usage past them adds small surcharges. Premium voice adds a fixed amount per turn. Connected time beyond the per-turn allowance is billed at a low hourly idle rate. Nothing is charged for failed turns.

Errors

Errors return JSON {"error": {"code", "message", "param", "request_id"}} with a matching HTTP status. Common codes: invalid_request, invalid_agent_config (with errors[]), agent_not_found, insufficient_balance (402), rate_limited (429), monthly_cap_reached, upstream_unavailable (retry with backoff). Include request_id when contacting .

Limits