Targets
A target is one destination a buyer receives calls on — a phone number, a SIP address or a bidding endpoint — with its own price, duration threshold, hours, states, caps and duplicate window.
After this guide you will be able to create a destination of any of the three kinds, set what a call there is worth and when it converts, tell a target's status from its health, and know which of its settings an integration may change and which belong to a person in the console.
A target is where a call is actually sent. It belongs to one buyer, can sit on the routing plan of any number of campaigns, and carries everything that is true of that destination wherever it is used. A buyer is a company; a target is one of their phones.
The three types
type | Console label | Destination | What a call is worth |
|---|---|---|---|
number | Phone number | A phone number in international format. | Fixed: the revenue you set. |
sip | SIP address | sip:user@host or sips:user@host, on a public host. | Fixed. |
rtb | RTB buyer | Whatever the buyer's bidding endpoint answers with. | Their bid, for that call. See External RTB targets. |
type in an edit is refused rather than ignored; create a new target instead.A target also reports a kind, which you do not set. A number or sip target with revenue above zero is static; one with revenue of 0 is delivery — a destination that takes calls and books nothing, which is what you want for your own overflow line. An rtb target is always external_rtb.
Creating one
Under Targets, New target opens a drawer. Its sections are Basics, Destination, Revenue and conversion, Caps, When it is open, Geography and Duplicates — and for an RTB buyer, the bidding sections in between.
{
"name": "Acme Health — Dallas floor",
"buyerId": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
"type": "number",
"destinationE164": "+12145550188",
"callerIdMode": "passthrough",
"ringTimeoutSeconds": 20,
"revenueCents": 4200,
"durationThresholdSeconds": 90,
"capCountsOn": "connected",
"concurrencyCap": 5,
"dailyCap": 40,
"states": [
"TX"
],
"timezone": "America/Chicago",
"dedupeWindowSeconds": 86400,
"externalRef": "ACME-DAL-01"
}{
"name": "BlueSky Legal — Intake trunk",
"buyerId": "e2d3c4b5-a697-4887-9bac-1d2e3f4a5b6c",
"type": "sip",
"sipUri": "sip:intake@sip.example.com",
"sipTransport": "tls",
"sipPort": 5061,
"sipAuthType": "basic",
"sipUsername": "buy3",
"sipSecret": "not-a-real-password",
"ringTimeoutSeconds": 25,
"revenueCents": 3800,
"durationThresholdSeconds": 60
}A field that belongs to another type is refused, not dropped: sending destinationE164 on a SIP target answers 400 with "Those fields do not apply to a … target". That rule is why an edit cannot half-work.
{
"target": {
"id": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
"name": "Acme Health — Dallas floor",
"type": "number",
"kind": "static",
"status": "active",
"buyerId": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
"buyer": {
"id": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
"name": "Acme Health",
"status": "active"
},
"health": "unknown",
"lastError": null,
"consecutiveFailures": 0,
"suppressedUntil": null,
"revenueCents": 4200,
"durationThresholdSeconds": 90,
"destination": {
"type": "number",
"value": "+12145550188",
"display": "+12145550188"
},
"endpoint": {
"id": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
"mode": "pstn_did",
"destinationE164": "+12145550188",
"callerIdMode": "passthrough",
"callerIdE164": null,
"sendDigits": null,
"ringTimeoutSeconds": 20,
"lastHealthCheckAt": null
},
"rtb": null,
"concurrencyCap": 5,
"hourlyCap": 0,
"dailyCap": 40,
"monthlyCap": 0,
"totalCap": 0,
"dailyRevenueCapCents": 0,
"capCountsOn": "connected",
"usage": {
"liveCalls": 0,
"hour": 0,
"day": 0,
"month": 0,
"total": 0,
"dayRevenueCents": 0
},
"liveCalls": 0,
"states": [
"TX"
],
"timezone": "America/Chicago",
"hoursSchedule": null,
"dedupeWindowSeconds": 86400,
"externalRef": "ACME-DAL-01",
"routes": []
}
}What a target carries
| Setting | Console label | Range | What it does |
|---|---|---|---|
name | Name | 2–200 characters | What this destination is called in the plan, the call log and every report. |
buyerId | Buyer | one of your buyers, not archived | Required. Who owns it, and whose caps sit above its own. |
externalRef | Your reference | up to 200 characters | Your own id for it. Frozen onto every call, and available to a pixel as {{target.externalRef}}. |
revenueCents | Revenue per converted call | 0–100,000,000 | What the buyer owes for a call that converts. Refused on an rtb target — the bid is the price. |
durationThresholdSeconds | Pays when the call lasts at least | 0–7200 | Talk time with the buyer, not ring time. Below it the call is connected but not converted. 0 converts every answered call. |
capCountsOn | A cap counts a call | claimed, connected (default), converted | Which moment moves the window caps. See How caps are counted. |
| Caps | Caps | see Hours and caps | Concurrency, hourly, daily, monthly, lifetime and a daily revenue cap. 0 is unlimited. |
states | States this buyer will take | up to 62 two-letter codes | Empty accepts every state. A caller whose state could not be worked out is never skipped by this list. |
hoursSchedule, timezone | Hours | a schedule, or none | When this target is considered. Outside them it is skipped with closed. |
dedupeWindowSeconds | Do not send the same caller again within | 0–2,592,000 (30 days) | A caller who already reached this target inside the window is skipped with duplicate. 0 is off. |
ringTimeoutSeconds | Ring for | 5–120, default 20 | How long this destination rings before the plan moves on. number and sip only. |
priority and weight are properties of the route, not of the target: each campaign's plan sets its own. See Routing plans.Phone destinations
- What the buyer sees as the caller (
callerIdMode) ispassthrough— the caller's own number, and the default —tracking_number, so the buyer sees the number that was dialled, orfixed, which always shows a number you give. - Digits to send after they answer (
sendDigits) navigates an extension or an IVR: digits,*and#, withwor a comma for a one-second pause.
SIP destinations
- The host must resolve to a public address. A private, loopback or link-local host is refused when you save it and again every time it is used.
- Transport is
udp,tcportls. Only TLS encrypts the signalling. The usual ports are 5060 for UDP and TCP, 5061 for TLS. - Authentication covers none, basic, bearer, an API key in a header, an HMAC signature, OAuth 2 client credentials, an IP allowlist on the buyer's side, and digest. The credential is write-only: it never comes back, and responses carry only
hasSipSecretand its last four characters. Choosingnoneclears what is stored.
Status and health are different things
Confusing these two is the single most expensive mistake on these screens. Status is what somebody chose. Health is what the platform has observed. An active target can be down, and while it is, every plan skips it with suppressed without anybody having touched a setting.
status | Meaning |
|---|---|
draft | Being wired up. Every plan skips it. Useful while you finish the details. |
active | Considered by every plan it sits on. |
paused | Skipped with paused. Its settings, counters and history are kept. |
archived | History. Archiving removes the target from every routing plan in the same transaction, and the response says how many routes went. |
409.health | Console label | What it means |
|---|---|---|
unknown | Not tested | Nothing has been observed yet. It is used normally. |
healthy | Healthy | The last thing that was tried worked. |
degraded | Degraded | Three consecutive failures. A warning only: the target is still used. |
down | Down | Six consecutive failures. On a bidding target that also suppresses it for a minute, and every plan skips it with suppressed until a background pass puts it back. |
consecutiveFailures, lastError and suppressedUntil say why, and when it will be tried again. One success resets all of it.{
"health": "degraded",
"consecutiveFailures": 4,
"lastError": "The bid URL answered HTTP 502",
"suppressedUntil": null
}- The three-and-six ladder is the bidding one. On an
external_rtbtarget the counters are moved by pings, anddownis what takes it out of a plan. See The circuit breaker. - A
numberorsiptarget's health comes from its reachability probes instead, and moves on the same ladder — but health alone never removes it from a plan. Plenty of working equipment ignores an OPTIONS probe, and a target skipped for that would be an outage the platform invented. - What does take a dialled target out is its own equipment asking for it: a
503with aRetry-After. Then it is skipped withsuppressedfor exactly as long as that header named, and comes back on its own. - Three failed calls in a row send a
target.downevent and raise an alert on the Overview page. That is a warning, not a suspension — the next call still tries the target.
What takes a target out of a plan
When a call arrives, every active route is tested in a fixed order and the first test it fails is its skip reason. This is the same table as in Routing plans, read from the target's side:
| Skip reason | Fix it on |
|---|---|
paused | The target's status — or its endpoint's. |
buyer_paused | The buyer, not the target. |
no_destination | The destination: a missing number, SIP address or bid URL. |
suppressed | Whatever the endpoint is doing. lastError says what was seen. |
closed | The target's hours, or the endpoint's. |
state | The accepted states. A caller with no known state is never skipped here. |
concurrency | The target's or the endpoint's concurrency cap. |
capped, spend_capped | The target's window caps and its daily revenue cap. |
buyer_capped | The buyer's caps, which sit above these. |
duplicate | The target's duplicate window. |
tag_filter | The campaign's filters — not the target. |
no_bid, timeout, error | The bidding endpoint. See External RTB targets. |
Reading a target's page
- Where it goes — the destination, or for a bidding target the host it bids at.
- Health and Status, in two separate columns, and an Open or Closed pill for its hours.
- Caps with what has been used against each: a bar that turns amber at 80 per cent of the cap and red at 100.
- Routes — the campaigns whose plan this target is on, with the priority, weight and revenue override each one gives it.
- Today — calls, connected, converted and revenue on the workspace's calendar day.
- The actions: Test, Pause or Activate, Reset caps and Archive.
Changing a target from a script
An API key with targets:write may change what a target is worth and how much it may take — never where its calls go. That is the whole rule, and the refusal names every field it would not accept.
| A key may | A key may not |
|---|---|
Change revenueCents, every cap, hoursSchedule and states | Change name, buyerId, type, destinationE164, sipUri, the rtb block, capCountsOn, timezone, durationThresholdSeconds or dedupeWindowSeconds |
Pause and resume a target that is already active or paused | Create a target, archive one, or restore one from archived |
| Reset its cap counters | Run a test against the buyer |
400 field_not_writable with a details entry per field, each naming what a key may change instead.curl -X PATCH "https://api.buy3.io/api/v1/targets/a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d" \
-H "Authorization: Bearer $BUY3_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "revenueCents": 4500, "dailyCap": 60 }'A key reading a bidding target sees its header names but never their values, and never the body template: rtb.bidHeaders values and rtb.bidBodyTemplate come back as •••, because that is where buyers' static tokens live in practice. Full request and response shapes are in the Targets API reference.
Worked example: one buyer, three destinations
Acme Health takes calls three ways, so they are three targets under one buyer:
| Target | Type | Why it exists |
|---|---|---|
| Acme Health — Dallas floor | number | The main room. Five lines, 40 connected calls a day, Texas only, $42.00 a converted call at 90 seconds. |
| Acme Health — Overflow | number | A second number for what the floor cannot take. Same buyer, no state list, priced lower on the campaign with a route override. |
| Acme Health — RTB | rtb | Their bidding endpoint, for the campaign where they want to price each call themselves. |
- All three are put on the campaign's plan: the floor at priority 1, overflow at 2, and on the other campaign the RTB target alone.
- Acme pauses for a public holiday. Pausing the buyer takes all three out of every plan at once, with
buyer_paused. Nothing about the targets changes, and un-pausing puts them all back. - A week later Acme's trunk provider has an outage and their bidding endpoint stops answering. After three failed pings the RTB target reads Degraded; after six it is Down and suppressed for a minute, and calls skip it with
suppressedwhile the other two carry on. - Acme closes the Dallas room and asks you to stop sending there. You archive that target: it leaves every plan in one transaction, and every past call, bid and report still names it.
