Getting Started

Prerequisites

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:

  1. Enter Twilio credentials → click Test Connection → auto-saves on success
  2. Enter ElevenLabs key → test → auto-saves
  3. Enter Resend key → test → auto-saves
  4. Configure server settings (webhook URL, orchestrator token)
  5. 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>

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:

  1. The server receives the webhook from Twilio/Resend/GreenAPI/LINE
  2. Validates the signature
  3. Checks channel blocking — blocked channels are silently dropped
  4. POSTs the message to your client's callbackUrl
  5. 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

WarningMeaning
No Twilio credentialsTelephony channels use mock adapters
No Resend API keyEmail uses mock adapter
Webhook URL is localhostInbound webhooks won't work externally
No orchestrator security tokenTool calls are unauthenticated
No ElevenLabs keyUsing free Edge TTS (not an error)
No Anthropic keyAnswering machine disabled

These are warnings, not errors. The server always starts.

← Home