# SMS & WhatsApp Messaging Agents > AI-powered text-based booking agents that handle SMS and WhatsApp conversations, check availability, and book meetings -- powered by Claude AI and Twilio. > Source: https://linktime.io/docs/ai/messaging > Last updated: February 2026 ## Overview Messaging Agents add a conversational AI booking assistant to your LinkTime account via text. The assistant can: - Respond to inbound SMS and WhatsApp messages in real time - Carry multi-turn conversations with full context across messages - List your available meeting types - Check real-time availability on your calendar - Book meetings, create contacts, and send confirmations - Handle STOP/HELP opt-out compliance (required by telecom regulations) ## How It Works ``` Customer texts your Twilio number -> Twilio webhook POST /api/webhooks/twilio/inbound -> Routes to MessagingAgent (state machine + Claude AI) -> Claude processes conversation with tools: -> list_event_types (show meeting types) -> get_available_slots (check calendar) -> create_booking (book meeting) -> Response sent back via Twilio SMS or WhatsApp API ``` Each agent has: name, greeting, tone (professional/friendly/casual/custom), language, linked event types, and a phone number. ### Conversation State Machine Messages flow through a state machine that tracks where the customer is in the booking flow: 1. **Greeting** -- Agent introduces itself 2. **Event selection** -- Customer picks a meeting type 3. **Date/time selection** -- Agent shows available slots 4. **Information collection** -- Name, email, any required fields 5. **Confirmation** -- Booking created, confirmation sent Context carries forward across messages so the customer never has to repeat themselves. ## SMS Agents - Work with any provisioned Twilio phone number - No additional setup beyond phone number provisioning - ~$0.0079/message US rate (varies by country) - STOP/HELP keyword compliance built in - Immediate delivery, no approval process ## WhatsApp Agents - Require WhatsApp Business sender registration on the phone number - Automated registration via Twilio Channels Senders API (v2.44.0) - Meta approval required: 1-3 business days - Template notifications for booking confirmations and reminders - Falls back to SMS when WhatsApp templates are pending or rejected ## WhatsApp Registration (v2.44.0) Self-service flow for enabling WhatsApp on a phone number: 1. Go to Phone Numbers dashboard and click "Enable WhatsApp" on your number 2. Enter your business display name (must follow Meta naming rules) 3. Status changes to **Pending** -- Meta reviews the display name 4. Meta approves or rejects (typically 1-3 business days) 5. Email notification sent on approval or rejection 6. WhatsApp agents can activate once the number is approved ### Status Tracking Two complementary mechanisms ensure status updates are never missed: - **Webhook (instant):** Twilio sends status changes to `/api/webhooks/twilio/whatsapp-sender-status` (signature-verified) - **Cron polling (fallback):** `/api/cron/whatsapp-status` polls every 5 minutes for pending senders, hourly for approved senders (revocation detection) ### API **Register a WhatsApp sender:** ``` POST /api/agents/phone-numbers/{id}/whatsapp Content-Type: application/json { "displayName": "Dr. Smith Dental" } ``` **Check registration status:** ``` GET /api/agents/phone-numbers/{id}/whatsapp GET /api/agents/phone-numbers/{id}/whatsapp?refresh=true (live check from Twilio) ``` **Deregister WhatsApp sender:** ``` DELETE /api/agents/phone-numbers/{id}/whatsapp ``` ### Status Mapping (Twilio to LinkTime) | Twilio Status | LinkTime Status | WhatsApp Enabled | |---------------|----------------|------------------| | CREATING, PENDING_VERIFICATION, VERIFYING, TWILIO_REVIEW, DRAFT, STUBBED | pending | No | | ONLINE, ONLINE:UPDATING | approved | Yes | | OFFLINE (with senderSid) | rejected | No | ## Display Name Rules (Meta Requirements) Your WhatsApp business display name must follow Meta's naming policy: **Must:** - Be 3-256 characters - Contain at least one letter - Represent your actual business name **Must NOT contain:** - Special characters: `~!@#$%^&*()_+:;"'{}[]|<>,/?` - URLs or web addresses - Meta product names: WhatsApp, Facebook, Instagram, Messenger - "Official" or "Verified" - Generic terms alone (e.g., just "Store" or "Business") **Valid examples:** "Dr. Smith Dental", "Acme Corp", "Main Street Salon" **Invalid examples:** "WhatsApp Business", "Official Store", "test@company" ## Phone Numbers Phone numbers are shared infrastructure -- one number can serve voice agents, SMS agents, and WhatsApp agents simultaneously. ### Provisioning ``` POST /api/agents/phone-numbers Content-Type: application/json { "countryCode": "US", "label": "Main Office" } ``` The `countryCode` parameter accepts any of the 14 supported countries (ISO 3166-1 alpha-2 code). LinkTime searches for a local number first, then falls back to toll-free if unavailable. | Country | Code | Dial Code | Approx. Cost | |---------|------|-----------|-------------| | United States | US | +1 | ~$1/mo | | Canada | CA | +1 | ~$1/mo | | United Kingdom | GB | +44 | ~$1/mo | | France | FR | +33 | ~$1/mo | | Germany | DE | +49 | ~$1/mo | | Spain | ES | +34 | ~$1/mo | | Netherlands | NL | +31 | ~$1/mo | | Italy | IT | +39 | ~$1/mo | | Australia | AU | +61 | ~$2/mo | | Brazil | BR | +55 | ~$3/mo | | Mexico | MX | +52 | ~$2/mo | | Japan | JP | +81 | ~$5/mo | | Singapore | SG | +65 | ~$3/mo | | UAE | AE | +971 | ~$5/mo | > **Full country data:** See `src/lib/countries.ts` (`PHONE_COUNTRIES` array) for flag emojis and SMS inbound capabilities. ### Releasing ``` DELETE /api/agents/phone-numbers/{id} ``` Released numbers enter a **30-day hold period** before being recycled by Twilio. WhatsApp sender registration is automatically deregistered on release. ## Plan Limits | Plan | Phone Numbers | SMS/WhatsApp Agents | Monthly Messages | |------|--------------|--------------------|--------------------| | Free | 0 | 0 | 0 | | Pro | 1 | 2 per channel | 100 | | Business | 5 | 10 per channel | 500 | | Enterprise | 20 | 50 per channel | Unlimited | > **Shared message pool:** Your monthly message quota is shared across all SMS and WhatsApp agents. If you have 100 messages on the Pro plan, those 100 messages are consumed by conversations with any of your agents across both channels. ## Agent API All endpoints require Clerk session authentication. ### Phone Numbers | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/api/agents/phone-numbers` | List all phone numbers | | POST | `/api/agents/phone-numbers` | Provision a new number | | GET | `/api/agents/phone-numbers/{id}` | Get number details | | PUT | `/api/agents/phone-numbers/{id}` | Update number settings | | DELETE | `/api/agents/phone-numbers/{id}` | Release number (30-day hold) | ### WhatsApp Registration | Method | Endpoint | Description | |--------|----------|-------------| | POST | `/api/agents/phone-numbers/{id}/whatsapp` | Register WhatsApp sender | | GET | `/api/agents/phone-numbers/{id}/whatsapp` | Check registration status | | DELETE | `/api/agents/phone-numbers/{id}/whatsapp` | Deregister WhatsApp sender | ### Messaging Agents | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/api/agents/messaging` | List all messaging agents | | POST | `/api/agents/messaging` | Create a new agent | | GET | `/api/agents/messaging/{id}` | Get agent details | | PUT | `/api/agents/messaging/{id}` | Update agent settings | | DELETE | `/api/agents/messaging/{id}` | Delete agent | ### Create Agent Example ```bash curl -X POST https://linktime.io/api/agents/messaging \ -H "Content-Type: application/json" \ -H "Cookie: __session=" \ -d '{ "name": "Sarah", "greeting": "Hi! I'\''m Sarah, Emmanuel'\''s scheduling assistant. How can I help?", "tone": "friendly", "language": "en", "phoneNumberId": "cm_phone_abc...", "channel": "sms" }' ``` ### Update Agent Example ```bash curl -X PUT https://linktime.io/api/agents/messaging/cm_agent_abc... \ -H "Content-Type: application/json" \ -H "Cookie: __session=" \ -d '{ "name": "Alex", "tone": "professional", "greeting": "Hello, this is Alex from LinkTime. How may I assist you?" }' ``` ## Architecture ### Key Components | Component | File | Purpose | |-----------|------|---------| | State Machine | `src/lib/messaging-state-machine.ts` | Conversation state transitions, tool preconditions, context schema | | Core Agent | `src/lib/messaging-agent.ts` | Conversational loop, rate limiter, circuit breaker, cost tracking | | Prompt Builder | `src/lib/messaging-agent-prompt.ts` | System prompt with context carry-forward and personality guardrails | | Tool Adapter | `src/lib/messaging-tools.ts` | Wraps voice-tools for Claude tool_use format, timezone resolution, idempotency | | Phone Provisioning | `src/lib/messaging-phone.ts` | Buy, configure smsUrl, release with 30-day hold | | WhatsApp Sender | `src/lib/whatsapp-sender.ts` | Twilio Senders API wrapper, validation, status mapping, notifications | | Inbound Webhook | `src/app/api/webhooks/twilio/inbound/route.ts` | STOP/HELP compliance + messaging agent routing | ### Tools Available to Claude The messaging agent uses the same underlying tool handlers as the voice agent: | Tool | Description | |------|-------------| | `list_event_types` | Returns the user's active, non-secret event types | | `get_available_slots` | Checks real-time calendar availability for a date | | `create_booking` | Creates a confirmed booking and sends confirmation | ### Safety Features - **Rate limiter:** Per-phone-number rate limiting to prevent abuse - **Circuit breaker:** Automatic fallback when Claude AI is unavailable - **Cost tracking:** Per-message cost logged for billing reconciliation - **Idempotency:** Duplicate message detection prevents double-bookings ## Security ### Twilio Signature Verification All inbound webhooks verify the `x-twilio-signature` header using `validateRequest()` from the Twilio SDK. This ensures messages genuinely originate from Twilio. ### WhatsApp Sender Status Webhook Sender registration status callbacks at `/api/webhooks/twilio/whatsapp-sender-status` use the same Twilio signature verification. ### STOP/HELP Compliance Required by US telecom regulations (TCPA/CTIA guidelines): - **STOP:** Immediately opts out the sender. No further messages are sent. Acknowledged with "You've been unsubscribed." - **HELP:** Returns contact information and opt-out instructions. - Opt-out status is checked before every outbound message (SMS and WhatsApp). - Opt-out records are stored in the `SmsOptOut` table and shared across all channels. ### User Data Isolation Each messaging agent is scoped to its owner's data. An agent can only access its owner's event types, calendar, and bookings. Cross-user data access is impossible by design. ### Rate Limiting Per-user rate limiting prevents excessive API usage. Limits are enforced at the inbound webhook level. ### Circuit Breaker If Claude AI becomes unavailable, the circuit breaker trips after consecutive failures. During this period, inbound messages receive a fallback response directing the sender to book online at the user's booking URL. ### Admin Kill Switch The `MESSAGING_AGENT_ENABLED` environment variable acts as a global kill switch. When set to `false`, all messaging agents stop responding. Individual agents can be disabled via the dashboard or API. ## Troubleshooting ### "WhatsApp API not yet available" Twilio requires at least one WhatsApp sender registered manually via Twilio Console before the API is available. This is a one-time setup per Twilio account. Error code 21656 is caught and displayed as a clear admin message. ### WhatsApp stuck on "Pending" Meta typically reviews display names within 1-3 business days. Click "Refresh Status" to poll Twilio for the latest. If stuck for 5+ days, try deregistering and re-registering with a different display name. ### WhatsApp "Rejected" The display name did not meet Meta's naming policy. Click "Try Again" and enter a different name that follows the display name rules above. ### Agent not responding to messages Check the following: 1. Agent is set to **active** in the dashboard 2. Phone number is provisioned and correctly linked 3. WhatsApp sender is **approved** (if using WhatsApp channel) 4. Sender has not opted out (check `SmsOptOut` table) 5. `MESSAGING_AGENT_ENABLED` is not set to `false` 6. `ANTHROPIC_API_KEY` is valid ### Messages failing to send Check Twilio geo-permissions for the destination country. Go to Twilio Console > Messaging > Settings > Geo Permissions and enable the target countries. ### "Circuit breaker open" in logs Claude AI experienced consecutive failures. The circuit breaker will automatically reset after a cooldown period. Check the `ANTHROPIC_API_KEY` is valid and the Anthropic API status page for outages. ### Duplicate bookings This should not happen -- the tool adapter includes idempotency checks. If it does occur, check for duplicate inbound webhook deliveries from Twilio and verify the rate limiter is functioning. ## Environment Variables | Variable | Required | Description | |----------|----------|-------------| | `TWILIO_ACCOUNT_SID` | Yes | Twilio Account SID ([Twilio Console](https://console.twilio.com)) | | `TWILIO_AUTH_TOKEN` | Yes | Twilio Auth Token | | `ANTHROPIC_API_KEY` | Yes | Claude AI API key for conversation processing ([Anthropic Console](https://console.anthropic.com)) | | `MESSAGING_AGENT_ENABLED` | No | Global kill switch for all messaging agents (default: `true`) | | `ADMIN_KILL_SECRET` | No | Secret token for admin override endpoints (min 8 chars) |