All posts
Building a 2FA Bot with Claude Code and Dial
TutorialBy Dial Engineering·8 min read·April 7, 2026·38

Building a 2FA Bot with Claude Code and Dial

A step-by-step tutorial on automating SMS verification workflows. We'll sign up for a real service, intercept the OTP, and complete verification — all from a Claude Code session.


This tutorial walks through building a fully automated SMS verification workflow using Claude Code and Dial. By the end, your agent will be able to sign up for a service, receive the OTP, and complete verification — without any human input.


What we're building

1. Claude Code gets a Dial phone number

2. The agent uses it to sign up for an SMS-gated service

3. Dial captures the inbound OTP

4. Claude reads it and completes verification


Step 1 — Set up Dial

Add the Dial MCP server to Claude Code:

{
  "mcpServers": {
    "dial": {
      "url": "https://getdial.ai/mcp",
      "headers": {
        "Authorization": "Bearer sk_live_..."
      }
    }
  }
}

Or use the REST API directly — no MCP configuration needed.


Step 2 — Provision a number

curl -X POST https://api.getdial.ai/v1/numbers \
  -H "Authorization: Bearer $DIAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"country": "US"}'

Response:

{
  "id": "clxxx...",
  "number": "+14155551234",
  "capabilities": "voice,sms,whatsapp"
}

Step 3 — Start waiting for the OTP before triggering the signup

This is the key insight: start the long-poll before you trigger the SMS, not after. Otherwise you risk a race condition where the message arrives before your listener is active.

// Start listening FIRST
const waitPromise = fetch("https://api.getdial.ai/v1/messages/wait", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.DIAL_API_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    phone_number_id: "clxxx...",
    timeout: 30
  })
});

// Then trigger the signup that will send the SMS
await triggerSignup("+14155551234");

// Now resolve the OTP
const result = await waitPromise;
const { body } = await result.json();

// Extract 6-digit code
const otp = body.match(/d{6}/)?.[0];
console.log("OTP received:", otp);

Step 4 — Complete verification

Pass the OTP to the service's verification endpoint. The exact call depends on the service, but the pattern is always the same: extract the code, POST it, done.


How Claude handles this natively

When you give Claude Code access to Dial via MCP, it figures out the pattern on its own. Just say:

> "Sign me up for [service] using a Dial phone number for SMS verification"

Claude will:

1. Call list_numbers to find an available number (or provision one)

2. Start wait_for_message before submitting the form

3. Complete the signup flow

4. Read the OTP from the returned message

5. Submit the verification code

No code written by you. No human in the loop.


Caveats

  • Dial blocks calls to emergency services and known abuse numbers
  • Some services flag VoIP numbers — international numbers often bypass this
  • The wait endpoint has a max timeout of 30 seconds — chain calls if you need longer

  • Next steps

  • Add a webhook to handle inbound messages in real time (no polling)
  • Attach a voice agent so the number can also handle calls
  • Use the same number for WhatsApp if your service supports it
  • Did you enjoy this post?

    38 claps