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 is a container, not a destination: a buyer owns one or more targets, and the calls go to those. 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.

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
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.
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,
      "targets": {
        "total": 4,
        "active": 3
      },
      "today": {
        "calls": 88,
        "connected": 66,
        "converted": 41,
        "connectRate": 0.75,
        "conversionRate": 0.6212,
        "revenueCents": 172200,
        "payoutCents": 112750
      },
      "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,
    "targets": {
      "total": 4,
      "active": 3
    },
    "today": {
      "calls": 88,
      "connected": 66,
      "converted": 41,
      "connectRate": 0.75,
      "conversionRate": 0.6212,
      "revenueCents": 172200,
      "payoutCents": 112750
    },
    "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. A buyer is created with no targets; add at least one before any call can reach it. 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

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",
    "concurrencyCap": 12,
    "dailyCap": 150,
    "externalRef": "CRM-4471"
  }'

Responses

  • 201The buyer that was created, with its counters at zero.
{
  "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,
    "targets": {
      "total": 0,
      "active": 0
    },
    "today": {
      "calls": 0,
      "connected": 0,
      "converted": 0,
      "connectRate": null,
      "conversionRate": null,
      "revenueCents": 0,
      "payoutCents": 0
    },
    "createdAt": "2026-09-20T18:50:11.000Z",
    "updatedAt": "2026-09-20T18:50:11.000Z"
  }
}

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.
402plan_upgrade_requiredThe workspace already holds as many buyers as its plan allows. details carries the limit and the current count.
409conflictAnother buyer of yours that is not archived already has that name. details.name says so.
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.
403forbiddenYou named no workspace and hold no owner or admin seat on any.
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,
    "targets": {
      "total": 4,
      "active": 3
    },
    "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.
403forbiddenYou named no workspace and hold no owner or admin seat on any.
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,
    "targets": {
      "total": 4,
      "active": 3
    },
    "today": {
      "calls": 88,
      "connected": 66,
      "converted": 41,
      "connectRate": 0.75,
      "conversionRate": 0.6212,
      "revenueCents": 172200,
      "payoutCents": 112750
    },
    "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.
403forbiddenYou named no workspace and hold no owner or admin seat on any.
404not_foundNo buyer with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.