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

typeConsole labelDestinationWhat a call is worth
numberPhone numberA phone number in international format.Fixed: the revenue you set.
sipSIP addresssip:user@host or sips:user@host, on a public host.Fixed.
rtbRTB buyerWhatever the buyer's bidding endpoint answers with.Their bid, for that call. See External RTB targets.
The type is chosen once and cannot be changed. Sending a different 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.

A phone destination
{
  "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"
}
A SIP trunk
{
  "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.

201 — the target as it is stored
{
  "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

SettingConsole labelRangeWhat it does
nameName2–200 charactersWhat this destination is called in the plan, the call log and every report.
buyerIdBuyerone of your buyers, not archivedRequired. Who owns it, and whose caps sit above its own.
externalRefYour referenceup to 200 charactersYour own id for it. Frozen onto every call, and available to a pixel as {{target.externalRef}}.
revenueCentsRevenue per converted call0–100,000,000What the buyer owes for a call that converts. Refused on an rtb target — the bid is the price.
durationThresholdSecondsPays when the call lasts at least0–7200Talk time with the buyer, not ring time. Below it the call is connected but not converted. 0 converts every answered call.
capCountsOnA cap counts a callclaimed, connected (default), convertedWhich moment moves the window caps. See How caps are counted.
CapsCapssee Hours and capsConcurrency, hourly, daily, monthly, lifetime and a daily revenue cap. 0 is unlimited.
statesStates this buyer will takeup to 62 two-letter codesEmpty accepts every state. A caller whose state could not be worked out is never skipped by this list.
hoursSchedule, timezoneHoursa schedule, or noneWhen this target is considered. Outside them it is skipped with closed.
dedupeWindowSecondsDo not send the same caller again within0–2,592,000 (30 days)A caller who already reached this target inside the window is skipped with duplicate. 0 is off.
ringTimeoutSecondsRing for5–120, default 20How long this destination rings before the plan moves on. number and sip only.
A target's 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) is passthrough — the caller's own number, and the default — tracking_number, so the buyer sees the number that was dialled, or fixed, which always shows a number you give.
  • Digits to send after they answer (sendDigits) navigates an extension or an IVR: digits, * and #, with w or 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, tcp or tls. 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 hasSipSecret and its last four characters. Choosing none clears 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.

statusMeaning
draftBeing wired up. Every plan skips it. Useful while you finish the details.
activeConsidered by every plan it sits on.
pausedSkipped with paused. Its settings, counters and history are kept.
archivedHistory. Archiving removes the target from every routing plan in the same transaction, and the response says how many routes went.
Activating a target clears its circuit breaker — the failure count and the suppression — so a fixed endpoint is tried again at once. Activating a target whose buyer is archived is refused with a 409.
healthConsole labelWhat it means
unknownNot testedNothing has been observed yet. It is used normally.
healthyHealthyThe last thing that was tried worked.
degradedDegradedThree consecutive failures. A warning only: the target is still used.
downDownSix 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.
A target the breaker has noticed
{
  "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_rtb target the counters are moved by pings, and down is what takes it out of a plan. See The circuit breaker.
  • A number or sip target'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 503 with a Retry-After. Then it is skipped with suppressed for exactly as long as that header named, and comes back on its own.
  • Three failed calls in a row send a target.down event 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 reasonFix it on
pausedThe target's status — or its endpoint's.
buyer_pausedThe buyer, not the target.
no_destinationThe destination: a missing number, SIP address or bid URL.
suppressedWhatever the endpoint is doing. lastError says what was seen.
closedThe target's hours, or the endpoint's.
stateThe accepted states. A caller with no known state is never skipped here.
concurrencyThe target's or the endpoint's concurrency cap.
capped, spend_cappedThe target's window caps and its daily revenue cap.
buyer_cappedThe buyer's caps, which sit above these.
duplicateThe target's duplicate window.
tag_filterThe campaign's filters — not the target.
no_bid, timeout, errorThe 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 mayA key may not
Change revenueCents, every cap, hoursSchedule and statesChange name, buyerId, type, destinationE164, sipUri, the rtb block, capCountsOn, timezone, durationThresholdSeconds or dedupeWindowSeconds
Pause and resume a target that is already active or pausedCreate a target, archive one, or restore one from archived
Reset its cap countersRun a test against the buyer
An attempt at the right-hand column answers 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:

TargetTypeWhy it exists
Acme Health — Dallas floornumberThe main room. Five lines, 40 connected calls a day, Texas only, $42.00 a converted call at 90 seconds.
Acme Health — OverflownumberA second number for what the floor cannot take. Same buyer, no state list, priced lower on the campaign with a route override.
Acme Health — RTBrtbTheir bidding endpoint, for the campaign where they want to price each call themselves.
  1. 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.
  2. 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.
  3. 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 suppressed while the other two carry on.
  4. 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.

Next steps