> For the complete documentation index, see [llms.txt](https://docs.voiceb.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.voiceb.ai/product-guides/outbound-campaigns-guide.md).

# Outbound Campaigns Guide

By Alex Bisbe. September 8th.

> **Module:** Outbound Reactivation (Enterprise only)\
> **Requires:** A CRM integration to fire triggers · your own opted-in database — VoiceB never dials cold, purchased lists.

This guide walks through how to build, launch, and operate an outbound campaign in VoiceB: how a call gets triggered from your own CRM, how VoiceB works around Spain's new "400" commercial-numbering rule, how the low outbound pickup rate is offset with an SMS bridge, and how outcomes flow back into HubSpot, Salesforce, or your own webhook.

***

## What the Outbound Reactivation module is — and isn't

The Outbound module lets an Enterprise client trigger autonomous calls from their own CRM instead of waiting for an inbound lead. It is built for **reactivation of an existing, opted-in database** — customers who filled a form, gave consent, or have an active commercial relationship. It is explicitly **not** a cold-calling or purchased-list tool.

What's included:

* Trigger API with automatic retries to maximize contact ratio
* All post-call features: deterministic outcome classification, CRM write-back
* Multi-language agents
* Runs strictly on the client's own opted-in database

What VoiceB does **not** commit to on outbound: we guarantee the mechanism runs flawlessly (trigger, retry, SMS bridge, opt-out, CRM sync). We do not commit to a fixed conversion rate or AI-response rate the way we sometimes benchmark inbound, because outbound pickup behavior is inherently less predictable and is about to get harder still under the October 2026 numbering rules described below.

## Architecture at a glance

```mermaid
flowchart LR
    A[Client CRM / Backend] -- "1. POST /api/click-to-call" --> B[VoiceB Click-to-Call Agent]
    B -- "2. Places first call immediately" --> C[Lead's phone]
    C -- "3. No answer" --> D[Retry Policy schedules next attempt]
    D -- "4. Warm-up SMS fires ahead of the scheduled retry" --> C
    D -- "5. Places retry call" --> C
    C -- "6. Call ends -> Outcome Engine classifies" --> E[Outcome + Transcript + Collected Data]
    C -- "7. Lead calls the inbound number from the SMS, or opts out" --> F[Inbound callback number]
    E -- "8. Push outcome" --> G[HubSpot / Salesforce / Webhook]
    F -- "9. Opt-out logged" --> G
```

The one part clients get wrong most often is treating the SMS as a separate "post-call" system. It isn't — it's the same Warm-up SMS feature, just attached to a *scheduled* call rather than the immediate first one.

The other two recurring mistakes are:

* Triggering from a system that doesn't already have consent
* Forgetting that starting October 17, 2026 in Spain, the number the call comes from can no longer receive a call back at all — which is exactly why the SMS bridge matters so much

## Triggering and scheduling calls from your own system

### Configure the agent as Click to Call

Outbound triggering is configured **at the agent level**, not globally:

{% stepper %}
{% step %}

## Open the agent configuration

Open the agent's configuration screen in Agent Studio.
{% endstep %}

{% step %}

## Set the agent type

Set **Type** to `Click to Call`.
{% endstep %}

{% step %}

## Save the configuration

Click **Save**.
{% endstep %}

{% step %}

## View credentials

A **"Show Credentials & Instructions"** button appears. This reveals the API Token, Secret, and endpoint, unique to that agent.
{% endstep %}
{% endstepper %}

Each agent has its own credentials and endpoint. If you run outbound for two different products or two different databases, use two agents so you can tune retries, scripts, and SMS independently.

### Fire the trigger from your CRM

```http
POST https://app.voiceb.ai/api/click-to-call
```

Headers:

| Header      | Value                                                        |
| ----------- | ------------------------------------------------------------ |
| `api-key`   | Your agent's API Token                                       |
| `signature` | HMAC-SHA256 of the request body, signed with your API Secret |

Body:

```json
{
  "phone": "+34612345678",
  "variables": {
    "name": "John",
    "product": "VoiceB AI"
  }
}
```

Anything in `variables` becomes available in the script as `{{variable_name}}` — this is how your CRM passes context, such as plan, contract end date, or campaign name, into the call without the agent asking for it again.

Reference every variable you send explicitly in the script; unreferenced or `undefined` variables can cause call failures, so send optional fields as empty strings rather than omitting them.

{% hint style="warning" %}
If your triggering system can't sign requests, such as some marketing automation tools, the agent config exposes a **Disable Signature Verification** option. Use it only from a trusted internal system, never expose the raw endpoint publicly.
{% endhint %}

**Scheduling the launch itself** is a decision you make on your side, not inside VoiceB: the API executes the call the moment it receives the request. "Programming" a campaign launch means your CRM, or the middleware in front of it, queues and paces the POST requests — for example, releasing 500 triggers at 09:00 local time instead of firing a whole database at once.

Batch your launch so retries don't stack multiple campaigns' backoffs into the same evening.

### Let the Retry Policy do the re-dialing

A single dial attempt reaches roughly 30–50% of leads; three to four spaced attempts typically lift that above 70%.

Configure the Retry Policy per agent rather than re-triggering manually from your CRM:

* **Max attempts** — total dials including the first. 3–5 is the right range for most B2C outbound; more rarely helps and can raise compliance flags.
* **Retry on** — which outcomes re-trigger a dial: typically `no answered`, `busy`, `voicemail`. Do **not** retry on `failed`, `dropped`, or `unreachable` — those are number-level problems more attempts won't fix.
* **Backoff** — Exponential (15 → 30 → 60 → 120 min, capped) for most lead-gen; a custom sequence, such as `15, 60, 240, 1440`, when you want a precise cadence.
* **Business hours** — respected in the campaign's local timezone, with a weekdays-only option. A retry that would fall outside the window is pushed to the next valid slot, never placed early.

A safe Spain/EU default: 4 attempts, retry on no-answer/busy/voicemail, exponential backoff (15/2/1440), 9:00–20:00 weekdays only.

Every retry is logged as its own call in the dashboard (`retry 1/3`, `retry 2/3`...), so you can audit the full attempt chain per lead.

{% hint style="info" %}
The first call fires the instant your CRM hits the API — there is no advance schedule to attach a warm-up message to. Every attempt from the first retry onward is scheduled in advance by the Retry Policy. Warm-up SMS can only fire ahead of a *scheduled* call, so in an outbound-from-CRM flow it fires after the lead has already missed the first attempt, not before it.
{% endhint %}

## The SMS bridge: one feature, correctly timed

There is only one SMS feature in the product — **Warm-up SMS** — configured per campaign and sent a defined amount of time, from 1 minute to 1 week, before a *scheduled* call.

In an inbound or immediate-trigger context, that naturally means "before the very first call." In a CRM-triggered outbound campaign with retries, it naturally means **after the first call has already gone unanswered, and before the retry** — because that retry is the only scheduled call in the sequence.

Get the cadence right and this single feature does double duty as both a warm-up and a "we just tried to reach you" bridge:

{% stepper %}
{% step %}

## Trigger the first call

Client CRM triggers the first call. It is immediate, with no SMS involved.
{% endstep %}

{% step %}

## Schedule the retry

The lead doesn't pick up. The Retry Policy schedules the next attempt, for example in 15 minutes.
{% endstep %}

{% step %}

## Send the Warm-up SMS

Warm-up SMS fires ahead of that scheduled retry, with content written as a post-miss message, not a pre-call teaser.
{% endstep %}

{% step %}

## Place the retry call

The retry call goes out on schedule.
{% endstep %}
{% endstepper %}

### Setting it up

{% stepper %}
{% step %}

## Enable Warm-up SMS

Campaign → configuration → enable **"Send a warm-up message before calling"**.
{% endstep %}

{% step %}

## Set the sender number

Use a dedicated SMS-capable DDI, separate from your voice DDI, billed monthly: €6 in most markets, $1.50 US, £2.50 UK.
{% endstep %}

{% step %}

## Set the timing

Set **Time before the call** shorter than your first retry delay so the SMS lands right after the miss, comfortably before the retry rings.

For example: retry backoff of 15 minutes → SMS timing of 5–10 minutes.
{% endstep %}

{% step %}

## Write the message

Use free text with `{{token}}` placeholders.
{% endstep %}
{% endstepper %}

Because it's a free-text field, you write the bridge content directly into it — there's no separate opt-out or callback-number setting; both live in the message body.

### Message content

> Hi {{name}}, this is {{brand}} — we just tried to reach you about {{topic}}. Call us now on {{inbound\_number}} if you'd like to talk, or we'll call you again shortly from {{outbound\_number}}. Don't want us to call? Reply STOP.

`{{inbound_number}}` must be a real, bidirectional number the lead can actually call — never the 400 CLI. Depending on the market this is a geographic or national number, for example `+34930XXXXXX` (Spain), `+1 250 566 XXXX` (Canada/US), or `+44 1527 65XXXX` (UK) — whatever bidirectional line the client already staffs or routes through an IVR.

### Opt-out is mandatory, not optional

Every bridge message must include a working opt-out (`STOP` reply or a link) — this isn't a nice-to-have, it's a legal requirement for commercial communications in Spain and most markets we operate in.

The opt-out must:

* Immediately suppress any pending retries for that lead in VoiceB
* Be written back into the same outcome payload that reaches the client's CRM (`optOut: true`) or fire its own webhook trigger condition
* Be treated by the client as authoritative for that phone number from that point on, across every campaign, not just the one it came from

If the call is cancelled or rescheduled before it goes out, the pending SMS is cancelled automatically — no lead ever gets a bridge message for a retry that never happens.

### Recommended campaign setup

Always run this as a **separate campaign**, independent from inbound, inside app.voiceb.ai.

This keeps retry cadence, business-hours rules, and SMS timing isolated from inbound agents, and keeps outbound-specific metrics — contact rate, SMS-driven callbacks, opt-out rate — from diluting inbound conversion reporting.

VoiceB commits to the mechanism working reliably end-to-end — trigger, retry, bridge SMS, opt-out, CRM sync — but not to a fixed conversion rate or AI pickup-response rate, since those depend on factors outside the platform: list quality, time since last contact, and market-wide pickup decline.

## Spain, October 17, 2026: the "400" prefix and what it forces you to redesign

Under Ley 10/2025 (customer service law) and the Government's anti-fraud plan, Spain's telecoms regulator has reserved a new nine-digit numbering range — the **400 block** — exclusively for commercial calls.

From **October 17, 2026**:

* Every commercial outbound call in Spain must show a `400xxxxxx` caller ID.
* Telecom operators will **block** commercial calls that don't use this range.
* The 400 range is **unidirectional — outbound only**. It cannot receive incoming calls. A lead cannot call it back, and it cannot be used as a customer-service number.
* 400 numbers must be requested through your telephony operator. The CNMC assigns blocks to operators, not directly to businesses, and they must be kept separate from customer-service lines such as geographic and 800/900 lines.

```mermaid
sequenceDiagram
    participant Agent as VoiceB Agent (400 CLI)
    participant Lead
    participant Inbound as Client's real inbound number
    Agent->>Lead: Outbound call from 400xxxxxx
    Note over Lead: Lead misses the call
    Lead-->>Agent: Cannot call back (400 is one-way)
    Note over Agent,Lead: Retry Policy schedules next attempt
    Agent->>Lead: Warm-up/bridge SMS with a real inbound number + STOP opt-out
    Lead->>Inbound: Calls the inbound number if they want to talk now
```

{% hint style="warning" %}
Before October 17, a lead who missed your call could simply call the displayed number back. After October 17, that path disappears entirely for any number in the 400 range. The bridge SMS stops being a nice-to-have and becomes the *only* route back to a live conversation for a lead who misses the call.
{% endhint %}

Action items for clients ahead of the deadline:

{% stepper %}
{% step %}

## Confirm applicability

Confirm with legal whether your outbound campaigns fall under the "commercial call" definition. There are narrow exceptions.
{% endstep %}

{% step %}

## Request 400 numbering

Request 400 numbering from your telephony/SIP provider with enough lead time to activate before October 17.
{% endstep %}

{% step %}

## Configure the outbound caller ID

Configure the 400 CLI as the outbound caller ID at the agent/SIP trunk level.
{% endstep %}

{% step %}

## Verify the inbound number

Make sure the inbound number quoted in the bridge SMS is a genuinely different, bidirectional number — never the 400 itself.
{% endstep %}
{% endstepper %}

## Writing Script Blocks for outbound agents

Outbound scripts use the same Script Block structure as inbound:

* Goal
* Role
* Objective
* Guardrails
* Script
* Pricing
* Objections

Three blocks need outbound-specific adjustments because the lead did not initiate this contact.

### Initial Message

An inbound agent can jump straight to helping; an outbound agent has to earn the first five seconds. State who you are, the company, and *why you're calling this specific person* before asking anything:

> "Hello, this is Laia from {{brand}}. I'm calling because {{reason\_for\_call}} — do you have a minute?"

### Guardrails

Add outbound-specific hard rules on top of the standard set:

* Always state the reason for the call and the company name within the first two sentences.
* Never claim the call is a random check-in if it was triggered by a specific CRM event — reference the real reason.
* If the lead says they didn't consent or don't recognize the company, acknowledge, do not argue, and route to the opt-out path.
* Respect the configured business-hours and weekday restrictions at the campaign level; the script should never encourage retrying "off the books".

### Objections

Outbound leads raise a different first objection set than inbound. Cover these explicitly, in addition to the standard price/timing objections:

* *"How did you get my number?"* → State plainly and honestly: existing customer relationship, form submission, opted-in database. Never deflect.
* *"I didn't ask for this call."* → Acknowledge, offer to place them on the opt-out list immediately, do not attempt to re-pitch.
* *"Is this a real person or a robot?"* → Follow your organization's disclosure policy directly and honestly — never claim to be human if asked directly.

Everything else in the block-editing workflow — versioning, Publish vs. Reset, Script History with Preview/Diff/Restore — works identically for outbound and inbound agents.

## Connecting Tools for real-time actions during the call

Outbound calls benefit from the same **Tools** mechanism as inbound: an HTTP action the agent can call mid-conversation to check or push data.

Typical outbound uses:

* Look up the lead's current plan/contract before pitching, so the agent doesn't re-offer what they already have.
* Validate that the lead hasn't already opted out in a parallel system before continuing the pitch.
* Push a qualified outcome to a downstream system mid-call, such as reserve a slot, rather than waiting for the post-call webhook.

When building a tool:

* Write the description as an instruction to the agent — when to call it, what inputs it needs, what it returns — not as internal documentation.
* Mark critical parameters as **Required** so the tool never fires with incomplete data.
* Keep endpoint latency under \~2 seconds; slower responses create dead air on the call.
* Always script a fallback path for when the tool errors or returns nothing.
* Build shared tools, such as "check opt-out status", once at the organization level and reuse them across every outbound agent, rather than duplicating per campaign.

## Post-call: getting outcomes into your CRM

Every call — including bridge-SMS follow-ups and opt-outs — should land in the client's CRM without manual work.

Three paths use the same underlying outcome payload.

### HubSpot

{% stepper %}
{% step %}

## Activate the integration

Activate the HubSpot integration at the organization level through the Integrations menu.
{% endstep %}

{% step %}

## Assign a Data Collection Schema

Confirm the agent has a Data Collection Schema assigned. Its fields — name, phone, email, address, and so on — are what you'll map to HubSpot properties.
{% endstep %}

{% step %}

## Authorize HubSpot

On the agent, add the HubSpot integration and authorize via OAuth.
{% endstep %}

{% step %}

## Map fields

Map VoiceB's default fields — Id, Start/End, From/To, Agent, Status, Transcript, Summary, Tags — and your schema fields to the matching HubSpot contact properties.
{% endstep %}

{% step %}

## Optionally set constants

Set **Constants** for fixed values injected on every push, such as Lead Source = "VoiceB Outbound".
{% endstep %}

{% step %}

## Set trigger conditions

Choose "Valid only" — Sold, Reschedule, Talk to Agent, Pending — to keep the CRM clean, or select outcomes manually if you also want opt-outs or no-answers logged for suppression purposes.
{% endstep %}
{% endstepper %}

### Salesforce

Salesforce integration follows the same shape as HubSpot — connect at the organization level, authorize, map VoiceB's default and schema fields to Salesforce objects, and set trigger conditions per outcome.

See the [Salesforce connection guide](https://docs.voiceb.ai/how-to-connect-your-voiceb.ai-agent-with-salesforce) for the exact steps, including registering a local External Client App where required.

### Webhook (custom CRM / middleware)

For any system without a native connector, configure a webhook at **Agent Config → Integrations → Webhook**:

```json
{
  "agent": "OutboundAgentName",
  "from": "+34400XXXXXX",
  "to": "+34612345678",
  "status": "NO_ANSWERED",
  "transcript": [ /* ... */ ],
  "collectedData": {
    "name": "John Doe",
    "email": "john.doe@example.com"
  }
}
```

* Sign the payload (`x-webhook-signature`) to verify authenticity on your end.
* Use **Trigger Conditions** to decide which outcomes actually fire the webhook. For outbound, it's common to fire on every terminal outcome, including no-answer and opt-out, so your CRM can drive suppression logic, not just the "Sold"/"Valid" set used for pipeline creation.

## Compliance checklist before going live

* [ ] Database is opted-in — no purchased or cold lists
* [ ] Retry Policy respects local calling hours and weekday rules for the campaign's market
* [ ] Max retry attempts reviewed against local rules (some markets cap total attempts per number per period)
* [ ] Spain campaigns: 400 numbering requested and activated as the outbound CLI before October 17, 2026
* [ ] Warm-up SMS timing is set shorter than the first retry delay, so the bridge message lands between the miss and the retry
* [ ] The inbound number quoted in the SMS is a genuine bidirectional line — never the 400 CLI
* [ ] Opt-out (STOP/link) is present in every bridge message — mandatory, not optional
* [ ] Opt-out suppresses further retries in VoiceB **and** is written back to the CRM
* [ ] Outbound campaign is configured separately from inbound campaigns in app.voiceb.ai
* [ ] Script Guardrails include honest disclosure of call source and AI/human status if directly asked

## Related documentation

* [API – Click-to-Call](https://docs.voiceb.ai/api-click-to-call)
* [Warm-up SMS for outbound calls](https://docs.voiceb.ai/api-click-to-call/warm-up-sms-for-outbound-calls)
* [Click-to-Call Retry Policy](https://docs.voiceb.ai/click-to-call-retry-policy)
* [Tools — Connect Your Agent to External Actions](https://docs.voiceb.ai/tools-connect-your-agent-to-external-actions)
* [How to Update a VoiceB Agent Prompt & Script](https://docs.voiceb.ai/how-to-update-a-voiceb-agent-prompt-and-script)
* [How to Connect Your VoiceB.ai Agent with HubSpot](https://docs.voiceb.ai/how-to-connect-your-voiceb.ai-agent-with-hubspot)
* [How to Connect Your VoiceB.ai Agent with Salesforce](https://docs.voiceb.ai/how-to-connect-your-voiceb.ai-agent-with-salesforce)
* [Webhook – Call Outcome Integration](https://docs.voiceb.ai/webhook-call-outcome-integration)
* [Understanding Status & Outcomes in VoiceB](https://docs.voiceb.ai/understanding-status-and-outcomes-in-voiceb)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.voiceb.ai/product-guides/outbound-campaigns-guide.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
