Your setup
voice AI guide
Step 4 of 7
Step 04 · The summons

Wire up WhatsApp.

Two parts. First, connect the Twilio WhatsApp Sandbox so your phone can message the agent. Second, deploy a small piece of code on Cloudflare that bridges them. About thirty minutes.

This is the longest step. Take a breath. You will not break anything. If you get stuck, walk away and come back.

Part A: Join the Twilio WhatsApp Sandbox

The Sandbox is the fastest way to get WhatsApp working with Twilio. The only quirk: each WhatsApp number that wants to message your agent has to send a one-time join code first. For personal use, you only do this once on your own phone.

  1. Open Chrome. Go to console.twilio.com.
  2. In the left sidebar, click Messaging, then Try it out, then Send a WhatsApp message.
  3. You will see a Sandbox WhatsApp number (something like +1 415 523 8886) and a join code that looks like join coffee-bear. Yours will be different. Copy both.
  4. Open WhatsApp on your phone. Start a new chat with the Sandbox number.
  5. Send the exact join code as a message. You should get a confirmation reply within a few seconds. ("Twilio Sandbox: You are all set!")
  6. Stay on the Twilio Sandbox page in Chrome. We will paste a webhook URL here in a few minutes.

Your phone is now connected to Twilio. You can WhatsApp the sandbox number and Twilio sees it. Next we tell Twilio what to do with those messages.

Part B: Deploy the connector

This is a small piece of code that listens for your WhatsApp messages and tells your voice agent what to do. Cloudflare Workers hosts it. Free tier covers way more than personal use.

Pick the route. The toggle remembers your choice for the rest of the guide.

Browser route · Cloudflare dashboard, point and click

You will create a Worker through Cloudflare's web dashboard, paste in the code below, set five secrets, and copy the URL. About fifteen minutes.

Create the Worker

  1. Open Chrome. Go to dash.cloudflare.com. Sign in.
  2. In the left sidebar, click Workers & Pages. (If your account has multiple, click your account first.)
  3. Click the orange Create application button. Pick the Create Worker tab.
  4. Name it voice-bot. Click Deploy. Cloudflare deploys a placeholder "Hello World" worker. This is fine.
  5. On the success page, click Edit code in the top right. The browser code editor opens with the placeholder code.
  6. Select all the existing code (Cmd+A on Mac, Ctrl+A on Windows). Delete it.
  7. Paste the code block below into the editor.
  8. Click Save and Deploy in the top right. Confirm the deploy.
export default {
  async fetch(request, env) {
    if (request.method !== "POST") return new Response("OK");

    const form = await request.formData();
    const message = (form.get("Body") || "").toString();
    const from = (form.get("From") || "").toString();
    if (!message || !from) return twiml("");

    const match = message.match(/call\s+(\+\d+)\s+about\s+(.+)/i);
    if (!match) return twiml("Format: call +1xxx about <objective>");
    const [, toNumber, objective] = match;

    await fetch(
      "https://api.elevenlabs.io/v1/convai/twilio/outbound-call",
      {
        method: "POST",
        headers: {
          "xi-api-key": env.ELEVENLABS_API_KEY,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          agent_id: env.ELEVENLABS_AGENT_ID,
          agent_phone_number_id: env.ELEVENLABS_AGENT_PHONE_ID,
          to_number: toNumber,
          conversation_initiation_client_data: {
            dynamic_variables: {
              objective,
              your_name: env.YOUR_NAME,
              agent_name: env.AGENT_NAME,
            },
          },
        }),
      }
    );
    return twiml(`${env.AGENT_NAME} is calling ${toNumber} now.`);
  },
};

function twiml(reply) {
  const body = `<Response>${reply ? `<Message>${escape(reply)}</Message>` : ""}</Response>`;
  return new Response(body, { headers: { "Content-Type": "text/xml" } });
}

function escape(s) {
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

Add the secrets

The Worker needs five values to run: your ElevenLabs API key, your Agent ID, your Phone Number ID, your name, and your agent's name.

  1. Click the back arrow or breadcrumb at the top to return to the Worker overview.
  2. Click Settings, then Variables and Secrets.
  3. Click Add. Type variable name: ELEVENLABS_API_KEY. Paste your EL API key as the value. Set Type to Secret. Click Save.
  4. Click Add again. Add ELEVENLABS_AGENT_ID with your agent ID as the value (Secret).
  5. Click Add again. Add ELEVENLABS_AGENT_PHONE_ID with your phone number ID (Secret).
  6. Click Add. Add YOUR_NAME with your name as a plain text variable (or Secret, your choice).
  7. Click Add. Add AGENT_NAME with the name you picked for your agent.

Get the Worker URL

  1. Click Overview on the Worker page.
  2. Find the URL near the top. It looks like https://voice-bot.yourname.workers.dev.
  3. Copy the URL.
Terminal route · wrangler CLI

Faster if you are comfortable with a shell. Make sure you installed Node.js back in step 1.

Install wrangler and create the project

npm install -g wrangler
wrangler login
wrangler init voice-bot
cd voice-bot

When prompted, pick "Hello World example", JavaScript, and yes to git/dependencies.

Replace the worker code

Open src/index.js in your editor (or run code src/index.js if you have VS Code). Replace its contents with:

export default {
  async fetch(request, env) {
    if (request.method !== "POST") return new Response("OK");
    const form = await request.formData();
    const message = (form.get("Body") || "").toString();
    const from = (form.get("From") || "").toString();
    if (!message || !from) return twiml("");

    const match = message.match(/call\s+(\+\d+)\s+about\s+(.+)/i);
    if (!match) return twiml("Format: call +1xxx about <objective>");
    const [, toNumber, objective] = match;

    await fetch(
      "https://api.elevenlabs.io/v1/convai/twilio/outbound-call",
      {
        method: "POST",
        headers: {
          "xi-api-key": env.ELEVENLABS_API_KEY,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          agent_id: env.ELEVENLABS_AGENT_ID,
          agent_phone_number_id: env.ELEVENLABS_AGENT_PHONE_ID,
          to_number: toNumber,
          conversation_initiation_client_data: {
            dynamic_variables: {
              objective,
              your_name: env.YOUR_NAME,
              agent_name: env.AGENT_NAME,
            },
          },
        }),
      }
    );
    return twiml(`${env.AGENT_NAME} is calling ${toNumber} now.`);
  },
};

function twiml(reply) {
  const body = `<Response>${reply ? `<Message>${escape(reply)}</Message>` : ""}</Response>`;
  return new Response(body, { headers: { "Content-Type": "text/xml" } });
}

function escape(s) {
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

Set the secrets

Wrangler will prompt you for each value:

wrangler secret put ELEVENLABS_API_KEY
wrangler secret put ELEVENLABS_AGENT_ID
wrangler secret put ELEVENLABS_AGENT_PHONE_ID
wrangler secret put YOUR_NAME
wrangler secret put AGENT_NAME

Deploy

wrangler deploy

Wrangler prints a URL like https://voice-bot.yourname.workers.dev. Copy it.

Connect the Worker to Twilio

Last step of this section. Glue the Worker URL into the Twilio Sandbox webhook field.

  1. Go back to the Twilio Sandbox tab in Chrome (the one from Part A).
  2. Click the Sandbox settings tab inside the Sandbox page.
  3. Find the field When a message comes in.
  4. Paste your Worker URL into that field.
  5. Set the dropdown next to it to HTTP POST.
  6. Click Save at the bottom.

Test the loop

All three layers are now connected: WhatsApp, Twilio, your Worker, ElevenLabs.

  1. Open WhatsApp on your phone. Open the chat with the Twilio Sandbox number.
  2. Send: call +1xxxxxxxxxx about confirming Friday's reservation (replace +1xxxxxxxxxx with your own mobile in international format).
  3. Within five seconds, you should get a WhatsApp reply saying "[your agent's name] is calling [your number] now."
  4. Within ten seconds, your phone rings. Pick up. Have a brief conversation. Hang up.

Your agent now lives on WhatsApp. The hard part is over.

Next we add memory so it remembers prior calls.