API reference

API keys

List and create API keys, rotate and revoke them, and read the log of every request a key has made.

An API key is what a program of yours uses to reach the REST API. This group mints keys, changes what they may do, rotates them on a schedule and kills them when something has gone wrong — and shows you, request by request, what each one has actually been doing. The advice on scoping is in API keys.

A console surface

Every endpoint on this page is the console's own, under /api/tracking, called with the session token of a signed-in owner or admin. A key cannot manage keys. There is no route on the REST API for any of this, so a leaked key can never mint itself a wider one, and a compromised integration cannot cover its tracks by reading or clearing the request log.

Base URL
https://api.buy3.io/api/tracking
  • Every request may carry an optional agencyId (a UUID, in the query string or the body). Leave it out when you administer one workspace.
  • Creating, editing and rotating need a plan that includes API access. Revoking never does — "upgrade to revoke your leaked key" is not a sentence this platform says.
  • How many keys a workspace may hold is a plan limit; see pricing. It counts keys that have not been revoked, so a rotated key inside its grace window does not hold a slot and a workspace at its limit can always rotate.
  • Every write here is recorded in the activity log under the kind api_key, at high severity. A rotated production key is indistinguishable from a stolen one until somebody can point at the entry that says who rotated it.

What you can do

OperationNotes
List keysEach key's label, prefix, scopes, restrictions, when it was last used and from where. Never the key itself.
Create a keyA label, its scopes, and optionally an address allowlist and a slower rate limit. The key is in that response only.
Update a keyThe label, the scopes, the allowlist and the rate limit. Only an active key can be edited.
Rotate a keyMints a successor and starts a grace window on the old one, so an integration can be moved over without a gap.
Revoke a keyThe key stops working at once, grace window and all.
Read the request logEvery request a key made, refusals included — one key at a time, or all of them together.

The key object

FieldTypeMeaning
iduuidThe key's id. It is not the key.
labelstringYour name for it. Name it after the program that holds it, so a request log reads as an accusation of something specific.
keyPrefixstringb3_live_ or b3_sandbox_ and the first four characters of the secret — enough to recognise a key in a log, useless as a credential.
scopesstring[]What it may do. See below.
envstringlive or sandbox. It cannot be changed.
statestringactive, grace or revoked. See below.
allowCidrsstring[]The addresses it may be used from. An empty list means anywhere.
rateLimitPerMinuteinteger | nullIts own per-minute limit. null means the platform default.
useCountintegerHow many requests it has ever made.
graceUntilISO 8601 | nullWhen a rotated key stops answering.
rotatedFromIduuid | nullThe key this one succeeded.
lastUsedAt, lastUsedIpISO 8601, string | nullWhen and from where it was last seen. The fastest way to find out whether anything still holds a key you want to kill.
revokedAtISO 8601 | nullWhen it was revoked. A rotated key has this set while it is still in grace.
createdBy, createdAtuuid | null, ISO 8601Who minted it and when.
The key's own secret is never in this object, or in any other. Only the response that minted it ever carries it.

The three states

stateAnswers requestsHow it got there
activeYes.It was created, and nothing has happened to it.
graceYes, until graceUntil, and not a second longer.It was rotated. Revoked on paper, still answering, so the integration holding it has time to move.
revokedNo.It was revoked, or its grace window ran out.
Only an active key can be edited. Editing a key on its way out would be widening a credential somebody has already decided to retire, so it answers 409 and points at the successor.

Scopes

A scope reads resource:verb. There are two implications and no more: * is every scope, and x:write includes x:read. A route that accepts several scopes accepts any of them. GET /api-keys returns the whole vocabulary beside the page of keys, each scope with the sentence that describes it, so an editor never has to hardcode the list.

ScopeOpens
calls:readList calls and the calls in progress, read one call with its routing waterfall and recordings, and export the call log as CSV.
calls:writeReport a conversion, add tags to a call, and adjust its recorded revenue or payout.
campaigns:readRead campaigns and their routing plans.
campaigns:writeChange a campaign's routing plan, and pause or resume a campaign.
targets:readRead targets, their caps and their live counters.
targets:writeChange a target's price, caps, hours and states, pause or resume it, and reset its cap counters.
buyers:readRead buyers, their caps and their targets.
publishers:readRead publishers and the payout terms on each campaign.
numbers:readRead tracking numbers and what each is assigned to.
reports:readRun summary and time-series reports.
pings:readRead the ping log: inbound pings from publishers and outbound pings to buyers.
blocklist:writeList, add and remove blocked callers.
*Everything this API can do, including scopes added later.
Each sentence says exactly what its scope opens and nothing more; a test holds the two together, so a route added without a word for it fails the build. What a key can never do — create a campaign, a buyer, a target or a number, change where a target's calls go, run a target test, or export pings and reports — is in Scopes.

The request log

Every request a recognised key makes is written down, refusals included: the method, the path, the parsed query string, the status, the error code, how long it took, the source address and the user agent. That is what answers "why has my integration been getting 401s since Tuesday" — because it is still sending the key you rotated.

  • Bodies are never stored. There is nothing in this log that needs redacting.
  • A request that never presented a recognisable key is not logged at all — there is no key to file it under.
  • A refusal thrown before the key was recognised has no row either. A refusal thrown after it was — a scope, a rate limit — has one, with its errorCode.

Endpoints

List keys

GET/api/tracking/api-keys

The workspace's keys, newest first, with the scope vocabulary beside them in scopes. Changes nothing, and never returns a key.

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
envOptionalstringOnly keys for this environment.live or sandbox
statusOptionalstringactive means not revoked. revoked includes keys still inside a rotation grace window.active or revoked
qOptionalstringMatches part of the label or the prefix, ignoring case.up to 200 characters
pageOptionalintegerThe page to return.from 1, default 1
pageSizeOptionalintegerRows per page. A larger value is clamped, not refused.1–200 · default 25

Example request

curl "https://api.buy3.io/api/tracking/api-keys?env=live&status=active" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200A page of keys, with scopes carrying the whole vocabulary and its descriptions. The example abbreviates that list.
{
  "data": [
    {
      "id": "9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f",
      "label": "BI warehouse sync",
      "keyPrefix": "b3_live_Qx7m",
      "scopes": [
        "calls:read",
        "reports:read"
      ],
      "env": "live",
      "state": "active",
      "ownerAgencyId": "8d6f2b1c-0a94-4e73-b5d8-1c2e3f4a5b60",
      "allowCidrs": [
        "203.0.113.0/24"
      ],
      "rateLimitPerMinute": 60,
      "useCount": 18204,
      "graceUntil": null,
      "rotatedFromId": null,
      "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
      "lastUsedAt": "2026-09-20T14:02:51.000Z",
      "lastUsedIp": "203.0.113.24",
      "revokedAt": null,
      "createdAt": "2026-08-02T11:20:41.000Z"
    }
  ],
  "page": 1,
  "pageSize": 25,
  "total": 1,
  "totalPages": 1,
  "scopes": [
    {
      "scope": "calls:read",
      "description": "List calls and the calls in progress, read one call with its routing waterfall and recordings, and export the call log as CSV."
    },
    {
      "scope": "reports:read",
      "description": "Run summary and time-series reports."
    }
  ]
}

Errors

StatusCodeWhen
400validation_errorenv or status is not one of the allowed words.
401unauthorizedNo session token, or one that has expired.
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_foundAn agencyId that is not a workspace you administer answers the same 404.

Create a key

POST/api/tracking/api-keys

Mints a key and returns it in this response only. Only a hash of it is stored, so nothing — not the console, not our staff — can show it again. Recorded in the activity log (api_key, high severity) with the scopes it was given.

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.

Request body

NameTypeDescription
labelRequiredstringYour name for the key. Name the program that will hold it.up to 120 characters
scopesRequiredstring[]What the key may do. Give it the narrowest set that works: a reporting job needs no write scope.at least one, at most 20 · each one a scope from the table above
envOptionalstringWhich environment the key belongs to. It cannot be changed afterwards.live or sandbox · default live
allowCidrsOptionalstring[]The source addresses the key may be used from. An empty list allows any address. An entry that cannot be parsed is refused here rather than discovered when the integration goes dark.at most 50 entries · an IPv4 address, an IPv4 range such as 203.0.113.0/24, or a single IPv6 address · default []
rateLimitPerMinuteOptionalintegerA per-minute limit of your own, lower or higher than the default. A key can be slowed down but never set free: the range starts at 1.1–1200 · default: the platform's own limit

Example request

curl -X POST "https://api.buy3.io/api/tracking/api-keys" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
      "label": "BI warehouse sync",
      "scopes": ["calls:read", "reports:read"],
      "allowCidrs": ["203.0.113.0/24"],
      "rateLimitPerMinute": 60
    }'

Responses

  • 201The key object, the key itself, and a note to store it.
{
  "apiKey": {
    "id": "1a2b3c4d-5e6f-4071-8293-a4b5c6d7e8f9",
    "label": "BI warehouse sync",
    "keyPrefix": "b3_live_Qx7m",
    "scopes": [
      "calls:read",
      "reports:read"
    ],
    "env": "live",
    "state": "active",
    "ownerAgencyId": "8d6f2b1c-0a94-4e73-b5d8-1c2e3f4a5b60",
    "allowCidrs": [
      "203.0.113.0/24"
    ],
    "rateLimitPerMinute": 60,
    "useCount": 0,
    "graceUntil": null,
    "rotatedFromId": null,
    "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
    "lastUsedAt": null,
    "lastUsedIp": null,
    "revokedAt": null,
    "createdAt": "2026-09-20T14:00:02.000Z"
  },
  "key": "b3_live_Qx7mV0c1rP8dLt2fSn6gJw4hEy0bKz9u",
  "note": "Store this key now — it is shown only once and cannot be recovered."
}

Errors

StatusCodeWhen
400validation_errorlabel or scopes is missing, or a field has the wrong type or is too long.
400bad_requestscopes is empty or names something that is not a scope — details.scopes lists the ones it could have been. Or an allowCidrs entry is not an IPv4 address, an IPv4 range or a single IPv6 address — details.allowCidrs names the entry.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active. Reads stay open to a lapsed workspace; writes do not. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking (details.feature is callTracking), or does not include API access (details.feature is apiAccess). It is also the answer when the workspace already holds as many keys as its plan allows — then details.feature is maxApiKeys and details carries limit and current beside it.
403forbiddenYou named no workspace and hold no owner or admin seat on any.
404not_foundAn agencyId that is not a workspace you administer answers the same 404.

Update a key

PATCH/api/tracking/api-keys/:id

Changes the label, the scopes, the address allowlist or the rate limit of an active key. env is fixed for life. Scopes and the allowlist are replaced outright, not merged. Recorded in the activity log (api_key) — at high severity when the scopes or the allowlist change, medium when only the label does, because what a key may do and from where is its security posture and a label is a label.

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 key's id.

Request body

NameTypeDescription
labelOptionalstringA new name.up to 120 characters
scopesOptionalstring[]The new scope list, replacing the old one.at least one, at most 20 · a scope the key already holds from an older vocabulary may stay, so renaming a key does not force a scope change
allowCidrsOptionalstring[]The new allowlist. Send [] to allow any address again.at most 50 entries · an IPv4 address or range, or a single IPv6 address
rateLimitPerMinuteOptionalintegerA new per-minute limit.1–1200

Example request

curl -X PATCH "https://api.buy3.io/api/tracking/api-keys/9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
      "scopes": ["calls:read", "reports:read", "pings:read"]
    }'

Responses

  • 200The key as it now stands.
{
  "apiKey": {
    "id": "9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f",
    "label": "BI warehouse sync",
    "keyPrefix": "b3_live_Qx7m",
    "scopes": [
      "calls:read",
      "reports:read",
      "pings:read"
    ],
    "env": "live",
    "state": "active",
    "ownerAgencyId": "8d6f2b1c-0a94-4e73-b5d8-1c2e3f4a5b60",
    "allowCidrs": [
      "203.0.113.0/24"
    ],
    "rateLimitPerMinute": 60,
    "useCount": 18204,
    "graceUntil": null,
    "rotatedFromId": null,
    "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
    "lastUsedAt": "2026-09-20T14:02:51.000Z",
    "lastUsedIp": "203.0.113.24",
    "revokedAt": null,
    "createdAt": "2026-08-02T11:20:41.000Z"
  }
}

Errors

StatusCodeWhen
400validation_errorA field has the wrong type or is out of range.
400bad_requestThe body is empty ("Nothing to update"). scopes is empty or names something that is not a scope — details.scopes lists the ones it could have been. Or an allowCidrs entry is not an IPv4 address, an IPv4 range or a single IPv6 address — details.allowCidrs names the entry.
409conflictThe key has been revoked or rotated. Edit its successor instead.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active. Reads stay open to a lapsed workspace; writes do not. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking (details.feature is callTracking), or does not include API access (details.feature is apiAccess). It is also the answer when the workspace already holds as many keys as its plan allows — then details.feature is maxApiKeys and details carries limit and current beside it.
403forbiddenYou named no workspace and hold no owner or admin seat on any.
404not_foundNo such key in the workspace. Another workspace's key and an id that is not a UUID answer exactly as a missing one does. An agencyId that is not a workspace you administer answers the same 404.

Rotate a key

POST/api/tracking/api-keys/:id/rotate

Rotation is a successor, not an edit. The old key is revoked now and keeps answering until graceUntil; the new one inherits the label, scopes, environment, allowlist and rate limit, and is returned once. Deploy the new key inside the window, then let the old one lapse — or end it early by revoking it. Recorded in the activity log (api_key, high severity) with the prefix it succeeded.

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 key's id — the one being retired.

Request body

NameTypeDescription
graceMinutesOptionalintegerHow long the old key keeps answering. 0 kills it immediately, which is what you want if it has leaked.0–10080 (seven days) · default 1440 (one day)

Example request

curl -X POST "https://api.buy3.io/api/tracking/api-keys/9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f/rotate" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
      "graceMinutes": 60
    }'

Responses

  • 201The successor, the key itself, the retired key with its state, and when it stops.
{
  "apiKey": {
    "id": "1a2b3c4d-5e6f-4071-8293-a4b5c6d7e8f9",
    "label": "BI warehouse sync",
    "keyPrefix": "b3_live_7Tn4",
    "scopes": [
      "calls:read",
      "reports:read"
    ],
    "env": "live",
    "state": "active",
    "ownerAgencyId": "8d6f2b1c-0a94-4e73-b5d8-1c2e3f4a5b60",
    "allowCidrs": [
      "203.0.113.0/24"
    ],
    "rateLimitPerMinute": 60,
    "useCount": 0,
    "graceUntil": null,
    "rotatedFromId": "9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f",
    "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
    "lastUsedAt": null,
    "lastUsedIp": null,
    "revokedAt": null,
    "createdAt": "2026-09-20T14:00:02.000Z"
  },
  "previous": {
    "id": "9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f",
    "label": "BI warehouse sync",
    "keyPrefix": "b3_live_Qx7m",
    "scopes": [
      "calls:read",
      "reports:read"
    ],
    "env": "live",
    "state": "grace",
    "ownerAgencyId": "8d6f2b1c-0a94-4e73-b5d8-1c2e3f4a5b60",
    "allowCidrs": [
      "203.0.113.0/24"
    ],
    "rateLimitPerMinute": 60,
    "useCount": 18204,
    "graceUntil": "2026-09-21T14:00:02.000Z",
    "rotatedFromId": null,
    "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
    "lastUsedAt": "2026-09-20T14:02:51.000Z",
    "lastUsedIp": "203.0.113.24",
    "revokedAt": "2026-09-20T14:00:02.000Z",
    "createdAt": "2026-08-02T11:20:41.000Z"
  },
  "key": "b3_live_7Tn4pQ2vHd9sMx1cRb6yJz0wLk8eFg3a",
  "graceUntil": "2026-09-21T14:00:02.000Z",
  "note": "Store this key now — it is shown only once. The previous key keeps working until 2026-09-21T14:00:02.000Z, then 401s."
}

Errors

StatusCodeWhen
400validation_errorgraceMinutes is not an integer between 0 and 10080.
409conflictThe key has already been rotated — rotate its successor instead — or it is revoked, or somebody rotated or revoked it a moment before you did.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active. Reads stay open to a lapsed workspace; writes do not. details carries status, planId and agencyId.
402plan_upgrade_requiredThe workspace's plan does not include call tracking (details.feature is callTracking), or does not include API access (details.feature is apiAccess). It is also the answer when the workspace already holds as many keys as its plan allows — then details.feature is maxApiKeys and details carries limit and current beside it.
403forbiddenYou named no workspace and hold no owner or admin seat on any.
404not_foundNo such key in the workspace. Another workspace's key and an id that is not a UUID answer exactly as a missing one does. An agencyId that is not a workspace you administer answers the same 404.

Revoke a key

POST/api/tracking/api-keys/:id/revoke

Kills the key at once and clears any grace window it was in. Takes no body. This is the emergency route, so it is never gated on the plan: a workspace that has changed plan, or whose subscription has lapsed, can still stop a key that has leaked. Recorded in the activity log (api_key, high severity).

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 key's id.

Example request

curl -X POST "https://api.buy3.io/api/tracking/api-keys/9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f/revoke" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200The key, now revoked.
{
  "apiKey": {
    "id": "9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f",
    "label": "BI warehouse sync",
    "keyPrefix": "b3_live_Qx7m",
    "scopes": [
      "calls:read",
      "reports:read"
    ],
    "env": "live",
    "state": "revoked",
    "ownerAgencyId": "8d6f2b1c-0a94-4e73-b5d8-1c2e3f4a5b60",
    "allowCidrs": [
      "203.0.113.0/24"
    ],
    "rateLimitPerMinute": 60,
    "useCount": 18204,
    "graceUntil": null,
    "rotatedFromId": null,
    "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
    "lastUsedAt": "2026-09-20T14:02:51.000Z",
    "lastUsedIp": "203.0.113.24",
    "revokedAt": "2026-09-20T14:12:30.000Z",
    "createdAt": "2026-08-02T11:20:41.000Z"
  }
}

Errors

StatusCodeWhen
409conflictThe key is already revoked.
401unauthorizedNo session token, or one that has expired.
402subscription_inactiveThe workspace's subscription is not active. Reads stay open to a lapsed workspace; writes do not. 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 such key in the workspace. Another workspace's key and an id that is not a UUID answer exactly as a missing one does. An agencyId that is not a workspace you administer answers the same 404.

Read one key's requests

GET/api/tracking/api-keys/:id/requests

Every request this key made, newest first, refusals included, with the key itself beside the page so a screen has its label without a second call. Changes nothing.

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 key's id.

Query parameters

NameTypeDescription
statusOptionalstringA status class, not one status: what somebody debugging asks is "show me the failures".2xx, 3xx, 4xx or 5xx
methodOptionalstringOnly requests made with this method.GET, POST, PUT, PATCH or DELETE
errorCodeOptionalstringOnly requests refused with this code, such as rate_limited or insufficient_scope.up to 80 characters
qOptionalstringMatches part of the path, ignoring case.up to 200 characters
fromOptionaldateOnly requests made at or after this moment.
toOptionaldateOnly requests made at or before this moment.
pageOptionalintegerThe page to return.from 1, default 1
pageSizeOptionalintegerRows per page. A larger value is clamped, not refused.1–200 · default 50

Example request

curl "https://api.buy3.io/api/tracking/api-keys/9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f/requests?status=4xx&from=2026-09-19T00:00:00Z" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200A page of requests. query is the parsed query string; status and errorCode are what the caller was answered with; requestId is the id in the reply's X-Request-Id header and in any error body.
{
  "data": [
    {
      "id": "48211904",
      "apiKeyId": "9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f",
      "keyPrefix": "b3_live_Qx7m",
      "keyLabel": "BI warehouse sync",
      "method": "POST",
      "path": "/api/v1/calls/0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11/conversion",
      "query": null,
      "status": 403,
      "errorCode": "insufficient_scope",
      "latencyMs": 6,
      "ip": "203.0.113.24",
      "userAgent": "warehouse-sync/1.4",
      "requestId": "5d1c0d2e-8a4f-4c0b-9e1d-2f3a4b5c6d7e",
      "at": "2026-09-20T14:03:02.000Z"
    },
    {
      "id": "48211903",
      "apiKeyId": "9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f",
      "keyPrefix": "b3_live_Qx7m",
      "keyLabel": "BI warehouse sync",
      "method": "GET",
      "path": "/api/v1/calls",
      "query": {
        "outcome": "converted",
        "pageSize": "100"
      },
      "status": 200,
      "errorCode": null,
      "latencyMs": 84,
      "ip": "203.0.113.24",
      "userAgent": "warehouse-sync/1.4",
      "requestId": "5d1c0d2e-8a4f-4c0b-9e1d-2f3a4b5c6d7e",
      "at": "2026-09-20T14:02:51.000Z"
    }
  ],
  "page": 1,
  "pageSize": 50,
  "total": 2,
  "totalPages": 1,
  "apiKey": {
    "id": "9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f",
    "label": "BI warehouse sync",
    "keyPrefix": "b3_live_Qx7m",
    "scopes": [
      "calls:read",
      "reports:read"
    ],
    "env": "live",
    "state": "active",
    "ownerAgencyId": "8d6f2b1c-0a94-4e73-b5d8-1c2e3f4a5b60",
    "allowCidrs": [
      "203.0.113.0/24"
    ],
    "rateLimitPerMinute": 60,
    "useCount": 18204,
    "graceUntil": null,
    "rotatedFromId": null,
    "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
    "lastUsedAt": "2026-09-20T14:02:51.000Z",
    "lastUsedIp": "203.0.113.24",
    "revokedAt": null,
    "createdAt": "2026-08-02T11:20:41.000Z"
  }
}

Errors

StatusCodeWhen
400validation_errorstatus is not a status class, method is not a method, or from or to is not a date.
401unauthorizedNo session token, or one that has expired.
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 such key in the workspace. Another workspace's key and an id that is not a UUID answer exactly as a missing one does. An agencyId that is not a workspace you administer answers the same 404.

Read every key's requests

GET/api/tracking/api-requests

The same log across every key in the workspace, each row naming the key that made it. This is the page you open on "what is failing anywhere", and you ask which key only once you know something is. Changes nothing.

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
apiKeyIdOptionaluuidNarrow to one key. A key id from another workspace simply matches nothing.
statusOptionalstringA status class, not one status: what somebody debugging asks is "show me the failures".2xx, 3xx, 4xx or 5xx
methodOptionalstringOnly requests made with this method.GET, POST, PUT, PATCH or DELETE
errorCodeOptionalstringOnly requests refused with this code, such as rate_limited or insufficient_scope.up to 80 characters
qOptionalstringMatches part of the path, ignoring case.up to 200 characters
fromOptionaldateOnly requests made at or after this moment.
toOptionaldateOnly requests made at or before this moment.
pageOptionalintegerThe page to return.from 1, default 1
pageSizeOptionalintegerRows per page. A larger value is clamped, not refused.1–200 · default 50

Example request

curl "https://api.buy3.io/api/tracking/api-requests?status=5xx&pageSize=100" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200A page of requests across the workspace's keys.
{
  "data": [
    {
      "id": "48211904",
      "apiKeyId": "9b2f4c1d-3e5a-4f6b-8c7d-0a1b2c3d4e5f",
      "keyPrefix": "b3_live_Qx7m",
      "keyLabel": "BI warehouse sync",
      "method": "POST",
      "path": "/api/v1/calls/0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11/conversion",
      "query": null,
      "status": 403,
      "errorCode": "insufficient_scope",
      "latencyMs": 6,
      "ip": "203.0.113.24",
      "userAgent": "warehouse-sync/1.4",
      "requestId": "5d1c0d2e-8a4f-4c0b-9e1d-2f3a4b5c6d7e",
      "at": "2026-09-20T14:03:02.000Z"
    },
    {
      "id": "48211902",
      "apiKeyId": "3c4d5e6f-7081-4293-a4b5-c6d7e8f90a1b",
      "keyPrefix": "b3_live_2Vb9",
      "keyLabel": "CRM postback",
      "method": "POST",
      "path": "/api/v1/calls/0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11/tags",
      "query": null,
      "status": 200,
      "errorCode": null,
      "latencyMs": 41,
      "ip": "203.0.113.24",
      "userAgent": "warehouse-sync/1.4",
      "requestId": "5d1c0d2e-8a4f-4c0b-9e1d-2f3a4b5c6d7e",
      "at": "2026-09-20T14:02:51.000Z"
    }
  ],
  "page": 1,
  "pageSize": 50,
  "total": 2,
  "totalPages": 1
}

Errors

StatusCodeWhen
400validation_errorapiKeyId is not a UUID, status is not a status class, method is not a method, or from or to is not a date.
401unauthorizedNo session token, or one that has expired.
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_foundAn agencyId that is not a workspace you administer answers the same 404.