API reference

Buyers

Read the buyers you sell calls to, their caps and their live counters; create, edit, pause and archive them through the console.

A buyer is a company you sell calls to. It has one destination — its phone, or its dashboard — which is a target of its own (primaryTargetId) made with the buyer in one save, and it may own further targets. What a buyer adds on top is a set of caps that count across every target it owns, and one switch that takes all of them out of routing at once. The concepts are in Buyers and A buyer's destination.

Two surfaces

EndpointSurfaceCredential
GET /api/v1/buyersREST APIAPI key with buyers:read
GET /api/v1/buyers/:idREST APIAPI key with buyers:read
POST /api/tracking/buyersConsoleSession token
PATCH /api/tracking/buyers/:idConsoleSession token
POST /api/tracking/buyers/:id/statusConsoleSession token
GET, PATCH /api/tracking/buyers/:id/destinationConsoleSession token
POST /api/tracking/buyers/:id/test-callConsoleSession token
GET, POST /api/tracking/buyers/:id/seats, PATCH, DELETE …/seats/:membershipId, invitation resend and withdrawConsoleSession token
GET, PATCH /api/tracking/buyers/:id/alertsConsoleSession token
GET /api/tracking/buyers/:id/numbers, POST …/draft-campaignConsoleSession token
GET /api/tracking/workspace/checklistConsoleSession token
The console calls the two reads as well, at /api/tracking/buyers and /api/tracking/buyers/:id. They are the same handlers, so the query and the answer are identical.

The buyer object

FieldTypeMeaning
iduuidThe buyer's id, used in every path below.
namestringWhat you call the buyer. It is unique in your workspace among buyers that are not archived, case-insensitively.
company, contactName, contactEmail, contactPhonestring or nullWho they are and who to ring. Contact details are for your team; the platform never writes to them.
statusstringactive, paused or archived.
timezonestringAn IANA zone, shown beside the buyer's figures.
concurrencyCap, hourlyCap, dailyCap, monthlyCapintegerCall caps across every target the buyer owns. 0 is unlimited.
dailyRevenueCapCentsintegerThe buyer's revenue cap for one UTC day, in cents. 0 is unlimited.
usageobjectThe live counters behind those caps: { liveCalls, hour, day, month, total, dayRevenueCents }.
liveCallsintegerCalls to this buyer right now.
externalRefstring or nullYour own reference for the buyer.
notesstring or nullFree text for your team.
primaryTargetIduuid or nullThe target behind the buyer's one destination; null for a buyer made before buyers were created with one.
destinationobject{ type, e164, seatName, ready, reason } — type is phone, dashboard or null; reason a code (no_destination, seat_missing, browser_not_allowed, seat_paused, softphone_not_ready, stale_heartbeat, mobile_not_allowed, paused, buyer_paused, suppressed). seatName is always null for an API key.
seatsobject{ active, invited } — the buyer's seat held, and invitations to it still out. Nothing else about a seat reaches a key.
callsToday, qualifiedToday, revenueTodayCentsintegerOn a list row, beside today.
settingsobjectOn a read and a create: { revealAfterQualified, allowSeatMobile, allowSeatBrowser }.
targetsobjectOn a list row and after an update: { total, active }, counting the buyer's targets that are not archived.
todayobjectOn a list row: the day's counts and money, on the workspace's calendar.
createdAt, updatedAttimestampWhen the buyer was created and last changed.
Read a buyer answers with the buyer and a separate targets array of full target objects, rather than the two counts.

Buyer caps and target caps

A buyer's caps and a target's caps are both checked, and the tighter one wins. A target is skipped with the reason capped when its own cap is full and buyer_capped when the buyer's is, so a waterfall always says which. Both sets of counters roll on UTC boundaries — the clock hour, the date, the first of the month — whatever time zone your workspace keeps, so a cap is a rate limit rather than a business day.

  • 0 means unlimited on every cap in this API. It does not mean "take no calls" — that is what paused is for.
  • A buyer's counters are reset on the buyer. Resetting a target's caps deliberately leaves the buyer's counters alone, so clearing one target cannot quietly open the buyer's whole budget.
  • dailyRevenueCapCents is counted at settlement, whatever a target's capCountsOn says about calls.

Endpoints

List buyers

GET/api/v1/buyers

Every buyer in your workspace, filtered and paged, each with its caps, its live counters, a count of its targets and the day so far. Archived buyers are left out unless you ask for them by name.

API key. Send Authorization: Bearer <api key>. See Authentication.Scopebuyers:read

Query parameters

NameTypeDescription
qOptionalstringMatches name, company, contactName or externalRef, case-insensitively, anywhere in the value.up to 120 characters
statusOptionalenumOne status.active, paused, archived · default: every status except archived
pageOptionalintegerPage number, from 1.default 1
pageSizeOptionalintegerRows per page.default 25 · at most 200; a larger value is clamped rather than refused
sortOptionalenumThe field to sort by. An unknown value falls back to the default rather than failing.createdAt, updatedAt, name, status · default createdAt
dirOptionalenumSort direction.asc, desc · default desc

Example request

curl "https://api.buy3.io/api/v1/buyers?status=active&pageSize=25" \
  -H "Authorization: Bearer $BUY3_API_KEY"

Responses

{
  "data": [
    {
      "id": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
      "name": "Acme Health",
      "company": "Acme Health Insurance LLC",
      "contactName": "Dana Reyes",
      "contactEmail": "dana@example.com",
      "contactPhone": "+1 555 0142",
      "status": "active",
      "timezone": "America/Chicago",
      "concurrencyCap": 12,
      "hourlyCap": 0,
      "dailyCap": 150,
      "monthlyCap": 0,
      "dailyRevenueCapCents": 0,
      "usage": {
        "liveCalls": 3,
        "hour": 9,
        "day": 88,
        "month": 1642,
        "total": 11204,
        "dayRevenueCents": 369600
      },
      "liveCalls": 3,
      "externalRef": "CRM-4471",
      "notes": null,
      "primaryTargetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
      "destination": {
        "type": "phone",
        "e164": "+12145550188",
        "seatName": null,
        "ready": true,
        "reason": null
      },
      "seats": {
        "active": 1,
        "invited": 0
      },
      "targets": {
        "total": 4,
        "active": 3
      },
      "today": {
        "calls": 88,
        "connected": 66,
        "converted": 41,
        "connectRate": 0.75,
        "conversionRate": 0.6212,
        "revenueCents": 172200,
        "payoutCents": 112750
      },
      "callsToday": 88,
      "qualifiedToday": 41,
      "revenueTodayCents": 172200,
      "createdAt": "2026-06-18T10:04:00.000Z",
      "updatedAt": "2026-09-19T11:02:44.000Z"
    }
  ],
  "page": 1,
  "pageSize": 25,
  "total": 4,
  "totalPages": 1
}

Errors

StatusCodeWhen
400validation_errorA filter has the wrong type, or status is not one of the three. details names the field.
401missing_key, invalid_keyNo bearer key was sent, or the key is malformed, unknown, or revoked with its grace period over. See Authentication.
402subscription_inactive, plan_upgrade_requiredThe workspace's subscription is not active, or its plan does not include call tracking and API access.
403insufficient_scope, ip_not_allowedThe key does not hold buyers:read, or the request came from an address outside the key's allowlist.
429rate_limitedThe key is over its per-minute limit. Wait for Retry-After seconds.

Read a buyer

GET/api/v1/buyers/:id

One buyer, and its targets in full — every target it owns that is not archived, oldest first. This is the request to make before changing a cap: it shows the buyer's counters and each target's beside them, so you can see which limit a call is actually meeting. An external RTB target in targets is blanked the same way it is in the Targets group: the bid header values and the body template read •••.

API key. Send Authorization: Bearer <api key>. See Authentication.Scopebuyers:read

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.

Example request

curl "https://api.buy3.io/api/v1/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b" \
  -H "Authorization: Bearer $BUY3_API_KEY"

Responses

{
  "buyer": {
    "id": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
    "name": "Acme Health",
    "company": "Acme Health Insurance LLC",
    "contactName": "Dana Reyes",
    "contactEmail": "dana@example.com",
    "contactPhone": "+1 555 0142",
    "status": "active",
    "timezone": "America/Chicago",
    "concurrencyCap": 12,
    "hourlyCap": 0,
    "dailyCap": 150,
    "monthlyCap": 0,
    "dailyRevenueCapCents": 0,
    "usage": {
      "liveCalls": 3,
      "hour": 9,
      "day": 88,
      "month": 1642,
      "total": 11204,
      "dayRevenueCents": 369600
    },
    "liveCalls": 3,
    "externalRef": "CRM-4471",
    "notes": null,
    "primaryTargetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
    "destination": {
      "type": "phone",
      "e164": "+12145550188",
      "seatName": null,
      "ready": true,
      "reason": null
    },
    "seats": {
      "active": 1,
      "invited": 0
    },
    "targets": {
      "total": 4,
      "active": 3
    },
    "today": {
      "calls": 88,
      "connected": 66,
      "converted": 41,
      "connectRate": 0.75,
      "conversionRate": 0.6212,
      "revenueCents": 172200,
      "payoutCents": 112750
    },
    "callsToday": 88,
    "qualifiedToday": 41,
    "revenueTodayCents": 172200,
    "createdAt": "2026-06-18T10:04:00.000Z",
    "updatedAt": "2026-09-19T11:02:44.000Z"
  },
  "targets": [
    {
      "id": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
      "name": "Acme Health — Dallas floor",
      "type": "number",
      "kind": "static",
      "status": "active",
      "externalRef": "acme-dal-01",
      "buyerId": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
      "buyer": {
        "id": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
        "name": "Acme Health",
        "status": "active"
      },
      "health": "healthy",
      "lastError": null,
      "consecutiveFailures": 0,
      "suppressedUntil": null,
      "revenueCents": 4200,
      "durationThresholdSeconds": 90,
      "destination": {
        "type": "number",
        "value": "+12145550188",
        "display": "+12145550188"
      },
      "rtb": null,
      "concurrencyCap": 5,
      "hourlyCap": 0,
      "dailyCap": 40,
      "monthlyCap": 0,
      "totalCap": 0,
      "dailyRevenueCapCents": 0,
      "capCountsOn": "connected",
      "usage": {
        "liveCalls": 2,
        "hour": 4,
        "day": 31,
        "month": 612,
        "total": 4188,
        "dayRevenueCents": 130200
      },
      "liveCalls": 2,
      "states": [
        "TX",
        "OK"
      ],
      "timezone": "America/Chicago",
      "hoursSchedule": null,
      "dedupeWindowSeconds": 86400,
      "createdAt": "2026-07-02T16:20:11.000Z",
      "updatedAt": "2026-09-19T11:02:44.000Z"
    }
  ]
}

Errors

StatusCodeWhen
404not_foundNo buyer with that id in your workspace. A malformed id, an id that does not exist and another workspace's buyer all answer exactly the same, before any query runs.
401missing_key, invalid_keyNo bearer key was sent, or the key is malformed, unknown, or revoked with its grace period over. See Authentication.
402subscription_inactive, plan_upgrade_requiredThe workspace's subscription is not active, or its plan does not include call tracking and API access.
403insufficient_scope, ip_not_allowedThe key does not hold buyers:read, or the request came from an address outside the key's allowlist.
429rate_limitedThe key is over its per-minute limit. Wait for Retry-After seconds.

Console API · Create a buyer

POST/api/tracking/buyers

Console API. This route takes a signed-in owner's or admin's session token, not an API key, and it may change with the console — treat the REST endpoints above as the stable surface. One save, one transaction: the buyer, its destination (a target and the line it rings) and — with invite — an invitation to its dashboard seat, emailed after the save commits. With a phone destination the buyer can take a call at once. Anything destination leaves out comes from your account's defaults for new buyers. How many buyers a workspace may hold depends on its plan; see pricing.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Request body

NameTypeDescription
nameRequiredstringWhat you call the buyer. It must be unique among your buyers that are not archived, case-insensitively.2 to 120 characters
companyOptionalstringThe legal or trading name, when it differs from name.up to 200 characters
contactNameOptionalstringWho to ring at the buyer.up to 200 characters
contactEmailOptionalstringAn email address. It is stored for your reference; the platform sends the buyer nothing.
contactPhoneOptionalstringA contact number. This is not where calls go — that is a target.up to 40 characters
timezoneOptionalstringAn IANA zone, used when a person reads the buyer's hours.up to 64 characters · default: the workspace's time zone
externalRefOptionalstringYour own id for this buyer — an account number in your CRM, say. It is never used for routing.up to 200 characters
notesOptionalstringFree text for your team.up to 4000 characters
concurrencyCapOptionalintegerHow many of this buyer's calls may be live at once, across every target it owns.0 to 100000 · 0 is unlimited
hourlyCapOptionalintegerCalls the buyer may take in one UTC clock hour.0 to 1000000 · 0 is unlimited
dailyCapOptionalintegerCalls in one UTC day.0 to 1000000 · 0 is unlimited
monthlyCapOptionalintegerCalls in one UTC month.0 to 10000000 · 0 is unlimited
dailyRevenueCapCentsOptionalintegerRevenue the buyer may generate in one UTC day, in cents. Counted at settlement.0 to 100000000000 · 0 is unlimited
revenueCentsOptionalintegerWhat a qualified call on the destination books — books only; the buyer pays you off-platform. Only with a destination.0 to 100000000
destinationOptionalobjectThe buyer's one destination: { type: "phone" | "dashboard", e164, callerIdPolicy, callerIdNumberId, whisperMode, whisperText, ringTimeoutSeconds, thresholdSeconds } — the fields of Change a buyer's destination. Omitted: a buyer with no destination, which routes nowhere until one is saved.
inviteOptionalobject{ email, name? } — an invitation to the buyer's seat. Required when destination.type is dashboard.

Example request

curl -X POST "https://api.buy3.io/api/tracking/buyers" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Health",
    "company": "Acme Health Insurance LLC",
    "contactName": "Dana Reyes",
    "contactEmail": "dana@example.com",
    "dailyCap": 150,
    "revenueCents": 4200,
    "destination": {
      "type": "phone",
      "e164": "+12145550188",
      "whisperMode": "press_to_accept",
      "thresholdSeconds": 90
    },
    "invite": {
      "email": "dana@example.com",
      "name": "Dana Reyes"
    }
  }'

Responses

  • 201The buyer that was created, with its counters at zero, its destination (the Destination tab's shape, or null) and — with invite — the invitation. warnings says when your default caller-ID number could not be used and the tracking number was chosen instead. The invitation's plaintext token is returned only on a development server.
{
  "buyer": {
    "id": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
    "name": "Acme Health",
    "company": "Acme Health Insurance LLC",
    "contactName": "Dana Reyes",
    "contactEmail": "dana@example.com",
    "contactPhone": "+1 555 0142",
    "status": "active",
    "timezone": "America/Chicago",
    "concurrencyCap": 12,
    "hourlyCap": 0,
    "dailyCap": 150,
    "monthlyCap": 0,
    "dailyRevenueCapCents": 0,
    "usage": {
      "liveCalls": 0,
      "hour": 0,
      "day": 0,
      "month": 0,
      "total": 0,
      "dayRevenueCents": 0
    },
    "liveCalls": 0,
    "externalRef": "CRM-4471",
    "notes": null,
    "primaryTargetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
    "destination": {
      "type": "phone",
      "e164": "+12145550188",
      "seatName": null,
      "ready": true,
      "reason": null
    },
    "seats": {
      "active": 1,
      "invited": 0
    },
    "targets": {
      "total": 0,
      "active": 0
    },
    "today": {
      "calls": 0,
      "connected": 0,
      "converted": 0,
      "connectRate": null,
      "conversionRate": null,
      "revenueCents": 0,
      "payoutCents": 0
    },
    "callsToday": 88,
    "qualifiedToday": 41,
    "revenueTodayCents": 172200,
    "createdAt": "2026-09-20T18:50:11.000Z",
    "updatedAt": "2026-09-20T18:50:11.000Z"
  },
  "destination": {
    "type": "phone",
    "e164": "+12145550188",
    "seatUserId": null,
    "seatName": null,
    "callerIdPolicy": "fixed",
    "callerIdNumberId": "0c4d2e1f-9a8b-4c7d-8e6f-5a4b3c2d1e0f",
    "callerIdNumber": {
      "id": "0c4d2e1f-9a8b-4c7d-8e6f-5a4b3c2d1e0f",
      "e164": "+18005550100",
      "formatted": "(800) 555-0100",
      "ready": true
    },
    "whisperMode": "press_to_accept",
    "whisperText": null,
    "ringTimeoutSeconds": 20,
    "thresholdSeconds": 90,
    "targetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
    "endpointId": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
    "ready": true,
    "reason": null,
    "changedAt": "2026-09-19T11:02:44.000Z",
    "changedBy": {
      "name": "Sam Ortiz",
      "via": "publisher"
    }
  },
  "invitation": {
    "id": "8e7d6c5b-4a39-4827-9615-04938271650a",
    "kind": "buyer",
    "email": "dana@example.com",
    "name": "Dana Reyes",
    "status": "pending",
    "sentAt": "2026-09-20T18:50:11.000Z",
    "expiresAt": "2026-09-27T18:50:11.000Z",
    "emailed": true
  }
}

Errors

StatusCodeWhen
400validation_errorA field has the wrong type or is out of range — a cap above its maximum, a name shorter than two characters. details names the field.
400bad_requestdetails keyed destination.*, invite.* or revenueCents: a phone with no number, a dashboard with no invitation, fixed with no number, a revenue with no destination.
402plan_upgrade_requiredThe workspace already holds as many buyers as its plan allows. details carries the limit and the current count.
404not_foundThe caller-ID number you named is another account's, or cannot be presented (Number not found).
409conflictAnother buyer of yours that is not archived already has that name. details.name says so.
409invite_pendingAn invitation to the same address for this buyer is already out.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active, and this is a write. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Update a buyer

PATCH/api/tracking/buyers/:id

Console API. Change any field of the create form. Send only what you are changing; anything you leave out keeps its value. Status has its own route, so an edit can never switch a buyer off by accident. Lowering a cap below what the buyer has already used today does not reverse anything that has happened — it simply means the next call is over the cap and the buyer's targets are skipped until the window rolls.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in.

Request body

NameTypeDescription
nameOptionalstringA new name. It must still be unique among your buyers that are not archived.2 to 120 characters
companyOptionalstringThe legal or trading name, when it differs from name.up to 200 characters
contactNameOptionalstringWho to ring at the buyer.up to 200 characters
contactEmailOptionalstringAn email address. It is stored for your reference; the platform sends the buyer nothing.
contactPhoneOptionalstringA contact number. This is not where calls go — that is a target.up to 40 characters
timezoneOptionalstringAn IANA zone, used when a person reads the buyer's hours.up to 64 characters · default: the workspace's time zone
externalRefOptionalstringYour own id for this buyer — an account number in your CRM, say. It is never used for routing.up to 200 characters
notesOptionalstringFree text for your team.up to 4000 characters
concurrencyCapOptionalintegerHow many of this buyer's calls may be live at once, across every target it owns.0 to 100000 · 0 is unlimited
hourlyCapOptionalintegerCalls the buyer may take in one UTC clock hour.0 to 1000000 · 0 is unlimited
dailyCapOptionalintegerCalls in one UTC day.0 to 1000000 · 0 is unlimited
monthlyCapOptionalintegerCalls in one UTC month.0 to 10000000 · 0 is unlimited
dailyRevenueCapCentsOptionalintegerRevenue the buyer may generate in one UTC day, in cents. Counted at settlement.0 to 100000000000 · 0 is unlimited

Example request

curl -X PATCH "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "dailyCap": 200,
    "notes": "Raised for the Q4 push"
  }'

Responses

  • 200The buyer as it now stands, with its target counts. today is not included on an update.
{
  "buyer": {
    "id": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
    "name": "Acme Health",
    "company": "Acme Health Insurance LLC",
    "contactName": "Dana Reyes",
    "contactEmail": "dana@example.com",
    "contactPhone": "+1 555 0142",
    "status": "active",
    "timezone": "America/Chicago",
    "concurrencyCap": 12,
    "hourlyCap": 0,
    "dailyCap": 200,
    "monthlyCap": 0,
    "dailyRevenueCapCents": 0,
    "usage": {
      "liveCalls": 3,
      "hour": 9,
      "day": 88,
      "month": 1642,
      "total": 11204,
      "dayRevenueCents": 369600
    },
    "liveCalls": 3,
    "externalRef": "CRM-4471",
    "notes": null,
    "primaryTargetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
    "destination": {
      "type": "phone",
      "e164": "+12145550188",
      "seatName": null,
      "ready": true,
      "reason": null
    },
    "seats": {
      "active": 1,
      "invited": 0
    },
    "targets": {
      "total": 4,
      "active": 3
    },
    "callsToday": 88,
    "qualifiedToday": 41,
    "revenueTodayCents": 172200,
    "createdAt": "2026-06-18T10:04:00.000Z",
    "updatedAt": "2026-09-20T18:52:30.000Z"
  }
}

Errors

StatusCodeWhen
400validation_errorA field has the wrong type or is out of range.
400bad_requestNothing to update — the body carried no change.
409conflictThe buyer is archived, or the name you sent belongs to another of your buyers.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active, and this is a write. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Change a buyer's status

POST/api/tracking/buyers/:id/status

Console API. Move a buyer between active, paused and archived. Pausing writes one row. None of the buyer's targets is edited: each simply becomes ineligible, with the skip reason buyer_paused, until the buyer is resumed — so routing goes back exactly as it was, with no route to rebuild. The response says how many active targets that covers.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in.

Request body

NameTypeDescription
statusRequiredenumThe status to move to.active, paused, archived
noteOptionalstringWhy, for the activity log and for whoever reads it later.up to 500 characters

Example request

curl -X POST "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/status" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "paused",
    "note": "Contract under review"
  }'

Responses

  • 200The buyer, whether anything changed, and — when it did — how many of its active targets the change affects. A repeat of the same request answers changed: false and leaves targetsAffected out.
{
  "buyer": {
    "id": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
    "name": "Acme Health",
    "company": "Acme Health Insurance LLC",
    "contactName": "Dana Reyes",
    "contactEmail": "dana@example.com",
    "contactPhone": "+1 555 0142",
    "status": "paused",
    "timezone": "America/Chicago",
    "concurrencyCap": 12,
    "hourlyCap": 0,
    "dailyCap": 150,
    "monthlyCap": 0,
    "dailyRevenueCapCents": 0,
    "usage": {
      "liveCalls": 3,
      "hour": 9,
      "day": 88,
      "month": 1642,
      "total": 11204,
      "dayRevenueCents": 369600
    },
    "liveCalls": 3,
    "externalRef": "CRM-4471",
    "notes": null,
    "primaryTargetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
    "destination": {
      "type": "phone",
      "e164": "+12145550188",
      "seatName": null,
      "ready": true,
      "reason": null
    },
    "seats": {
      "active": 1,
      "invited": 0
    },
    "targets": {
      "total": 4,
      "active": 3
    },
    "today": {
      "calls": 88,
      "connected": 66,
      "converted": 41,
      "connectRate": 0.75,
      "conversionRate": 0.6212,
      "revenueCents": 172200,
      "payoutCents": 112750
    },
    "callsToday": 88,
    "qualifiedToday": 41,
    "revenueTodayCents": 172200,
    "createdAt": "2026-06-18T10:04:00.000Z",
    "updatedAt": "2026-09-20T18:54:02.000Z"
  },
  "changed": true,
  "targetsAffected": 3
}

Errors

StatusCodeWhen
400validation_errorstatus is missing or is not one of the three.
402plan_upgrade_requiredRestoring an archived buyer would take the workspace past the number of buyers its plan allows.
409conflictYou are restoring an archived buyer whose name has since been taken by another buyer. Rename one of them first.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active, and this is a write. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Read a buyer's destination

GET/api/tracking/buyers/:id/destination

Console API. The buyer's ONE destination — the Destination tab — and the three buyer settings its seat works with. ready and reason are the router's own answer about the line (see Ready, or why not); changedBy says who saved it last, you or the buyer's own seat.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Example request

curl "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/destination" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200The destination and the buyer settings.
{
  "destination": {
    "type": "phone",
    "e164": "+12145550188",
    "seatUserId": null,
    "seatName": null,
    "callerIdPolicy": "fixed",
    "callerIdNumberId": "0c4d2e1f-9a8b-4c7d-8e6f-5a4b3c2d1e0f",
    "callerIdNumber": {
      "id": "0c4d2e1f-9a8b-4c7d-8e6f-5a4b3c2d1e0f",
      "e164": "+18005550100",
      "formatted": "(800) 555-0100",
      "ready": true
    },
    "whisperMode": "press_to_accept",
    "whisperText": null,
    "ringTimeoutSeconds": 20,
    "thresholdSeconds": 90,
    "targetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
    "endpointId": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
    "ready": true,
    "reason": null,
    "changedAt": "2026-09-19T11:02:44.000Z",
    "changedBy": {
      "name": "Sam Ortiz",
      "via": "publisher"
    }
  },
  "settings": {
    "revealAfterQualified": false,
    "allowSeatMobile": true,
    "allowSeatBrowser": true
  }
}

Errors

StatusCodeWhen
404not_foundNo buyer with that id in your workspace. A malformed id, an id that does not exist and another workspace's buyer all answer exactly the same, before any query runs.
401unauthorizedNo session token, or one that has expired.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Change a buyer's destination

PATCH/api/tracking/buyers/:id/destination

Console API. Rewrites the ONE destination in place — phone ↔ dashboard changes the same line, so its caps, history and routes stay — and any of the three buyer settings. Send any subset, flat or as { destination: {…}, settings: {…} }. The buyer row is locked for the save and the last save wins, yours or the seat's. A buyer with no destination yet is given one here, from your account's defaults for anything not sent.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Request body

NameTypeDescription
typeOptionalenumWhere calls ring.phone, dashboard
e164OptionalstringTheir phone number, E.164. Required for phone; refused for dashboard.
callerIdPolicyOptionalenumWhat their phone shows as the caller. Never the caller's own number.tracking_number, fixed
callerIdNumberIdOptionaluuidThe number fixed presents: one of yours, active, voice-enabled and wired to the platform. Sent alone it means fixed; null means the tracking number. Anything else is a 404.
whisperModeOptionalenumBefore the caller is put through. press_to_accept plays on a phone destination only.none, announce, press_to_accept
whisperTextOptionalstringYour own prompt; one that never names the key has "Press 1 to accept" added.up to 500 characters
ringTimeoutSecondsOptionalintegerHow long one ring lasts.5 to 120
thresholdSecondsOptionalintegerQualifies at: the talk time before a call counts.0 to 7200
revealAfterQualifiedOptionalbooleanShow the caller's number once a call qualifies — works together with the seat's canSeeCaller.
allowSeatMobileOptionalbooleanLet their seat ring their own verified mobile.
allowSeatBrowserOptionalbooleanLet their seat take calls in the browser — what lets a dashboard destination take calls at all.

Example request

curl -X PATCH "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/destination" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "whisperMode": "none",
    "revealAfterQualified": true
  }'

Responses

  • 200The destination and settings as saved, with warnings when something was changed on the way in.
{
  "destination": {
    "type": "phone",
    "e164": "+12145550188",
    "seatUserId": null,
    "seatName": null,
    "callerIdPolicy": "fixed",
    "callerIdNumberId": "0c4d2e1f-9a8b-4c7d-8e6f-5a4b3c2d1e0f",
    "callerIdNumber": {
      "id": "0c4d2e1f-9a8b-4c7d-8e6f-5a4b3c2d1e0f",
      "e164": "+18005550100",
      "formatted": "(800) 555-0100",
      "ready": true
    },
    "whisperMode": "none",
    "whisperText": null,
    "ringTimeoutSeconds": 20,
    "thresholdSeconds": 90,
    "targetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
    "endpointId": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
    "ready": true,
    "reason": null,
    "changedAt": "2026-09-27T09:12:40.000Z",
    "changedBy": {
      "name": "Sam Ortiz",
      "via": "publisher"
    }
  },
  "settings": {
    "revealAfterQualified": false,
    "allowSeatMobile": true,
    "allowSeatBrowser": true
  }
}

Errors

StatusCodeWhen
400bad_requestNothing to update, or a field is wrong — details is keyed destination.<field> or settings.<field>.
404not_foundNo such buyer — or a fixed caller ID naming another account's number, or one that cannot be presented (Number not found).
409conflictThe buyer is archived.
409seat_requiredSwitching to the dashboard while nobody holds the buyer's seat and no invitation to it is out — nothing could ever ring.
409seat_mobile_destinationThe save would leave calls ringing the seat's own verified mobile with allowSeatMobile off. details.fields names the switch or destination.e164.
409seat_browser_destinationThe save would leave the dashboard as the destination with allowSeatBrowser off. details.fields names the switch or destination.type.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active, and this is a write. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Send a test call

POST/api/tracking/buyers/:id/test-call

Console API. Rings the buyer's destination for real, once — their phone, or their seat's browser — from the buyer's caller-ID number, else your first number that can make a call. Whoever answers hears "This is a test call from <your account>" and is hung up. The request waits for the end (at most the ring time plus 30 seconds). Not a call: no call row, no charge, nothing in reports; it is audited on the buyer with its outcome. Once per buyer per minute. No body.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Example request

curl -X POST "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/test-call" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200outcome is answered, no_answer, busy, declined, failed or timeout; cause is the carrier's hang-up cause or ours; detail a sentence. A voicemail box that answers reads answered.
{
  "outcome": "answered",
  "rangSeconds": 4,
  "cause": null,
  "detail": "Acme Health's phone answered and heard the test message."
}

Errors

StatusCodeWhen
404not_foundNo buyer with that id in your workspace. A malformed id, an id that does not exist and another workspace's buyer all answer exactly the same, before any query runs.
409buyer_archivedThe buyer is archived.
409no_destinationThe buyer has no destination to ring.
409seat_not_readyThe destination is the dashboard and a real call would not ring the browser now. details.reason is the reason code: browser_not_allowed, seat_missing, seat_busy, seat_paused, softphone_not_ready or stale_heartbeat.
409mobile_not_allowedThe destination is the seat's own verified mobile while allowSeatMobile is off — a real call would skip it too.
409no_caller_id_numberYou have no number the call can be placed from.
429rate_limitedA test to this buyer in the last minute. details.retryAfterSeconds says how long to wait.
503telephony_not_configured, webhook_unreachableTelephony is not set up, or the carrier could not report back.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active, and this is a write. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Read a buyer's seat

GET/api/tracking/buyers/:id/seats

Console API. The buyer's one seat and its invitation. seat.delivery is worked out from the destination, never stored: browser when the dashboard is bound to this login, mobile when the destination is the mobile this login proved, null for anything else (a number you typed — the console reads that as "their phone"). invitation is the one that is out, or — while nobody holds the seat — the newest that lapsed or was declined.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Example request

curl "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/seats" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200{ seat, invitation }, either of which may be null.
{
  "seat": {
    "membershipId": "4b3a2918-0f7e-4d6c-9b5a-493827160514",
    "userId": "7c6b5a49-3827-4160-9f5e-4d3c2b1a0f9e",
    "name": "Dana Reyes",
    "email": "dana@example.com",
    "status": "active",
    "canSeeCaller": false,
    "canEditDestination": true,
    "delivery": null,
    "createdAt": "2026-09-21T15:02:11.000Z"
  },
  "invitation": null
}

Errors

StatusCodeWhen
404not_foundNo buyer with that id in your workspace. A malformed id, an id that does not exist and another workspace's buyer all answer exactly the same, before any query runs.
401unauthorizedNo session token, or one that has expired.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Invite someone to a buyer's seat

POST/api/tracking/buyers/:id/seats

Console API. Emails an invitation to the buyer's dashboard, after the save commits. One seat per buyer: while a seat is held or an invitation to it is out — to any address — a second is refused. The plaintext token (with a note) comes back only on a development server, never in production.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Request body

NameTypeDescription
emailRequiredstringWho to invite. Only a login with this address can accept.
nameOptionalstringTheir name, for the email and the Seats tab.up to 200 characters

Example request

curl -X POST "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/seats" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "dana@example.com",
    "name": "Dana Reyes"
  }'

Responses

  • 201The invitation, and whether the email went out.
{
  "seat": null,
  "invitation": {
    "id": "8e7d6c5b-4a39-4827-9615-04938271650a",
    "email": "dana@example.com",
    "name": "Dana Reyes",
    "status": "pending",
    "sentAt": "2026-09-27T09:00:00.000Z",
    "expiresAt": "2026-10-04T09:00:00.000Z",
    "canResend": true,
    "canRevoke": true
  },
  "emailed": true
}

Errors

StatusCodeWhen
400validation_erroremail is missing or is not an address.
404not_foundNo buyer with that id in your workspace. A malformed id, an id that does not exist and another workspace's buyer all answer exactly the same, before any query runs.
409seat_existsThe seat is held (details.membershipId) or an invitation to it is out (details.invitationId, email, expiresAt).
409buyer_archivedThe buyer is archived.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active, and this is a write. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Change a seat's switches

PATCH/api/tracking/buyers/:id/seats/:membershipId

Console API. The seat's two switches. Audited at high severity on the buyer.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.
membershipIdRequireduuidThe seat's membershipId, from Read a buyer's seat.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Request body

NameTypeDescription
canSeeCallerOptionalbooleanCan see the caller's number after a call qualifies. Default off; the buyer's revealAfterQualified must be on too.
canEditDestinationOptionalbooleanCan change their own destination. Default on.

Example request

curl -X PATCH "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/seats/4b3a2918-0f7e-4d6c-9b5a-493827160514" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "canSeeCaller": true
  }'

Responses

  • 200The seat as it now stands. warnings says when canSeeCaller is on while the buyer's revealAfterQualified is off — the seat still sees only the last four digits.
{
  "seat": {
    "membershipId": "4b3a2918-0f7e-4d6c-9b5a-493827160514",
    "userId": "7c6b5a49-3827-4160-9f5e-4d3c2b1a0f9e",
    "name": "Dana Reyes",
    "email": "dana@example.com",
    "status": "active",
    "canSeeCaller": true,
    "canEditDestination": true,
    "delivery": null,
    "createdAt": "2026-09-21T15:02:11.000Z"
  },
  "invitation": null,
  "warnings": [
    "Acme Health's \"reveal after qualifying\" is off, so the seat still sees only the last four digits — turn it on in the Destination tab."
  ]
}

Errors

StatusCodeWhen
400bad_requestNothing to update.
404not_foundNo such buyer, or no such seat on it.
409buyer_archivedThe buyer is archived.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active, and this is a write. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Remove a buyer's seat

DELETE/api/tracking/buyers/:id/seats/:membershipId

Console API. Takes the seat away; the login stays a login. A dashboard destination bound to it is unbound in the same transaction and reads seat_missing; a phone destination is left as it is, even the mobile the seat proved; an alert number that was that mobile is cleared. note says what, if anything, you should change next. Allowed on an archived buyer.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.
membershipIdRequireduuidThe seat's membershipId, from Read a buyer's seat.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Example request

curl -X DELETE "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/seats/4b3a2918-0f7e-4d6c-9b5a-493827160514" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200Who was removed, the destination as it now stands, and whether it was unbound or the alert number cleared.
{
  "seat": null,
  "invitation": null,
  "removed": {
    "membershipId": "4b3a2918-0f7e-4d6c-9b5a-493827160514",
    "userId": "7c6b5a49-3827-4160-9f5e-4d3c2b1a0f9e",
    "name": "Dana Reyes",
    "email": "dana@example.com"
  },
  "destination": {
    "type": "phone",
    "e164": "+12145550188",
    "seatName": null,
    "ready": true,
    "reason": null
  },
  "destinationUnbound": false,
  "alertsCleared": false,
  "note": null
}

Errors

StatusCodeWhen
404not_foundNo such buyer, or no such seat on it.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active, and this is a write. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Resend a seat invitation

POST/api/tracking/buyers/:id/seats/invitations/:invitationId/resend

Console API. A fresh link and seven fresh days, to the same address, for a pending or expired invitation. The old link stops working.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.
invitationIdRequireduuidThe invitation's id, from Read a buyer's seat.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Example request

curl -X POST "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/seats/invitations/8e7d6c5b-4a39-4827-9615-04938271650a/resend" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200The invitation, pending again.
{
  "invitation": {
    "id": "8e7d6c5b-4a39-4827-9615-04938271650a",
    "email": "dana@example.com",
    "name": "Dana Reyes",
    "status": "pending",
    "sentAt": "2026-09-27T09:00:00.000Z",
    "expiresAt": "2026-10-04T09:00:00.000Z",
    "canResend": true,
    "canRevoke": true
  }
}

Errors

StatusCodeWhen
404not_foundAnother buyer's or account's invitation, or none.
409seat_existsThe seat is held, or another invitation to it is out.
409buyer_archivedThe buyer is archived.
409invitation_accepted, invitation_revoked, invitation_declinedThe invitation is finished — send a new one.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active, and this is a write. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Withdraw a seat invitation

DELETE/api/tracking/buyers/:id/seats/invitations/:invitationId

Console API. The emailed link stops working at once.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.
invitationIdRequireduuidThe invitation's id, from Read a buyer's seat.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Example request

curl -X DELETE "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/seats/invitations/8e7d6c5b-4a39-4827-9615-04938271650a" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200The invitation, revoked.
{
  "invitation": {
    "id": "8e7d6c5b-4a39-4827-9615-04938271650a",
    "email": "dana@example.com",
    "name": "Dana Reyes",
    "status": "revoked",
    "sentAt": "2026-09-27T09:00:00.000Z",
    "expiresAt": "2026-10-04T09:00:00.000Z",
    "canResend": false,
    "canRevoke": false
  },
  "revoked": true
}

Errors

StatusCodeWhen
404not_foundAnother buyer's or account's invitation, or none.
409invitation_accepted, invitation_revoked, invitation_declinedThe invitation is already finished.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active, and this is a write. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Read a buyer's SMS alerts

GET/api/tracking/buyers/:id/alerts

Console API. The SMS alerts tab: the switch, the number they come from and go to, whether a text would go out now (ready, reason), and the newest 50 alerts, skipped ones included.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Example request

curl "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/alerts" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200The alert settings and history. reason is alerts_off, no_alert_number, invalid_alert_number, opted_out, no_from_number or texting_not_configured when ready is false.
{
  "smsAlerts": true,
  "fromNumberId": null,
  "fromNumber": null,
  "sender": {
    "id": "5f4e3d2c-1b0a-4987-8654-3210fedcba98",
    "e164": "+14805550166",
    "formatted": "(480) 555-0166",
    "chosen": false
  },
  "fromNumbers": [
    {
      "id": "5f4e3d2c-1b0a-4987-8654-3210fedcba98",
      "e164": "+14805550166",
      "formatted": "(480) 555-0166"
    }
  ],
  "alertE164": "+12145550117",
  "optedOut": false,
  "template": "{campaign}: qualified call, {talkTime} talk time, caller {caller}. {link}",
  "ready": true,
  "reason": null,
  "history": [
    {
      "id": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a",
      "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
      "sentAt": "2026-09-27T14:07:02.000Z",
      "createdAt": "2026-09-27T14:07:01.000Z",
      "campaignName": "Medicare — Inbound",
      "callerMasked": "***-***-0142",
      "toE164": "+12145550117",
      "fromE164": "+14805550166",
      "status": "delivered",
      "chargedCents": 1,
      "reason": null,
      "error": null
    }
  ]
}

Errors

StatusCodeWhen
404not_foundNo buyer with that id in your workspace. A malformed id, an id that does not exist and another workspace's buyer all answer exactly the same, before any query runs.
401unauthorizedNo session token, or one that has expired.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Change a buyer's SMS alerts

PATCH/api/tracking/buyers/:id/alerts

Console API. Each field optional. A switch left on with no alert number, a number that replied STOP, or nothing to send from is saved and said in warnings — each such alert is recorded as skipped, with why.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Request body

NameTypeDescription
smsAlertsOptionalbooleanText this buyer when one of their calls qualifies.
alertE164OptionalstringWhere the texts go: a US (+1) mobile. You attest it is the buyer's; nobody texts it a code. null clears it.
fromNumberIdOptionaluuidWhich of your numbers sends them: yours, active, SMS-enabled, not quarantined. null is automatic — your oldest such number.

Example request

curl -X PATCH "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/alerts" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "smsAlerts": true,
    "alertE164": "+12145550117"
  }'

Responses

  • 200As the read, with warnings when a text could not go out now.
{
  "smsAlerts": true,
  "fromNumberId": null,
  "fromNumber": null,
  "sender": {
    "id": "5f4e3d2c-1b0a-4987-8654-3210fedcba98",
    "e164": "+14805550166",
    "formatted": "(480) 555-0166",
    "chosen": false
  },
  "fromNumbers": [
    {
      "id": "5f4e3d2c-1b0a-4987-8654-3210fedcba98",
      "e164": "+14805550166",
      "formatted": "(480) 555-0166"
    }
  ],
  "alertE164": "+12145550117",
  "optedOut": false,
  "template": "{campaign}: qualified call, {talkTime} talk time, caller {caller}. {link}",
  "ready": true,
  "reason": null,
  "history": [
    {
      "id": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a",
      "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
      "sentAt": "2026-09-27T14:07:02.000Z",
      "createdAt": "2026-09-27T14:07:01.000Z",
      "campaignName": "Medicare — Inbound",
      "callerMasked": "***-***-0142",
      "toE164": "+12145550117",
      "fromE164": "+14805550166",
      "status": "delivered",
      "chargedCents": 1,
      "reason": null,
      "error": null
    }
  ]
}

Errors

StatusCodeWhen
400bad_requestNothing to update, or details.alertE164 — not a US mobile.
404not_foundNo such buyer — or details.fromNumberId: a number that is not yours or cannot send texts (one answer for all).
409buyer_archivedThe buyer is archived.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active, and this is a write. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Numbers that reach a buyer

GET/api/tracking/buyers/:id/numbers

Console API. Your numbers whose campaign has a switched-on route to one of this buyer's targets — worked out from the routing plans, never stored on the number — and the campaigns that reach the buyer. After buying a number, assign it to one of campaigns; when there are none, draft one with the next endpoint.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Example request

curl "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/numbers" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200data: the numbers (each with its campaign and every buyer it reaches), at most 500, by number. campaigns: { id, name, status } of each campaign that reaches the buyer.
{
  "data": [
    {
      "id": "0c4d2e1f-9a8b-4c7d-8e6f-5a4b3c2d1e0f",
      "e164": "+18005550100",
      "formatted": "(800) 555-0100",
      "status": "active",
      "role": "tracking",
      "campaign": {
        "id": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
        "name": "Medicare — Inbound",
        "status": "live"
      },
      "voiceEnabled": true,
      "smsEnabled": false,
      "callerIdReady": true,
      "reaches": [
        {
          "buyerId": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
          "name": "Acme Health"
        }
      ],
      "…": "…"
    }
  ],
  "campaigns": [
    {
      "id": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
      "name": "Medicare — Inbound",
      "status": "live"
    }
  ]
}

Errors

StatusCodeWhen
404not_foundNo buyer with that id in your workspace. A malformed id, an id that does not exist and another workspace's buyer all answer exactly the same, before any query runs.
401unauthorizedNo session token, or one that has expired.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Draft a campaign for a buyer

POST/api/tracking/buyers/:id/draft-campaign

Console API. A draft campaign whose routing plan is one route: this buyer's destination, priority 1. Setting it live stays the campaign's own step, behind its usual checks. Counts against your plan's campaign allowance.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Path parameters

NameTypeDescription
idRequireduuidThe buyer's id, as returned by List buyers.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Request body

NameTypeDescription
nameOptionalstringThe campaign's name.1 to 120 characters · default "<buyer name> calls"
verticalIdOptionalstringA vertical you can use; default the one your newest campaign uses.

Example request

curl -X POST "https://api.buy3.io/api/tracking/buyers/f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b/draft-campaign" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Health calls"
  }'

Responses

  • 201The campaign, in its detail shape with its setup checklist.
{
  "campaign": {
    "id": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
    "name": "Acme Health calls",
    "status": "draft",
    "kind": "tracking",
    "…": "…"
  }
}

Errors

StatusCodeWhen
404not_foundNo buyer with that id in your workspace. A malformed id, an id that does not exist and another workspace's buyer all answer exactly the same, before any query runs.
402plan_upgrade_requiredYour plan's campaign allowance is full.
409conflictThe buyer is archived.
409no_destinationThe buyer has no destination to route to.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active, and this is a write. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · The setup checklist

GET/api/tracking/workspace/checklist

Console API. The five steps Overview shows until the first qualified call, each read from what the account holds — nothing is ticked by hand. href is a path under the console, like an alert's to. complete is the last step: once a call qualifies, the checklist is done for good.

Console session. Called by the console with the signed-in user's session. Use the REST API with an API key for your own integrations.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in. Only needed when you hold an owner or admin seat on more than one.

Example request

curl "https://api.buy3.io/api/tracking/workspace/checklist" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200The steps, the next one to do, and whether it is complete.
{
  "steps": [
    {
      "key": "number",
      "label": "Buy a number",
      "done": true,
      "href": "numbers",
      "to": "numbers"
    },
    {
      "key": "campaign",
      "label": "Create a campaign",
      "done": true,
      "href": "campaigns",
      "to": "campaigns"
    },
    {
      "key": "buyer",
      "label": "Add a buyer",
      "done": true,
      "href": "buyers",
      "to": "buyers"
    },
    {
      "key": "route",
      "label": "Route a campaign to a buyer",
      "done": false,
      "href": "campaigns/6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d?tab=routing",
      "to": "campaigns/6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d?tab=routing"
    },
    {
      "key": "first_call",
      "label": "Take the first qualified call",
      "done": false,
      "href": "calls",
      "to": "calls"
    }
  ],
  "nextStep": "route",
  "complete": false
}

Errors

StatusCodeWhen
401unauthorizedNo session token, or one that has expired.
402plan_upgrade_requiredThe workspace's plan does not include call tracking. details.feature is callTracking.
404not_foundYou hold no owner or admin seat to act from — an agent seat, a buyer seat or no seat at all — or you named a workspace with agencyId that is not yours. The same answer as a workspace that does not exist.