Getting Started
Prerequisites
- Node.js 22+ and npm
- Twilio account — for SMS, voice calls, and WhatsApp
- Resend account (optional) — for email
- ElevenLabs account (optional) — for premium TTS (free Edge TTS is the default)
Installation
git clone https://github.com/95percent-ai/butt-dial.git
cd butt-dial
npm install
cp .env.example .env
Configuration
Minimum (Demo Mode)
PORT=3100
DEMO_MODE=true
Demo mode uses mock providers — no real API calls, no costs.
Production
PORT=3100
DEMO_MODE=false
Required for SMS/voice
TWILIO_ACCOUNT_SID=AC...
TWILIO_AUTH_TOKEN=...
Required for email
RESEND_API_KEY=re_...
Optional premium TTS (falls back to free Edge TTS)
ELEVENLABS_API_KEY=sk_...
Security — used to encrypt provider credentials at rest
CREDENTIALS_ENCRYPTION_KEY=a-32-character-or-longer-secret
WEBHOOK_BASE_URL=https://your-domain.com
See .env.example for all available options.
Build & Run
npm run build
npm run seed # Creates a test client (test-client-001)
node dist/index.js
The server starts on port 3100 (default).
Web Setup Wizard
Visit http://localhost:3100/admin/setup for guided configuration:
- Enter Twilio credentials → click Test Connection → auto-saves on success
- Enter ElevenLabs key → test → auto-saves
- Enter Resend key → test → auto-saves
- Configure server settings (webhook URL, orchestrator token)
- Set voice defaults (greeting, language, voice ID)
Each card validates credentials live before saving.
Verify
curl http://localhost:3100/health
Returns: {"status":"ok","uptime":...,"version":"0.1.0"}
Expose Webhooks (Development)
For inbound messages and calls to reach your local server, expose it publicly:
ngrok http 3100
Copy the HTTPS URL to WEBHOOK_BASE_URL in .env
Connect an AI Client
Your AI client connects via MCP over SSE:
GET http://localhost:3100/sse?token=<client-security-token>
token— Security token generated during provisioningagentId— Registers the client session for voice routing and message delivery
Once connected, your MCP client can list and call all available tools. See MCP Tools for the full reference.
Inbound Message Flow
When someone texts, emails, or WhatsApps your client's number:
- The server receives the webhook from Twilio/Resend/GreenAPI/LINE
- Validates the signature
- Checks channel blocking — blocked channels are silently dropped
- POSTs the message to your client's
callbackUrl - If the callback fails, stores it in the dead letter queue (24h hold)
Inbound messages arrive at your callback as:
{ "type": "message", "agentName": "My Bot", "channel": "whatsapp", "from": "+1555...", "senderName": "John", "body": "Hey!", "timestamp": "..." }
Group messages include additional fields:
{ "type": "message", "agentName": "My Bot", "channel": "whatsapp", "from": "+1555...", "senderName": "John", "groupId": "120363XXXXX@g.us", "groupName": "Sales Team", "isGroup": true, "body": "Hey everyone!", "timestamp": "..." }
To reply, always use the replyTo field as your to target — it's the group ID for group messages, or the sender's phone for DMs.
To list group members: comms_get_group_members({ groupId: "120363XXXXX@g.us" }) — returns phone numbers, admin status, and display names. You can then DM any member directly.
Delivery status updates also arrive at your callback:
{ "type": "status", "agentName": "My Bot", "messageId": "abc-123", "status": "delivered", "timestamp": "..." }
If your callback is down, fetch missed messages with GET /api/v1/waiting-messages.
For voice calls, the server relays the caller's speech as text to your client via MCP sampling, and your client's text response is spoken back to the caller.
Startup Warnings
| Warning | Meaning |
|---|---|
| No Twilio credentials | Telephony channels use mock adapters |
| No Resend API key | Email uses mock adapter |
| Webhook URL is localhost | Inbound webhooks won't work externally |
| No orchestrator security token | Tool calls are unauthenticated |
| No ElevenLabs key | Using free Edge TTS (not an error) |
| No Anthropic key | Answering machine disabled |
These are warnings, not errors. The server always starts.