All integrations

Twilio

Channel

Put your agent on a phone number: SMS and speech-transcribed calls.

At a glance

From Eve docs · synced Jul 22, 2026

Webhook route

POST /eve/v1/twilio

Environment variables

TWILIO_ACCOUNT_SID
required for default outbound SMS
TWILIO_AUTH_TOKEN
required for inbound signature verification

Capabilities

  • Human-in-the-loopSMS and voice have no native button or card affordance, so HITL prompts do not render as interactive controls.
  • Proactive sessionsStart a session without an inbound message through receive(twilio, { message, target, auth }) from a schedule run handler, or args.receive(twilio, ...) from another channel.
  • AttachmentsInbound media attachments are not supported on this channel today.

Derived from Eve's Twilio docs.

Install

Install the framework:

npm install eve@latest

Quick start

Create agent/channels/twilio.ts. allowFrom is required and gates who can reach the inbound hooks:

// agent/channels/twilio.tsimport { twilioChannel } from "eve/channels/twilio";export default twilioChannel({  allowFrom: "+15551234567",  messaging: { from: "+15557654321" },});
TWILIO_ACCOUNT_SID=AC...   # required for default outbound SMSTWILIO_AUTH_TOKEN=...      # required for inbound signature verification

Configure

In the Twilio console, point your number's Messaging webhook at /eve/v1/twilio/messages and its Voice webhook at /eve/v1/twilio/voice. Inbound calls are answered with speech gathering, and the transcript feeds the same session SMS uses. See the Twilio channel docs for dispatch, streaming, and voice specifics.

How it behaves

Excerpts from Eve's Twilio docs. Prefer the source when something looks out of date.

Dispatch

allowFrom is required. It gates who can reach the inbound hooks. Pass a single number, a list, an async resolver, or "*". The wildcard is dangerous; only use it with an explicit check inside onText/onVoice.

export default twilioChannel({ allowFrom: ["+15551234567", "+15557654321"] });

onText and onVoiceTranscription decide dispatch and auth. Return { auth } to proceed, or null to drop the message. onVoice fires the moment a call comes in. Return null to reject it, or return an object to override the spoken prompt, language, <Say voice>, and speech-recognition options.

export default twilioChannel({
  allowFrom: ["+15551234567"],
  onText: (ctx, message) => ({
    auth: {
      principalId: message.from,
      principalType: "user",
      authenticator: "twilio",
      attributes: { to: message.to ?? "" },
    },
  }),
});
Delivery

The default message.completed handler sends the reply as SMS through Twilio's Messages API. A reply to an inbound message can reuse the webhook's To as the sender, but a proactive send has nothing to reuse, so it needs messaging.from or messaging.messagingServiceSid. Behind a proxy, set webhookUrl so signature verification matches the exact configured URL, and publicBaseUrl so voice TwiML can build absolute callback URLs.

Human-in-the-loop (HITL)

SMS and voice have no native button or card affordance, so HITL prompts do not render as interactive controls. The agent's input.requested event reaches your events["input.requested"] handler if you declare one. Handle it by sending the prompt as text and mapping the caller's reply back to the input request yourself.

Proactive sessions

Start a session without an inbound message through receive(twilio, { message, target, auth }) from a schedule run handler, or args.receive(twilio, ...) from another channel. target.phoneNumber is required, and the channel needs messaging.from or messaging.messagingServiceSid for the outbound sender.

Attachments

Inbound media attachments are not supported on this channel today.

Agents using Twilio