hermes-agent/website/docs/user-guide/messaging/sms.md
Teknium fef1a41248
docs: round 2 audit — messaging, developer-guide, guides, integrations (#22858)
Cross-checked 75 docs pages under user-guide/messaging/, developer-guide/,
guides/, and integrations/ against the live registries and gateway code.

messaging/
- index.md: API Server toolset is hermes-api-server (was 'hermes (default)');
  Google Chat slug is hermes-google_chat (underscore — plugin name uses _).
- google_chat.md: drop bogus 'pip install hermes-agent[google_chat]' (no such
  extra); list the actual deps (google-cloud-pubsub, google-api-python-client,
  google-auth, google-auth-oauthlib).
- qqbot.md: config namespace is platforms.qqbot (was platforms.qq, which is
  silently ignored by the adapter); QQ_STT_BASE_URL is not read directly —
  baseUrl lives under platforms.qqbot.extra.stt.
- teams-meetings.md: 'hermes teams-pipeline' is plugin-gated (teams_pipeline
  plugin must be enabled), not a built-in subcommand.
- sms.md: example log line 0.0.0.0:8080 -> 127.0.0.1:8080 (default
  SMS_WEBHOOK_HOST).
- open-webui.md: API_SERVER_* are env vars, not YAML keys — write them to
  per-profile .env, not 'hermes config set' (same pattern fixed in
  api-server.md last round). Also bumped example ports to 8650+ to dodge the
  default webhook (8644)/wecom-callback (8645)/msgraph-webhook (8646)
  collision.

developer-guide/
- architecture.md: tool/toolset counts (61/52 -> 70+/~28); LOC stamps for
  run_agent.py, cli.py, hermes_cli/main.py, setup.py, mcp_tool.py,
  gateway/run.py replaced with 'large file' to stop drifting.
- agent-loop.md: same LOC drift (~13,700 -> 'a large file (15k+ lines)').
- gateway-internals.md: '14+ external messaging platforms' -> '20+'; gateway
  platform tree updated (qqbot is a sub-package, not qqbot.py; added
  yuanbao.py, feishu_comment.py, msgraph_webhook.py); 'gateway/builtin_hooks/
  (always active)' was wrong — it's an empty extension point and
  _register_builtin_hooks() is a no-op stub.
- acp-internals.md: drop fictional 'message_callback' from the bridged-
  callbacks list; clarify thinking_callback is currently set to None.
- provider-runtime.md: provider list was missing AWS Bedrock, Azure Foundry,
  NVIDIA NIM, xAI, Arcee, GMI Cloud, StepFun, Qwen OAuth, Xiaomi, Ollama
  Cloud, LM Studio, Tencent TokenHub. Fallback section described only the
  legacy single-pair model — corrected to the canonical list-form
  fallback_providers chain.
- environments.md: parsers list missing llama4_json and the deepseek_v31
  alias; both register via @register_parser.
- browser-supervisor.md: drop reference to scripts/browser_supervisor_e2e.py
  which doesn't exist in-repo.
- contributing.md: tinker-atropos is a git submodule — note that
  'git submodule update --init' is required if cloning without
  --recurse-submodules.

guides/
- operate-teams-meeting-pipeline.md: cron flags were all wrong — schedule is
  positional (not --schedule), the script-only flag is --no-agent (not
  --script-only), and there's no --command flag. Replaced with a real example
  that creates the script under ~/.hermes/scripts/ and uses the actual flags.
  Also replaced fictional 'hermes cron show <name>' with 'hermes cron status'.
- automation-templates.md: 'cron create --skills "a,b"' doesn't work —
  the flag is --skill (singular, repeatable). Fixed all 5 occurrences via AST
  rewrite.
- minimax-oauth.md: 'hermes auth add minimax-oauth --region cn' silently
  fails because --region isn't registered on the auth-add argparse spec.
  Pointed users at the minimax-cn provider (or MINIMAX_CN_API_KEY env) for
  China-region access.
- cron-script-only.md: 'hermes send' is fictional — replaced the comparison-
  table mention with a webhook-subscription pointer; also fixed the dead link
  to /guides/pipe-script-output (page doesn't exist).
- cron-troubleshooting.md: 'hermes serve' isn't a real subcommand. Pointed
  at 'hermes gateway' (foreground) / 'hermes gateway start' (service).
- local-ollama-setup.md: 'agent.api_timeout' is not a config key. The right
  knob is the HERMES_API_TIMEOUT env var.
- python-library.md: run_conversation() return dict has only final_response
  and messages — task_id is stored on the agent instance, not echoed back.
- use-mcp-with-hermes.md: '--args /c "npx -y …"' wraps the npx command in
  one quoted string, so cmd.exe gets a single arg instead of the multi-token
  command line it needs. Removed the surrounding quotes — argparse nargs='*'
  collects each token correctly.

integrations/
- providers.md: Bedrock guardrail YAML keys were 'id'/'version' (don't exist);
  actual keys are guardrail_identifier/guardrail_version (matches DEFAULT_CONFIG
  and the run_agent.py reader). GMI default base URL (api.gmi.ai/v1 ->
  api.gmi-serving.com/v1) and portal URL (inference.gmi.ai -> www.gmicloud.ai)
  refreshed. Fallback section rewritten to lead with the canonical
  fallback_providers list form (was leading with the legacy fallback_model
  single dict); supported-providers list extended to include azure-foundry,
  alibaba-coding-plan, lmstudio.

index.md
- '68 built-in tools' -> '70+'; '15+ platforms' was both inconsistent with
  integrations/index.md ('19+') and undercounted — bumped to 20+ and added
  Weixin/QQ Bot/Yuanbao/Google Chat to the list.

Validation: 'npm run build' clean (exit 0); broken-link count unchanged at
155 (same as round-1 post-skill-regen baseline). 24 files, +132/-89.
2026-05-09 15:00:24 -07:00

6.4 KiB

sidebar_position sidebar_label title description
8 SMS (Twilio) SMS (Twilio) Set up Hermes Agent as an SMS chatbot via Twilio

SMS Setup (Twilio)

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. :::


Prerequisites

  • Twilio accountSign up at twilio.com (free trial available)
  • A Twilio phone number with SMS capability
  • A publicly accessible server — Twilio sends webhooks to your server when SMS arrives
  • aiohttppip install 'hermes-agent[sms]'

Step 1: Get Your Twilio Credentials

  1. Go to the Twilio Console
  2. Copy your Account SID and Auth Token from the dashboard
  3. Go to Phone Numbers → Manage → Active Numbers — note your phone number in E.164 format (e.g., +15551234567)

Step 2: Configure Hermes

hermes gateway setup

Select SMS (Twilio) from the platform list. The wizard will prompt for your credentials.

Manual setup

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

Step 3: Configure Twilio Webhook

Twilio needs to know where to send incoming messages. In the Twilio Console:

  1. Go to Phone Numbers → Manage → Active Numbers
  2. Click your phone number
  3. Under Messaging → A MESSAGE COMES IN, set:
    • Webhook: https://your-server:8080/webhooks/twilio
    • HTTP Method: POST

:::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

Step 4: Start the Gateway

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.


Environment Variables

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: 0.0.0.0)
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)

SMS-Specific Behavior

  • Plain text only — Markdown is automatically stripped since SMS renders it as literal characters
  • 1600 character limit — Longer responses are split across multiple messages at natural boundaries (newlines, then spaces)
  • Echo prevention — Messages from your own Twilio number are ignored to prevent loops
  • Phone number redaction — Phone numbers are redacted in logs for privacy

Security

Webhook signature validation

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

User allowlists

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. :::


Troubleshooting

Messages not arriving

  1. Check your Twilio webhook URL is correct and publicly accessible
  2. Verify TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN are correct
  3. Check the Twilio Console → Monitor → Logs → Messaging for delivery errors
  4. Ensure your phone number is in SMS_ALLOWED_USERS (or SMS_ALLOW_ALL_USERS=true)

Replies not sending

  1. Check TWILIO_PHONE_NUMBER is set correctly (E.164 format with +)
  2. Verify your Twilio account has SMS-capable numbers
  3. Check Hermes gateway logs for Twilio API errors

Webhook port conflicts

If port 8080 is already in use, change it:

SMS_WEBHOOK_PORT=3001

Update the webhook URL in Twilio Console to match.