Twilio

Your agent can answer the phone.

Connect your own Twilio number and calls are answered by the same agent that answers your chat — every request proven by signature, the transcript arriving in your inbox as a conversation, and a lead or a ticket captured while the caller is still talking.

Every row below is traced to the file that implements it and was probed against production on 2026-09-01. Nothing on this page is aspirational.

1
inbound Twilio door, signature-verified
5
tools the agent can fire mid-call
2
outbound paths — voice and SMS
0
inbound SMS doors

The state of it

Nine capabilities, four honest states

Shipped and reachable, shipped but waiting on credentials, shipped with a known defect, or simply not built. A page that only listed the first kind would be a brochure.

Live 2 — deployed and reachable Gated 2 — ships, needs credentials Defect 3 — ships, does not do its job Absent 2 — no implementation
Live

Inbound phone call

channels/src/channels/voice.ts

Twilio posts the call, the signature is verified, and the spoken transcript becomes a signal in a conversation for that call. Probed today: POST /webhook/voice returns 401 without a valid signature.

Defect

Per-workspace routing by number

channels/src/index.ts — LENIENT_DEMUX_DOORS

The dialled number is meant to be the tenant key, and the connection row is stored and sealed — but the voice door is still on the lenient ramp, so a number with no row falls back to the default workspace instead of being refused. The signature is also checked against one worker-wide token, not the caller’s own.

Live

Tools the agent fires mid-call

VOICE_CLIENT_TOOLS

Answer from the knowledge base, capture a lead, open a ticket, send an email, or notify a person for a handoff — each one a signal into the same substrate the team already works in.

Gated

Outbound phone call

lib/resolvers/voice.ts — voice:call

Dials out through the ElevenLabs voice agent and writes every attempt to the call log, connected or not. Needs a provisioned agent and an ElevenLabs key.

Gated

Outbound campaign SMS

channels/src/agents/campaign-sms.ts

Sends through the workspace’s own connected Twilio account, billed against the campaign it belongs to. Needs a Composio key and a linked Twilio account.

Defect

Number ownership proof

pages/api/connect/voice.ts

The check is written and the resolver fails closed on it — but the shipped POST route never calls it, so a number claimed through that door is unproven.

Defect

Inbound SMS webhook auth

campaign-sms.ts — verifyWebhook

Returns true for any non-empty signature. It performs no HMAC at all, despite the comment describing one.

Absent

Inbound SMS → conversation

channels/src/channels/index.ts

Ten channel doors are registered and none of them is SMS. A reply to a campaign text reaches nothing.

Absent

SMS consent enforcement

campaign-sms.ts — push()

Contacts carry an SMS consent flag, but push() only documents that the caller should check it. Nothing blocks a send.

The path

From a ringing phone to a captured lead

Five hops. The reply does not travel back over Twilio, which is the detail that explains why the door only ever listens.

1

Twilio posts the call

A caller dials your number. Twilio sends the call to the voice door as form-encoded parameters, with the dialled number among them.

2

The signature is recomputed

HMAC-SHA1 over the request URL plus the sorted parameters, using your auth token, compared byte by byte with no early exit. Missing signature, missing token or mismatch — all 401, nothing enters.

3

The number looks up the workspace

The dialled number is the demux key. Where a connection row exists the call lands in that workspace; where none does, it currently falls back to the default one rather than being refused — the door is still on the lenient migration ramp.

4

The transcript becomes a conversation

The spoken words become a signal in a conversation for that call. It reads like any other thread — searchable, assignable, followed up the same way.

5

The agent answers, and acts

The reply travels the realtime voice session rather than back over Twilio, which is why the door never sends anything outbound. While talking, the agent can fire any of its five tools.

The proof, not the promise

$ curl -X POST https://channels.one.ie/webhook/voice \
       -d "CallSid=x&SpeechResult=hi"

401

Run against production on 2026-09-01. An unsigned call is refused at hop 2 — the door is deployed and it fails closed.

Mid-call

Five things it can do while still talking

Each tool is a signal, not a private integration — so what happens on a call shows up exactly where the rest of the work already lives.

Answer from the workspace knowledge base

lookupInfo → knowledge:query

Create the caller as a lead

captureLead → crm:create

Open a support ticket

createTicket → support:create

Email the caller

sendEmail → notify:email

Notify a person for a live handoff

transferCall → human:notify

What it costs

Credits per unit

From the billing config, not a price list. An outbound call minute costs about twenty-four texts — worth knowing before a workflow starts dialling. Twilio bills you separately for the number and the traffic; that account stays yours.

Outbound voice, per minute 47.3
Inbound voice, per minute 8.8
SMS, per message 2
Email, per send 1

What we are honest about

SMS only goes one way

Voice is the finished surface. SMS is not, and the gap is the kind that matters: an agent can send a campaign text, but nothing catches the reply.

No inbound SMS door

Ten channel doors are registered and not one of them is SMS. A reply to a campaign text — including someone answering STOP — reaches nothing. Until that door exists, use voice or email whenever the reply is the point.

Consent is recorded, not enforced

Every contact carries an SMS consent flag, and the send path documents that the caller should check it — but nothing in the code blocks a send. The flag is a note today, not a gate.

Two verifiers, one standard

The voice door does the HMAC properly. The SMS webhook verifier approves any non-empty signature and computes nothing. The correct implementation already exists a few files away, which is exactly what makes the second one indefensible.

One token for every number

Each workspace stores its own Twilio auth token, but the voice door still verifies every inbound call against one worker-wide token — the stored rows are sealed and not yet read. Until they are, an agency cannot give each client number its own credential.

A proof that is written but not called

Number ownership is verified against Twilio’s own API, and the resolver refuses a number that fails. The shipped provisioning route never invokes it — so the check passes review and protects nothing.

Bring your own number.

Add a Twilio number and auth token, provision the voice agent, and the next call arrives as a conversation. The Twilio account stays yours — we never resell numbers.