website/docs/user-guide/messaging/sms.md
Hermes connects to SMS through the Twilio API. People text your Twilio phone number and get AI responses back — same conversational experience as Telegram or Discord, but over standard text messages.
:::info Shared Credentials
The SMS gateway shares credentials with the optional telephony skill. If you've already set up Twilio for voice calls or one-off SMS, the gateway works with the same TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_PHONE_NUMBER.
:::
pip install 'hermes-agent[sms]'+15551234567)hermes gateway setup
Select SMS (Twilio) from the platform list. The wizard will prompt for your credentials.
Add to ~/.hermes/.env:
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token_here
TWILIO_PHONE_NUMBER=+15551234567
# Security: restrict to specific phone numbers (recommended)
SMS_ALLOWED_USERS=+15559876543,+15551112222
# Optional: set a home channel for cron job delivery
SMS_HOME_CHANNEL=+15559876543
Twilio needs to know where to send incoming messages. In the Twilio Console:
https://your-server:8080/webhooks/twilioPOST:::tip Exposing Your Webhook If you're running Hermes locally, use a tunnel to expose the webhook:
# Using cloudflared
cloudflared tunnel --url http://localhost:8080
# Using ngrok
ngrok http 8080
Set the resulting public URL as your Twilio webhook. :::
Set SMS_WEBHOOK_URL to the same URL you configured in Twilio. This is required for Twilio signature validation — the adapter will refuse to start without it:
# Must match the webhook URL in your Twilio Console
SMS_WEBHOOK_URL=https://your-server:8080/webhooks/twilio
The webhook port defaults to 8080. Override with:
SMS_WEBHOOK_PORT=3000
hermes gateway
You should see:
[sms] Twilio webhook server listening on 127.0.0.1:8080, from: +1555***4567
If you see Refusing to start: SMS_WEBHOOK_URL is required, set SMS_WEBHOOK_URL to the public URL configured in your Twilio Console (see Step 3).
Text your Twilio number — Hermes will respond via SMS.
| Variable | Required | Description |
|---|---|---|
TWILIO_ACCOUNT_SID | Yes | Twilio Account SID (starts with AC) |
TWILIO_AUTH_TOKEN | Yes | Twilio Auth Token (also used for webhook signature validation) |
TWILIO_PHONE_NUMBER | Yes | Your Twilio phone number (E.164 format) |
SMS_WEBHOOK_URL | Yes | Public URL for Twilio signature validation — must match the webhook URL in your Twilio Console |
SMS_WEBHOOK_PORT | No | Webhook listener port (default: 8080) |
SMS_WEBHOOK_HOST | No | Webhook bind address (default: 127.0.0.1) |
SMS_INSECURE_NO_SIGNATURE | No | Set to true to disable signature validation (local dev only — not for production) |
SMS_ALLOWED_USERS | No | Comma-separated E.164 phone numbers allowed to chat |
SMS_ALLOW_ALL_USERS | No | Set to true to allow anyone (not recommended) |
SMS_HOME_CHANNEL | No | Phone number for cron job / notification delivery |
SMS_HOME_CHANNEL_NAME | No | Display name for the home channel (default: Home) |
Hermes validates that inbound webhooks genuinely originate from Twilio by verifying the X-Twilio-Signature header (HMAC-SHA1). This prevents attackers from injecting forged messages.
SMS_WEBHOOK_URL is required. Set it to the public URL configured in your Twilio Console. The adapter will refuse to start without it.
For local development without a public URL, you can disable validation:
# Local dev only — NOT for production
SMS_INSECURE_NO_SIGNATURE=true
The gateway denies all users by default. Configure an allowlist:
# Recommended: restrict to specific phone numbers
SMS_ALLOWED_USERS=+15559876543,+15551112222
# Or allow all (NOT recommended for bots with terminal access)
SMS_ALLOW_ALL_USERS=true
:::warning SMS has no built-in encryption. Don't use SMS for sensitive operations unless you understand the security implications. For sensitive use cases, prefer Signal or Telegram. :::
TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN are correctSMS_ALLOWED_USERS (or SMS_ALLOW_ALL_USERS=true)TWILIO_PHONE_NUMBER is set correctly (E.164 format with +)If port 8080 is already in use, change it:
SMS_WEBHOOK_PORT=3001
Update the webhook URL in Twilio Console to match.