API referencePhone

Place an outbound AI phone call

Places an outbound AI voice call and returns immediately with the call in queued status; the dial runs asynchronously, so poll GET /v1/phone/calls/{id} or subscribe to call.status_changed to follow it to a terminal state.

Endpoint
POST https://api.dairo.app/v1/phone/calls

Places an outbound AI voice call and returns immediately with the call in queued status; the dial runs asynchronously, so poll GET /v1/phone/calls/{id} or subscribe to call.status_changed to follow it to a terminal state. to and from are required E.164 numbers — from must be a number this account owns with status: "active", otherwise the request is a 400 — and instructions (required, up to 8,000 characters) tells the agent what to accomplish. {{name}} placeholders in instructions and greeting are filled from variables when the call is created (a placeholder with no matching variable stays as written). Every call runs on Dairo Live: model is optional and only dairo-live is accepted — any other value is a 400. Set voice to one of the 22 Dairo Live voice ids from the phone voice catalog (default marin; any other value is a 400) and language to the ISO code the agent speaks, like de; maxDurationSeconds caps the call between 30 and 780 seconds (default 600); and voicemailMessage (≤500 characters) is left on a voicemail — without it the agent hangs up silently when a mailbox answers. Pass up to 10 questions and the agent works them into the call, landing structured answers on the row afterward. Every connected call is recorded, and the agent hangs up by itself once the person says goodbye. Supply an Idempotency-Key header or idempotencyKey body field (≤128 characters) so a retried request returns the original call instead of dialing the person twice; a header that disagrees with the body field is a 400. The call emits a call.status_changed webhook to your subscribed endpoints when it is answered and when it reaches a terminal status. Price: $0.09 per minute for Dairo Live plus the destination's telephony rate, per started minute; a call that never connects is free. Requires the phone:call scope.

Scope: phone:call. Send the API key as a bearer token; Scopes lists what each one reaches.

Request

cURL
curl -X POST "https://api.dairo.app/v1/phone/calls" \
  -H "Authorization: Bearer $DAIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "string",
    "from": "string",
    "instructions": "string"
  }'

Headers

NameTypeRequiredDescription
Idempotency-KeystringnoOptional idempotency key for safe retries of side-effecting operations. (max length 128)

Body

Required, application/json: PhoneCallCreateRequest.

Calls are always recorded. To follow a call's progress subscribe a webhook to the call.status_changed event. The agent hangs up by itself when the person says goodbye.

FieldTypeRequiredDescription
tostringyesDestination, E.164. A real phone rings here.
fromstringyesCaller id, E.164. MUST be a number this account owns with status active.
instructionsstringyesWhat the agent should accomplish. {{name}} placeholders are filled from variables when the call is created; unknown placeholders stay as written. (max length 8000)
greetingstringnoOpening line spoken when the callee answers. Supports {{name}} placeholders filled from variables.
voice"marin", "cedar", "alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "quartz", "ripple", "vesper", "willow", "stone", "gleam", "meridian", "bossa", "tempo", "beacon", "delta", "cinder"noA Dairo Live voice id. marin (default), cedar, alloy, ash, ballad, coral, echo, sage, shimmer and verse are multilingual; quartz, ripple (Australian English), vesper (British English), willow, stone (Irish English), gleam, meridian (North American English), bossa, tempo (Brazilian Portuguese), beacon (Filipino English), delta, cinder (Southern U.S. English). Any other value is a 400. Browse them with GET /v1/phone/voices. (default "marin")
model"dairo-live"noThe call model. Phone calls run on Dairo Live; dairo-live is the only accepted value (any other value is a 400). (default "dairo-live")
languagestringnoISO language code the agent speaks (e.g. en, de).
maxDurationSecondsintegernoHard cap on call length in seconds (30-780). (minimum 30, maximum 780, default 600)
variablesobjectnoValues for {{name}} placeholders in instructions and greeting, filled in at create time. Placeholders without a value stay as written.
voicemailMessagestringnoMessage the agent leaves when the call reaches a voicemail. Without it the agent hangs up silently on a voicemail. (max length 500)
metadataobjectno
idempotencyKeystringnoA retried request returns the original call rather than dialing the human twice.

Response

201 Created. Body: application/json, PhoneCall.

FieldTypeRequiredDescription
object"phone_call"yes
idstring (uuid)yes
direction"outbound", "inbound"yesoutbound for calls placed with POST /v1/phone/calls, inbound for calls answered by a number's inbound agent.
status"queued", "initiating", "ringing", "in_progress", "completed", "failed", "no_answer", "busy", "canceled"yesTerminal: completed, failed, no_answer, busy, canceled.
fromNumberstringyes
toNumberstringyes
instructionsstring or nullno
greetingstring or nullno
voicestring or nullnoThe Dairo Live voice id.
modelstring or nullnoAlways dairo-live.
languagestring or nullno
maxDurationSecondsinteger or nullno
voicemailMessagestring or nullno
queuedAtstring (date-time) or nullno
startedAtstring (date-time) or nullno
answeredAtstring (date-time) or nullno
endedAtstring (date-time) or nullno
durationSecondsinteger or nullno
hangupCausestring or nullno
answeredBystring or nullnohuman or machine, from answering-machine detection.
summarystring or nullno
notesarray of PhoneCallNote or nullnoInbound calls: the caller's structured notes (tasks, notes, reminders, messages).
costUsdnumber or nullnoBilled per started minute: the $0.09/min Dairo Live rate plus the destination's telephony rate. Calls that never connect are free.
errorstring or nullno
metadataobjectno
idempotencyKeystring or nullno
createdAtstring (date-time)yes
201
{
  "object": "phone_call",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "direction": "outbound",
  "status": "queued",
  "fromNumber": "string",
  "toNumber": "string",
  "instructions": null,
  "greeting": null,
  "voice": null,
  "model": null,
  "language": null,
  "maxDurationSeconds": null,
  "voicemailMessage": null,
  "queuedAt": "2026-10-19T09:30:00Z",
  "startedAt": "2026-10-19T09:30:00Z",
  "answeredAt": "2026-10-19T09:30:00Z",
  "endedAt": "2026-10-19T09:30:00Z",
  "durationSeconds": null,
  "hangupCause": null,
  "answeredBy": null,
  "summary": null,
  "notes": [
    {
      "kind": "task",
      "text": "Thanks for signing up.",
      "due": "string"
    }
  ],
  "costUsd": null,
  "error": null,
  "metadata": {},
  "idempotencyKey": null,
  "createdAt": "2026-10-19T09:30:00Z"
}

Errors

Every error has the same body, {"error": {"type", "code", "message", "param"}}; branch on code. Errors lists every code.

StatusMeaning
400The request is malformed or a value is invalid (invalid_request, invalid_domain, idempotency_key_mismatch).
401The API key is missing or invalid.
403The key lacks the scope this endpoint needs (scope_missing) or may not do this (not_permitted).
404No such resource in this account (resource_not_found).
409The resource already exists or its state does not allow this (duplicate_resource or a condition-specific code).
429Rate limit, plan limit, budget or spend cap reached (rate_limited, plan_limit_reached, budget_exceeded, spend_cap_reached).
500An unexpected error; retry with backoff, then contact support (internal_error).

On this page