← All posts
How-ToAugust 31, 2026 · 6 min

Automated Cold Calling for B2B Leads with Vapi + OpenAI: A Step-by-Step Build

You can spin up a fully hands-free outbound dialer that greets prospects, answers objections, and writes the outcome straight into your CRM - all without hiring additional SDRs. In this guide I'll show you how to connect Vapi's voice-AI gateway to OpenAI's conversational model, enrich the call with ElevenLabs TTS, and push results through a webhook to a CRM of your choice. By the end you'll have a production-ready pipeline that can run 50-100 calls per hour and feed qualified leads directly into your funnel.

Voice AI agent is a software service that receives a phone call, turns the caller's speech into text, runs that text through a language model, and replies with synthesized speech, all in real time.


What you need

ToolPlan / Price*Role
Docker Engine (Community Edition)Free (self-hosted)Container runtime for the whole stack
VapiPay-as-you-go (check Vapi's current pricing)Inbound/outbound telephony, SIP bridge, call event webhook
OpenAI APIPay-as-you-go (check OpenAI's current pricing)Generates dynamic dialog and lead-qualification logic
ElevenLabs TTSPay-as-you-go (check ElevenLabs pricing)High-quality voice synthesis for the agent
Twilio Programmable VoicePay-as-you-go (check Twilio's current pricing)Provides a public phone number that forwards to Vapi
n8n (open-source workflow engine)Free (self-hosted)Orchestrates webhook handling, API calls, and CRM updates
PostgreSQL (or any SQL DB)Free (self-hosted)Persists call transcripts and scoring metadata
Your CRM (HubSpot, Pipedrive, Salesforce, etc.)Whatever plan you already haveStores qualified lead records
ngrok (optional for local dev)Free tier available (check ngrok's current pricing)Exposes local webhook endpoints during development

\*Pricing notes are deliberately vague; always verify the latest rates on the provider's website before committing budget.

Rough build time: 8-12 hours for a developer comfortable with Docker and REST APIs.


Build it

1. Spin up the infrastructure

Create a Docker-Compose file that launches n8n, PostgreSQL, and a tiny Reverse Proxy (Caddy) to expose the webhook endpoint securely.

yaml
# docker-compose.yml
version: "3.8"
services:
 db:
 image: postgres:15
 environment:
 POSTGRES_USER: aab_user
 POSTGRES_PASSWORD: aab_pass
 POSTGRES_DB: aab_calls
 volumes:
 - db_data:/var/lib/postgresql/data

 n8n:
 image: n8nio/n8n:latest
 ports:
 - "5678:5678"
 environment:
 - DB_TYPE=postgresdb
 - DB_POSTGRESDB_HOST=db
 - DB_POSTGRESDB_PORT=5432
 - DB_POSTGRESDB_DATABASE=aab_calls
 - DB_POSTGRESDB_USER=aab_user
 - DB_POSTGRESDB_PASSWORD=aab_pass
 depends_on:
 - db

 caddy:
 image: caddy:2
 ports:
 - "443:443"
 volumes:
 - ./Caddyfile:/etc/caddy/Caddyfile
 depends_on:
 - n8n

volumes:
 db_data:

This file creates three containers: a PostgreSQL instance for persisting call data, n8n as the orchestrator, and Caddy to terminate TLS for the public webhook URL.

Run docker compose up -d. Verify each service is reachable (docker ps should list three containers).

2. Register a Twilio phone number

Log into the Twilio console, buy a local number, and point its Voice → Webhooks URL to the Caddy-exposed endpoint, e.g.:

https://yourdomain.com/webhook/vapi

Set the request method to POST and enable Webhook for both Incoming Calls and Call Status.

3. Connect Twilio ↔ Vapi

Create a Vapi Outbound Flow that uses the Twilio number as the source. In the Vapi dashboard go to Flows → New Flow → Outbound and set:

  • Source Number: the Twilio number you just bought
  • Destination: dynamic; will be supplied by n8n when the workflow triggers
  • Callback URL: https://yourdomain.com/webhook/vapi (the same endpoint you configured in Twilio)

Vapi will POST a JSON payload for each call event (ringing, answered, speech, etc.) to that URL.

4. Design the n8n workflow

  1. Webhook Node - listens on /webhook/vapi. Set Response Mode to "Immediately respond 200 OK" so Twilio/Vapi don't time out.
  2. IF Node - checks event.type field. If event.type === "answer" proceed; otherwise ignore.
  3. HTTP Request Node (Speech-to-Text) - call Vapi's /speech-to-text endpoint, passing event.recording_url. Store result in {{ $json["transcript"] }}.
  4. OpenAI Node - prompt the latest transcript with a system prompt that encodes your sales script. Example prompt (see code block below).
  5. ElevenLabs TTS Node - feed OpenAI's reply text, receive an audio URL, and return it to Vapi via a HTTP Request PUT /call/{callId}/audio.
  6. PostgreSQL Node - INSERT call metadata (caller, transcript, AI reply, confidence score) into calls table.
  7. CRM HTTP Node - POST qualified lead JSON to your CRM's REST endpoint (HubSpot contacts, for example).

#### OpenAI Prompt (Step 4)

json
{
 "model": "gpt-4o-mini",
 "messages": [
 {
 "role": "system",
 "content": "You are a friendly B2B sales rep for a SaaS company that offers a data-enrichment platform. Your goal is to qualify the prospect, capture company name, size, and pain point, and schedule a 30-minute demo if interest is shown. Keep the conversation under 90 seconds."
 },
 {
 "role": "assistant",
 "content": "Hi, this is Alex from DataBoost. I'm calling because we help companies like {{company_name}} improve lead quality. Do you have a minute to see if we might help?"
 },
 {
 "role": "user",
 "content": "{{transcript}}"
 }
 ],
 "temperature": 0.2,
 "max_tokens": 200
}

The JSON is sent directly to https://api.openai.com/v1/chat/completions. The response's choices[0].message.content becomes the text that ElevenLabs will speak back to the prospect.

5. Deploy the webhook in Caddy

Create a Caddyfile that terminates TLS with Let's Encrypt and forwards /webhook/vapi to the n8n container:

caddy
yourdomain.com {
 encode gzip
 reverse_proxy /webhook/vapi n8n:5678
}

Reload Caddy (docker exec caddy caddy reload). Verify the endpoint works with a simple curl -X POST https://yourdomain.com/webhook/vapi -d '{}'. You should receive 200 OK.

6. Test end-to-end

  1. Use Twilio's Call Simulator (or dial the number from a real phone).
  2. When the call connects, Vapi streams inbound audio to the webhook.
  3. n8n converts speech → text → OpenAI → ElevenLabs → Vapi, completing a round-trip conversation.
  4. After the call, check PostgreSQL (SELECT * FROM calls ORDER BY created_at DESC LIMIT 1;) and your CRM dashboard for a new lead entry.

If everything logs correctly, you have a functional automated cold calling for b2b leads pipeline.

7. Add lead scoring

Create a second IF Node after the OpenAI response that looks for keywords like "interested", "demo", "budget". Set a numeric score (e.g., 80) when those appear; otherwise assign a lower score (20). Store the score in the PostgreSQL row and send it to the CRM as a custom field. This enables your sales team to prioritize follow-ups automatically.


Where this breaks

Failure modeSymptomFix
Vapi webhook timeoutTwilio logs "504 Gateway Timeout".Ensure the n8n webhook node is set to Immediately respond 200 OK; handle heavy processing in downstream nodes, not the initial response.
Speech-to-text latencyProspect hears awkward pauses >3 s.Cache the OpenAI response locally for identical utterances, or pre-record short greetings with ElevenLabs and play them while waiting for the AI reply.
OpenAI rate limits429 "Too Many Requests" errors in n8n logs.Implement an exponential back-off in the OpenAI HTTP node; keep calls under the provider's per-minute limit (check OpenAI's current docs).
Twilio cost surpriseUnexpected per-minute charges appear on the bill.Monitor Twilio usage daily; set alerts in the Twilio console for spend > $20.
CRM field mismatchLeads appear with empty "Company" field.Verify the JSON payload matches the CRM's required schema; use the CRM's Test API endpoint to inspect field names.
Docker container crashesdocker ps shows n8n exiting with code 1.Check docker logs n8n; most crashes stem from missing env vars (POSTGRES_PASSWORD). Add missing vars to the compose file and restart.
TLS certificate renewal failureHTTPS requests return 502 after 90 days.Caddy auto-renews, but only if port 80 is reachable. Ensure firewall rules allow inbound traffic on 80/443.

Key warning: The whole chain is only as reliable as its slowest link. In practice, the OpenAI request adds ~500 ms of latency; budget extra buffer in your call flow to avoid breaking the natural conversation rhythm.


For a deeper technical reference, see n8n's documentation.

Frequently asked questions

How many calls can this setup handle per hour?

The bottleneck is usually the OpenAI token throughput and Twilio's per-minute pricing. In our production sandbox a single n8n instance comfortably processes ~70 calls/hour while staying under typical OpenAI rate limits. Scale horizontally by running multiple n8n workers behind a load balancer.

Do I need a separate SIP trunk for each outbound call?

No. Vapi manages SIP sessions internally. You only need one Twilio number as the outbound caller ID; Vapi will negotiate the SIP leg for each destination automatically.

Can I replace ElevenLabs with another TTS provider?

Absolutely. The n8n workflow uses a generic HTTP Request node, so you can swap the URL, payload, and auth header to point at Google Cloud Text-to-Speech, Amazon Polly, or any self-hosted model. Just adjust the response parsing accordingly.

What if a prospect hangs up mid-dialog?

The Vapi event payload includes a `call.status = "completed"` flag. Add an IF node after the webhook that checks for `status !== "completed"` and writes a "no answer" record to PostgreSQL. This lets you later re-dial or flag the number as low-quality.

Is it possible to run this entirely on-prem without any cloud services?

You can self-host Docker, n8n, and PostgreSQL, but you still need a telephony provider (Twilio or any SIP-compatible carrier) and a language model. OpenAI offers a self-hosted option for enterprise customers, but pricing and availability require a direct sales conversation.

Where can I learn more about selling AI-driven automations?

Check out the article AI automations you can sell for a curated list of ready-made services, and grab the free guide to jump-start your first paid automation project. --- Building an automated cold calling for b2b leads system may sound ambitious, but with Vapi's telephony layer, OpenAI's conversational intelligence, and n8n's glue logic you can get a reliable pipeline up in a single day. Keep an

Get the full toolkit

Grab the free guide with the node-by-node build for all 10 automations.

No spam. Unsubscribe anytime. Just the good stuff.