documentation
Self-hosting

Twilio

You need a Twilio account, a phone number, and a SIP trunk that bridges to LiveKit Cloud.

1. Account credentials

From console.twilio.com, copy these values:

  • TWILIO_ACCOUNT_SID — starts with AC…
  • TWILIO_AUTH_TOKEN — click "Show" to see the value

Put them in .env.

2. Phone number

Go to Phone Numbers → Buy a number. Select a number with the Voice capability. For outbound SMS, select a number that also has the SMS capability. The carrier fixes these capabilities when you buy the number; you cannot add SMS to a voice-only number later. Note the E.164 format (+1…).

3. SIP trunk

  1. Elastic SIP Trunking → Trunks → Create new Trunk.
  2. Under Termination, choose a unique termination domain such as your-trunk.pstn.twilio.com.
  3. Add a credential list with a SIP username and password. LiveKit's outbound trunk must use the same credentials.
  4. Under Numbers, attach the phone number from step 2.
  5. Continue with LiveKit Cloud setup, using the Twilio termination domain and credentials to create a LiveKit outbound trunk.

Do not add the termination domain to Hail's .env: Hail uses the resulting LiveKit trunk ID (LIVEKIT_TWILIO_SIP_OUTBOUND_TRUNK_ID) at runtime. Twilio calls traffic from LiveKit to the PSTN “termination.” Its “origination” settings carry inbound calls to LiveKit: see §6.

Twilio requires outbound destinations and caller IDs in E.164 format. Trial accounts can call only verified destination numbers. See Twilio's Elastic SIP Trunking reference.

4. Outbound SMS

Hail sends SMS from a dedicated number.

  1. Acquire an SMS-capable number (step 2 above). Only numbers with the SMS capability can send.
  2. Hail sets the number up. When the number becomes active, Hail attaches it to your organization's Twilio Messaging Service and creates that service the first time, with its inbound webhook pointed at <HAIL_API_URL>/sms/inbound. There is one Messaging Service per organization; every number joins the same shared sender pool. If that step failed (the API log says why), assigning a texts agent runs it again, and POST /numbers/{id}/enable-sms runs it by hand. Both are idempotent.
  3. Send. Call POST /sms with the recipient and body. Hail sends from your organization's dedicated number.

A2P 10DLC (United States). US carriers deliver application-to-person SMS on long-code numbers only after you register an A2P 10DLC brand and campaign. Do this in the Twilio console (Messaging → Regulatory Compliance → A2P 10DLC) before you send to US numbers. Registration is a Twilio-side requirement; Hail does not manage it.

Sender ID (optional, rest-of-world). For destinations that allow it, set a custom alphanumeric sender with PATCH /sms/sender-id (read the current value with GET /sms/sender-id). Clear it by sending null. When you set none, Hail falls back to the platform default sender. The United States and Canada do not allow alphanumeric sender IDs — messages there always send from the dedicated number, regardless of this setting.

Rate limits. Per-organization send velocity is capped by HAIL_VELOCITY_SMS_PER_HOUR (default 100) and HAIL_VELOCITY_SMS_PER_DAY (default 1000). An abuse monitor suspends an organization's SMS channel when its opt-out rate is too high — see operations for the HAIL_SMS_ABUSE_* variables and how to lift a suspension.

5. Inbound SMS & opt-out

Inbound texts arrive through the Messaging Service's inbound webhook, which Hail sets to <HAIL_API_URL>/sms/inbound (HTTP POST) when it creates or reuses the service. Nothing to set on the number itself. Hail verifies Twilio's X-Twilio-Signature against HAIL_API_URL, so that value must be the public URL that Twilio posts to.

Recognized keywords (Hail matches them on the message body, case-insensitive):

  • Opt out (STOP): STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT
  • Opt in (START): START, YES, UNSTOP
  • Help: HELP, INFO

Hail records opt-outs in its own suppression list, regardless of the Twilio configuration. Hail checks this list before every send.

Opt-out replies: By default, Twilio replies automatically to STOP/HELP/START and carrier-blocks opted-out numbers. In that setup, keep HAIL_SMS_COMPLIANCE_REPLIES_ENABLED=false. If you want Hail to own the replies (for example, a non-Twilio provider, or a custom keyword experience), do the two steps that follow. Caution: if you disable Twilio's default opt-out handling, the change is account-wide and requires a Twilio Support request; there is no API for it. First, disable Twilio's default opt-out handling. Then set HAIL_SMS_COMPLIANCE_REPLIES_ENABLED=true.

6. Inbound calls

Twilio sends a call to LiveKit when the number sits on an Elastic SIP trunk whose origination URI is LiveKit. Hail attaches the number when it routes calls to an agent; you set up the trunk once.

  1. On the trunk from step 3, open Origination and add the origination URI sip:<project>.sip.livekit.cloud;transport=tcp (your LiveKit SIP endpoint is on the project's settings page).
  2. Copy the trunk SID (TK...) into .env as TWILIO_SIP_TRUNK_SID.
  3. Create the LiveKit inbound trunk and dispatch rule (LiveKit Cloud §4) and set LIVEKIT_SIP_INBOUND_TRUNK_ID.

Twilio Elastic SIP Trunking has no inbound username/password, so the LiveKit trunk lists the numbers instead: PATCH /v1/numbers/{id} with voice_agent_id adds the number to the Twilio trunk (trunking.v1.trunks(TK...).phone_numbers) and to the LiveKit trunk; null removes it from both (Agents).

On this page