On this page
Phone
Phone agents call out over your own carrier: connect a SIP trunk, import the numbers you own on it, and place calls from them. Talos does not sell numbers or bill for carrier minutes.
Bring your own carrier
In Twilio
- Create an Elastic SIP trunk and turn on Secure Trunking: Talos dials over TLS (port 5061) with SRTP media.
- Under Termination, set a Termination SIP URI (
<name>.pstn.twilio.com) and attach a Credential List (a username and password). IP access control lists on their own are not supported: Talos authenticates with those digest credentials. - Under Numbers, add every number you will call from.
In Talos
The simplest path is Talos: Phone numbers → Connect trunk, then Import number for each caller ID. It stores the password as a secret for you, so the password never passes through a script or an assistant. The API steps are below.
Setting up telephony takes an admin key. A trunk is your carrier's SIP termination (twilio_elastic_sip or generic_sip) with digest credentials: the username, and the password stored as a secret of kind sip_auth. Its host must resolve to public addresses.
Connect a trunk and import a number
# 1. Store the trunk's SIP digest password as a secret (admin key).
PUT /api/v1/secrets/twilio-sip { "kind": "sip_auth", "value": "…" }
# → { "id": "twilio-sip", "current_version": "…", … }
# 2. Connect the trunk. Talos dials out through it with these credentials.
POST /api/v1/telephony/trunks
{ "trunk_id": "twilio-main", "name": "Twilio main", "provider": "twilio_elastic_sip",
"termination_uri": "acme.pstn.twilio.com", "auth_username": "talos",
"auth_secret": { "id": "twilio-sip", "version": "<current_version>" } }
# 3. Import a number you own on that trunk, and assign it to an agent.
POST /api/v1/telephony/numbers
{ "e164": "+14155550100", "trunk_id": "twilio-main", "nickname": "Collections line",
"outbound_agent_id": "<phone agent id>" }
# → { "number_id": "…", "state": "pending" | "ready", … }
- A number is
pendinguntil it is provisioned, thenready. Calls from a pending number are refused with503 provider_unavailable; retry once it is ready. outbound_agent_idrecords which published agent calls from the number; an agent with a number assigned cannot be deleted.- Inbound calls to your numbers are not available yet:
inbound_agent_idand inbound trunks are refused. - Trunks and numbers belong to one environment: connect them in your sandbox to test calls there, and again in production.
What Cumulus sets up first
Cumulus staff set each environment's concurrent call cap and open it for runs. Before that, connecting a trunk or importing a number is refused with
tenant_not_configured, and calls withtenant_not_configuredortenant_disabled(403 forbidden, indetails.reason).GET /api/v1/runs/capacityshows the limit and whether admission is open. Ask your Cumulus contact to set voice capacity and open run admission for the environment (its slug andtenant_idare inGET /api/v1/environments). Your calls always go out on your own trunk; you never need a Cumulus trunk.
| Operation | MCP tool | Key role | Purpose |
|---|---|---|---|
GET /api/v1/telephony/trunks | trunks_list | viewer | List BYOC SIP trunks |
POST /api/v1/telephony/trunks | trunks_create | admin | Connect a BYOC SIP trunk |
PATCH /api/v1/telephony/trunks/{trunk_id} | trunks_update | admin | Update a BYOC SIP trunk |
DELETE /api/v1/telephony/trunks/{trunk_id} | trunks_delete | admin | Disconnect a BYOC SIP trunk |
GET /api/v1/telephony/numbers | numbers_list | viewer | List BYOC phone numbers |
POST /api/v1/telephony/numbers | numbers_import | admin | Import a phone number from a connected trunk |
PATCH /api/v1/telephony/numbers/{number_id} | numbers_update | admin | Assign agents or rename a phone number |
DELETE /api/v1/telephony/numbers/{number_id} | numbers_release | admin | Release a phone number |
Caller ID: from
A phone run picks its caller ID and trunk like this:
- The run's
from, or else thefromthe agent was published with (agent.telephony). With neither, the run is refused:400 invalid_request. - If that number is one you imported, the call goes out on the number's trunk.
- Otherwise it goes out on the trunk the agent was published with; an agent with no trunk is refused with
409 capability_unavailable. - The trunk must be yours, enabled, and allowed to present that number; if not,
403 forbiddenwithdetails.reason: "trunk_not_authorized".
Note
The simplest setup: import each number you call from, and pass it as
from. See Place a phone call.
Set an agent's trunk and default caller ID with the agent.telephony edit. POST …/draft/validate warns before any call would be refused: telephony_trunk_unknown (no such trunk in this environment), telephony_from_not_imported (the caller ID is not an imported number) and telephony_number_pending (the number is still being set up). Warnings do not block publishing, so you can test in the browser or with simulated calls before your trunk is ready.
Every phone run is a real call
test: truekeeps a run out of billing, webhooks and triggers, but it still dials. Test with simulated calls or a browser test session first, and call numbers you own.
During and after a call
phone_statefollows the call:admitted,preparing,awaiting_sip,dialing,ringing,active,terminating,ended,rejected,expired,setup_failed.- Events such as
phone.amd_verdict,phone.voicemailandphone.transferreport answering-machine detection, voicemail and transfers. - On an outbound call where the agent speaks first, it greets once the person has picked up: after their first words ("Hello?") and a pause, or after 1.5 s of quiet on the line, and never later than 5 s after the call connects. Those first words stay in the transcript.
beginMessageDelayMsadds to the wait. SetopenerWaitsForCallee: false(Talos: "Wait for the person to answer") to greet as soon as the call connects. Inbound calls and browser tests don't wait. - With voicemail or IVR detection set to hang up, the agent greets at answer, before detection decides, so a voicemail box can record that first line. It must be fixed text that names no one and no debt, amount or collector (for example "Hi, this is Alex on a recorded line."); ask for the person in the next node. Publishing refuses any other opener (
opener_not_neutralon the start node), and nothing else is said until detection finds a person. The finding carries a fix infixesthat puts such a greeting in front of your opener: send itseditswithdrafts.edit(Talos's Problems list does this with one click). A leave-message policy waits for detection before its first word, so its opener is not limited. - Phone agents record both channels by default.
GET …/recordingreturns a link that expires after five minutes;GET …/transcriptthe turns with their timing. - A recording never ends or shortens a call. If recording cannot start by the time the call is answered, or stops partway, the call continues unrecorded, the run's
recording.updatedevent reportsfailedwith a reason, andGET …/recordingreadsfailed. - Concurrent calls are capped per organization (
GET /api/v1/runs/capacity, channelphone).
Voices
GET /api/v1/voices lists the speech-to-text and text-to-speech models and the voices an agent can use, optionally for one language (en or es). Set them on a draft with the agent.provider edit.
| Operation | MCP tool | Key role | Purpose |
|---|---|---|---|
GET /api/v1/voices | voices_list | viewer | List the speech models and platform TTS voices an agent can use |