Campaigns overview
What a tracking campaign is, the gates every call passes before a buyer is considered, every campaign setting with its range and default, how a campaign goes live and what an API key can change.
After this guide you will be able to set a campaign up end to end: choose its settings knowingly, take it live without being refused, pause and resume it from a script, and say why a call to one of its numbers never reached a buyer.
A campaign is one stream of calls with one set of rules: one offer, one vertical, one group of tracking numbers. It exists so that the questions that are about the *call* — is it inside hours, has this person called before, what happens if nobody answers — are asked once, in one place, before the questions that are about a *buyer*. Those belong to the campaign's routing plan.
Two ways a call reaches a campaign
- Somebody dials one of its numbers. The tracking number says which campaign the call belongs to, and — when the number is tied to a publisher or sits in a number pool — who sent it. This is the path the rest of this page describes.
- A publisher pings it, wins and sends the call. The publisher's platform asks for a bid first, and the call that follows is routed by the same plan, with the auction standing in for the gates. See How RTB works.
The gates a call passes first
Before a single target is looked at, a dialled call is tested against eight gates, always in this order. The first one it fails ends the call there, with that reason code on it. Every call gets a row in the call log, including the ones that went nowhere.
| Gate | Decided by | Reason if it fails | Caller gets |
|---|---|---|---|
| Is the workspace's plan active, and does it include call tracking? | Billing and usage | workspace_inactive | A busy signal |
| Is the wallet funded? | Wallet | workspace_unfunded | A busy signal |
| Is the campaign live? | Status, below | campaign_not_live | A busy signal |
| Is the caller blocked? | Blocked callers | caller_blocked | A busy signal |
| Did the caller hide their number? | anonymousPolicy | anonymous_blocked | A busy signal |
| Is the campaign inside its hours? | hoursSchedule | campaign_closed | The unrouted action |
| Is the campaign under its caps? | dailyCap, concurrencyCap | campaign_capped | The unrouted action |
| Has this caller reached a buyer here before? | repeatCallerPolicy | repeat_blocked | A busy signal |
unroutedAction: hang up, a spoken message or a forward.When the wallet gate fails, the workspace's owners are notified and a wallet.low event is sent — at most once an hour, however many callers are turned away in it.
The settings
| Field | Values | What it does |
|---|---|---|
name | 1–120 characters | Required. |
verticalId | one of the platform's verticals, or one of your own | Required, and set once: a campaign cannot be moved to another vertical. Create a new campaign instead. |
description | up to 300 characters | A note for your team. |
routingMode | priority (default), weighted, highest_bid | How the routing plan is put in order for each call. See The three routing modes. |
recordCalls | true (default), false | Whether calls on this campaign are recorded. Read once, when the call arrives. |
repeatCallerPolicy | allow (default), block, same_target | What happens to a caller who has already reached a buyer on this campaign. See Repeat and blocked callers. |
dedupeWindowSeconds | 0–7 776 000 (90 days); default 0 | How far back the repeat-caller policy looks. 0 means for ever. |
anonymousPolicy | allow (default), block | Whether a caller with a withheld number is routed. |
unroutedAction | hangup (default), message, forward | What a caller gets when the campaign wanted the call and could not place it. See The unrouted action. |
unroutedMessage | up to 500 characters | Required when the action is message. |
unroutedForwardE164 | a phone number in international format | Required when the action is forward. |
dialTimeoutSeconds | 5–120; default 18 | How long a destination rings when the target does not set a ring time-out of its own. |
failoverBudgetSeconds | 10–180; default 45 | How long the platform may go on starting new attempts. Never shorter than dialTimeoutSeconds. See The failover budget. |
dailyCap | 0–1 000 000; default 0 | Calls a day, on the workspace's calendar day. 0 is unlimited. |
concurrencyCap | 0–100 000; default 0 | Calls in progress at once. 0 is unlimited. |
hoursSchedule | a schedule, or null (default) | When the campaign takes calls. null is always open. See Hours of operation. |
timezone | an IANA zone such as America/Chicago | The clock the hours run on. It is stored inside the schedule, so it has no effect on a campaign with no schedule. |
states | up to 62 two-letter codes; [] is every state | Which caller states the campaign bids on when a publisher pings it. A dialled call is not refused by this list — use each target's accepted states for that. |
unroutedAction to forward is refused unless a forward number is already there or arrives in the same request.A campaign also shows a routingKeyPrefix. It is minted by the platform, read-only, and is what to quote to support when a question is about this campaign.
Status
| Status | Meaning |
|---|---|
draft | Being built. Every new campaign starts here. Its numbers do not route. |
live | Routing. Every call to one of its numbers runs the gates and the plan. |
paused | Not routing. Callers get a busy signal and the call is logged with campaign_not_live. |
restricted | Set by platform staff, and only staff can lift it. You can read it; you cannot set it. |
archived | History. It no longer counts against your plan's campaign allowance. |
Status is changed by its own request, never as a side effect of editing a setting, so a settings change cannot switch a campaign on or off by accident. A change to the status a campaign already has answers changed: false and does nothing. These changes are refused, each with a 409 that says what to do instead:
- Going live while not ready. The answer carries
details.blockers— the whole list, not just the first. There are exactly two: no active tracking number points at the campaign, and the routing plan has no active route to an active target. - Archiving a live campaign. Pause it first.
- Archiving while numbers still point at it. They would go on ringing into a campaign nobody can see, and go on being rented. Move or release them;
details.numberssays how many. - Taking an archived campaign anywhere but
draft. Restoring re-checks your plan's campaign allowance, so it can also answer402.
An optional note of up to 500 characters is stored as the campaign's statusNote — "paused for the weekend" — and cleared when the campaign goes live. Every status change is written to the activity log.
The readiness checklist
A campaign's detail carries a checklist: the journey from nothing to a routed call, each step a fact the server checked rather than a box somebody ticked. Two steps are blocking — they are the two a launch is refused on — and one is optional.
| Step | Done when | Blocks going live |
|---|---|---|
| Add a buyer | The workspace has at least one buyer. | No |
| Add a target | The workspace has at least one target. | No |
| Put a target in the routing plan | The plan has an active route to an active target. | Yes |
| Point a tracking number at the campaign | An active tracking number is assigned to it. | Yes |
| Add a publisher lane | Only if somebody else sends you this traffic. Your own media needs none. | No — optional |
| Fund the wallet | The workspace wallet has a balance. | No |
| Set the campaign live | Its status is live. | No |
canGoLive, blockers — the sentences a launch would be refused with — and checklist.warnings, such as a plan whose every active target belongs to a buyer that is not active.An empty wallet does not block a launch on purpose: a campaign may go live on Friday for traffic that starts on Monday. It does stop calls routing, so the checklist says so.
Worked example: a campaign from nothing to live
- 1
Create the campaign
In the console, open Campaigns and create one. These are the settings for a Medicare enquiry line that is open on weekdays and Saturday mornings, sends a returning caller back to the buyer they spoke to, and reads a message to anybody it cannot place:
The campaign's settings{ "name": "Medicare enquiries", "verticalId": "medicare", "description": "Inbound enquiries from search and partner sites", "routingMode": "priority", "recordCalls": true, "repeatCallerPolicy": "same_target", "anonymousPolicy": "block", "dedupeWindowSeconds": 2592000, "unroutedAction": "message", "unroutedMessage": "Thank you for calling. Our lines are closed right now. Please call again tomorrow.", "dialTimeoutSeconds": 18, "failoverBudgetSeconds": 45, "dailyCap": 400, "concurrencyCap": 0, "hoursSchedule": { "tz": "America/Chicago", "days": { "mon": [ [ "08:00", "20:00" ] ], "tue": [ [ "08:00", "20:00" ] ], "wed": [ [ "08:00", "20:00" ] ], "thu": [ [ "08:00", "20:00" ] ], "fri": [ [ "08:00", "20:00" ] ], "sat": [ [ "09:00", "13:00" ] ], "sun": [] } } }The campaign is created as a
draft. The response already tells you what stands between it and a routed call:canGoLiveisfalse, andblockerslists both blocking steps. - 2
Give it somewhere to send calls
Add a buyer and a target, then put the target on the campaign's Routing plan tab. See Routing plans.
- 3
Give it a number
Buy a tracking number under Numbers and assign it to the campaign. See Assigning numbers.
- 4
Check it, then go live
Simulate a call from a caller you choose. When the simulation ends at the target you expect, set the campaign live.
canGoLiveis nowtrueand the request succeeds.
Pause and resume from a script
Creating a campaign, editing its settings, launching a draft and archiving are acts for a person in the console. An API key can do the things an integration needs day to day: read campaigns (campaigns:read), replace a routing plan, and pause and resume a campaign that is already live or paused (campaigns:write).
curl -X POST "https://api.buy3.io/api/v1/campaigns/6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d/status" \
-H "Authorization: Bearer $BUY3_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "paused", "note": "Buyers closed for the public holiday" }'A resume passes the same readiness check as a launch. If somebody released the campaign's last number while it was paused, the answer is:
{
"error": "That campaign is not ready to take calls yet",
"code": "conflict",
"details": {
"blockers": [
"No tracking number points at this campaign — buy or assign one first",
"The routing plan has no active target — add one, or every caller goes unrouted"
]
},
"requestId": "5d1c0d2e-8a4f-4c0b-9e1d-2f3a4b5c6d7e"
}Asking a key to set any other status, or to change a campaign that is a draft or archived, answers 409 with details.allowed listing live and paused. The full request and response shapes are in the Campaigns API reference.
