API reference

Publishers

Read the publishers who send you calls and the lanes that join them to your campaigns; set payout terms, caps and keys through the console.

A publisher is a partner who sends you calls. A publisher joins one of your campaigns through a lane — the pairing of the two — and the lane is where the commercial relationship lives: the payout terms, the caps, the states and hours the publisher may send, and the key they authenticate with. One publisher can have a lane on several campaigns, on different terms. The concepts are in Publishers overview.

Two surfaces

EndpointSurfaceCredential
GET /api/v1/publishersREST APIAPI key with publishers:read
GET /api/v1/publishers/:idREST APIAPI key with publishers:read
GET /api/tracking/campaigns/:id/publishersConsoleSession token
GET /api/tracking/campaigns/:id/publishers/:publisherIdConsoleSession token
PUT /api/tracking/campaigns/:id/publishers/:publisherIdConsoleSession token
DELETE /api/tracking/campaigns/:id/publishers/:publisherIdConsoleSession token
POST /api/tracking/campaigns/:id/publishers/:publisherId/compatConsoleSession token
PATCH /api/publishers/keys/:keyIdConsoleSession token
POST /api/publishers/keys/:keyId/compat-token/rotateConsoleSession token
POST /api/publishers/keys/:keyId/signing-secret/rotateConsoleSession token
The REST API reads publishers under the publisher; the console reads and writes lanes under the campaign. Both are the same rows.

The publisher object

FieldTypeMeaning
iduuidThe publisher's id.
namestringWhat you call them.
statusstringonboarding, live, paused, review or terminated. Only a live publisher's pings are accepted.
statusNotestring or nullThe note behind the current status.
routingPrefixstringThe short code that identifies this publisher in routing keys and references.
sourcestring or nullHow they came to you, for your own reporting.
contactName, contactEmail, contactPhone, contactImstring or nullWho to reach, and how.
timezonestringThe publisher's own zone, shown beside their figures.
qualityinteger or nullA rolling quality score from 0 to 100, or null when there is not enough traffic to judge.
portalEnabledbooleanWhether this publisher can sign in to the publisher portal.
payoutSchedulestringweekly, net_15 or net_30.
payoutMethodstringach, wire or paypal.
joinedAt, createdAt, updatedAttimestamp or nullWhen they joined, and when the record was created and last changed.
lanesarrayEvery lane this publisher has onto your tracking campaigns. See The lane.
The REST API returns an allowlist, never the whole record: internal notes, the account manager and the payout destination are not in it.

The lane

A lane is one publisher's standing on one campaign. It is created when you attach the publisher and it holds everything that is specific to that pairing.

FieldTypeMeaning
campaignId, campaignuuid, objectWhich campaign. The REST API names it as { id, name, status }; the console returns publisherId and a publisher block instead, because it reads lanes under the campaign.
activebooleanfalse stops the lane taking calls without closing it.
payoutModestringinherit, fixed or revshare. See Payout terms.
payoutCentsinteger or nullThe fixed amount per converted call, when payoutMode is fixed.
revsharePctnumber or nullThe share of revenue, from 0 to 100, when payoutMode is revshare.
minPayoutCents, maxPayoutCentsinteger or nullA floor and a ceiling on a revenue-share payout.
bufferSeconds, effectiveBufferSecondsinteger or null, integer or nullThe lane's own settlement buffer, and the one actually used — the lane's, or the campaign's when the lane has none.
dedupeWindowSecondsintegerHow long a caller counts as a duplicate from this publisher.
hourlyCap, dailyCap, monthlyCap, concurrencyCapintegerHow much this publisher may send. 0 is unlimited.
dailyPayoutCapCentsintegerThe most this lane may earn the publisher in a day. 0 is unlimited.
blockWhenCappedbooleantrue refuses a ping once a cap is full; false lets it through unpaid.
states, hoursSchedule, timezonearray or null, object or null, string or nullWhere and when this publisher may send.
notesPublic, notesInternalstring or nullnotesPublic is shown to the publisher in their portal. notesInternal is yours.
usageobject{ liveCalls, hour, day, month, dayPayoutCents } — the counters behind the caps.
keyobject or nullThe lane's routing key: { id, prefix, state, protocol, environment, requireSignature, lastUsedAt, useCount, issuedAt }. The key itself is never here — only its prefix.
numbersintegerTracking numbers attributed to this publisher on this campaign.
compatobjectWhether the Ringba-compatible single-step ping is switched on. The console also returns its url; the REST API returns { enabled } only.
assignedAt, termsFrozenAttimestamp or nullWhen the lane was created, and when its terms were last frozen.

Payout terms

A lane's payout is fixed — an amount in cents for each converted call — or a revenue share, a percentage of what the buyer paid, with an optional floor and ceiling. inherit takes the campaign's own terms. Whatever the mode, the terms in force when a call starts are frozen onto that call: changing a lane never re-prices a call that has already arrived, and termsFrozenAt records when the terms last moved. The guide is Payout terms.

Endpoints

List publishers

GET/api/v1/publishers

Every publisher in your workspace, filtered and paged, each with its lanes onto your tracking campaigns. This is the read a reporting integration needs — who feeds this workspace, and on what terms per campaign. Terminated publishers are history: they are left out unless you ask for them by name.

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

Query parameters

NameTypeDescription
qOptionalstringMatches name, routingPrefix or contactName, case-insensitively, anywhere in the value.up to 120 characters
statusOptionalenumOne status.onboarding, live, paused, review, terminated · default: every status except terminated
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/publishers?status=live&pageSize=25" \
  -H "Authorization: Bearer $BUY3_API_KEY"

Responses

  • 200A page of publisher objects, each with its lanes. A publisher with no lane onto one of your tracking campaigns has an empty array.
{
  "data": [
    {
      "id": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
      "name": "Northwind Media",
      "status": "live",
      "statusNote": null,
      "routingPrefix": "NW",
      "source": "referral",
      "contactName": "Sam Okafor",
      "contactEmail": "sam@example.com",
      "contactPhone": "+1 555 0177",
      "contactIm": null,
      "timezone": "America/New_York",
      "quality": 82,
      "portalEnabled": true,
      "payoutSchedule": "net_15",
      "payoutMethod": "ach",
      "joinedAt": "2026-07-05T13:11:02.000Z",
      "createdAt": "2026-07-04T17:45:00.000Z",
      "updatedAt": "2026-09-19T08:12:00.000Z",
      "lanes": [
        {
          "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
          "active": true,
          "payoutMode": "fixed",
          "payoutCents": 2750,
          "revsharePct": null,
          "minPayoutCents": null,
          "maxPayoutCents": null,
          "bufferSeconds": null,
          "effectiveBufferSeconds": 0,
          "dedupeWindowSeconds": 86400,
          "hourlyCap": 0,
          "dailyCap": 400,
          "monthlyCap": 0,
          "concurrencyCap": 25,
          "dailyPayoutCapCents": 0,
          "blockWhenCapped": true,
          "states": [
            "TX",
            "OK",
            "NM"
          ],
          "hoursSchedule": null,
          "timezone": "America/Chicago",
          "notesPublic": "Send health cover calls only. No aged leads.",
          "notesInternal": "Renegotiate the payout in October.",
          "usage": {
            "liveCalls": 1,
            "hour": 6,
            "day": 142,
            "month": 2988,
            "dayPayoutCents": 390500
          },
          "key": {
            "id": "1f2e3d4c-5b6a-4798-8c9d-0e1f2a3b4c5e",
            "prefix": "b3k_live_7Qx4",
            "state": "live",
            "protocol": "rtb_v1",
            "environment": "production",
            "requireSignature": false,
            "lastUsedAt": "2026-09-20T18:19:55.000Z",
            "useCount": 18422,
            "issuedAt": "2026-07-05T13:11:02.000Z"
          },
          "numbers": 2,
          "campaign": {
            "id": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
            "name": "Health cover — inbound",
            "status": "live"
          },
          "compat": {
            "enabled": false
          },
          "assignedAt": "2026-07-05T13:11:02.000Z",
          "termsFrozenAt": "2026-09-01T00:00:00.000Z"
        }
      ]
    }
  ],
  "page": 1,
  "pageSize": 25,
  "total": 2,
  "totalPages": 1
}

Errors

StatusCodeWhen
400validation_errorA filter has the wrong type, or status is not one of the five. 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 publishers: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 publisher

GET/api/v1/publishers/:id

One publisher with every lane it holds onto your tracking campaigns, oldest lane first. Each lane carries its terms, its caps and its counters, so one request answers "what has this publisher agreed to, and where are they against it today".

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

Path parameters

NameTypeDescription
idRequireduuidThe publisher's id, as returned by List publishers.

Example request

curl "https://api.buy3.io/api/v1/publishers/c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b" \
  -H "Authorization: Bearer $BUY3_API_KEY"

Responses

  • 200The publisher and its lanes.
{
  "publisher": {
    "id": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
    "name": "Northwind Media",
    "status": "live",
    "statusNote": null,
    "routingPrefix": "NW",
    "source": "referral",
    "contactName": "Sam Okafor",
    "contactEmail": "sam@example.com",
    "contactPhone": "+1 555 0177",
    "contactIm": null,
    "timezone": "America/New_York",
    "quality": 82,
    "portalEnabled": true,
    "payoutSchedule": "net_15",
    "payoutMethod": "ach",
    "joinedAt": "2026-07-05T13:11:02.000Z",
    "createdAt": "2026-07-04T17:45:00.000Z",
    "updatedAt": "2026-09-19T08:12:00.000Z",
    "lanes": [
      {
        "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
        "active": true,
        "payoutMode": "fixed",
        "payoutCents": 2750,
        "revsharePct": null,
        "minPayoutCents": null,
        "maxPayoutCents": null,
        "bufferSeconds": null,
        "effectiveBufferSeconds": 0,
        "dedupeWindowSeconds": 86400,
        "hourlyCap": 0,
        "dailyCap": 400,
        "monthlyCap": 0,
        "concurrencyCap": 25,
        "dailyPayoutCapCents": 0,
        "blockWhenCapped": true,
        "states": [
          "TX",
          "OK",
          "NM"
        ],
        "hoursSchedule": null,
        "timezone": "America/Chicago",
        "notesPublic": "Send health cover calls only. No aged leads.",
        "notesInternal": "Renegotiate the payout in October.",
        "usage": {
          "liveCalls": 1,
          "hour": 6,
          "day": 142,
          "month": 2988,
          "dayPayoutCents": 390500
        },
        "key": {
          "id": "1f2e3d4c-5b6a-4798-8c9d-0e1f2a3b4c5e",
          "prefix": "b3k_live_7Qx4",
          "state": "live",
          "protocol": "rtb_v1",
          "environment": "production",
          "requireSignature": false,
          "lastUsedAt": "2026-09-20T18:19:55.000Z",
          "useCount": 18422,
          "issuedAt": "2026-07-05T13:11:02.000Z"
        },
        "numbers": 2,
        "campaign": {
          "id": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
          "name": "Health cover — inbound",
          "status": "live"
        },
        "compat": {
          "enabled": false
        },
        "assignedAt": "2026-07-05T13:11:02.000Z",
        "termsFrozenAt": "2026-09-01T00:00:00.000Z"
      }
    ]
  }
}

Errors

StatusCodeWhen
404not_foundNo publisher with that id in your workspace. A malformed id, an id that does not exist and another workspace's publisher 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 publishers: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 · List a campaign's lanes

GET/api/tracking/campaigns/:id/publishers

Console API. These routes take a signed-in owner's or admin's session token, not an API key, and they may change with the console. Every lane onto this campaign, oldest assignment first, each with its terms, caps, counters, key prefix and the state of its single-step ping URL. Only publishers your workspace owns are listed.

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

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/campaigns/6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d/publishers" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200The campaign's lanes.
{
  "lanes": [
    {
      "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
      "publisherId": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
      "publisher": {
        "id": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
        "name": "Northwind Media",
        "status": "live",
        "routingPrefix": "NW"
      },
      "active": true,
      "payoutMode": "fixed",
      "payoutCents": 2750,
      "revsharePct": null,
      "minPayoutCents": null,
      "maxPayoutCents": null,
      "bufferSeconds": null,
      "effectiveBufferSeconds": 0,
      "dedupeWindowSeconds": 86400,
      "hourlyCap": 0,
      "dailyCap": 400,
      "monthlyCap": 0,
      "concurrencyCap": 25,
      "dailyPayoutCapCents": 0,
      "blockWhenCapped": true,
      "states": [
        "TX",
        "OK",
        "NM"
      ],
      "hoursSchedule": null,
      "timezone": "America/Chicago",
      "notesPublic": "Send health cover calls only. No aged leads.",
      "notesInternal": "Renegotiate the payout in October.",
      "usage": {
        "liveCalls": 1,
        "hour": 6,
        "day": 142,
        "month": 2988,
        "dayPayoutCents": 390500
      },
      "key": {
        "id": "1f2e3d4c-5b6a-4798-8c9d-0e1f2a3b4c5e",
        "prefix": "b3k_live_7Qx4",
        "state": "live",
        "protocol": "rtb_v1",
        "environment": "production",
        "requireSignature": false,
        "lastUsedAt": "2026-09-20T18:19:55.000Z",
        "useCount": 18422,
        "issuedAt": "2026-07-05T13:11:02.000Z"
      },
      "numbers": 2,
      "compat": {
        "enabled": false,
        "url": null
      },
      "assignedAt": "2026-07-05T13:11:02.000Z",
      "termsFrozenAt": "2026-09-01T00:00:00.000Z"
    }
  ]
}

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.
403forbiddenYou named no workspace and hold no owner or admin seat on any.
404not_foundNo tracking campaign with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Read one lane

GET/api/tracking/campaigns/:id/publishers/:publisherId

Console API. One publisher's standing on one campaign, with the single-step ping URL in full when it is switched on — this is the page that URL is copied from.

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 campaign's id.
publisherIdRequireduuidThe publisher's id.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in.

Example request

curl "https://api.buy3.io/api/tracking/campaigns/6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d/publishers/c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200The lane.
{
  "lane": {
    "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
    "publisherId": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
    "publisher": {
      "id": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
      "name": "Northwind Media",
      "status": "live",
      "routingPrefix": "NW"
    },
    "active": true,
    "payoutMode": "fixed",
    "payoutCents": 2750,
    "revsharePct": null,
    "minPayoutCents": null,
    "maxPayoutCents": null,
    "bufferSeconds": null,
    "effectiveBufferSeconds": 0,
    "dedupeWindowSeconds": 86400,
    "hourlyCap": 0,
    "dailyCap": 400,
    "monthlyCap": 0,
    "concurrencyCap": 25,
    "dailyPayoutCapCents": 0,
    "blockWhenCapped": true,
    "states": [
      "TX",
      "OK",
      "NM"
    ],
    "hoursSchedule": null,
    "timezone": "America/Chicago",
    "notesPublic": "Send health cover calls only. No aged leads.",
    "notesInternal": "Renegotiate the payout in October.",
    "usage": {
      "liveCalls": 1,
      "hour": 6,
      "day": 142,
      "month": 2988,
      "dayPayoutCents": 390500
    },
    "key": {
      "id": "1f2e3d4c-5b6a-4798-8c9d-0e1f2a3b4c5e",
      "prefix": "b3k_live_7Qx4",
      "state": "live",
      "protocol": "rtb_v1",
      "environment": "production",
      "requireSignature": false,
      "lastUsedAt": "2026-09-20T18:19:55.000Z",
      "useCount": 18422,
      "issuedAt": "2026-07-05T13:11:02.000Z"
    },
    "numbers": 2,
    "compat": {
      "enabled": false,
      "url": null
    },
    "assignedAt": "2026-07-05T13:11:02.000Z",
    "termsFrozenAt": "2026-09-01T00:00:00.000Z"
  }
}

Errors

StatusCodeWhen
404not_foundPublisher not found when the publisher is not yours; Publisher lane not found when they have no lane on this campaign.
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 tracking campaign with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Attach a publisher, or change their terms

PUT/api/tracking/campaigns/:id/publishers/:publisherId

Console API. One request creates a lane or updates the one that is there. Creating a lane issues its routing key in the same transaction, and that key is returned in full exactly once — in this response, as key. It is never readable again; afterwards only its prefix comes back. Updating an existing lane never touches the key. A lane's terms take effect on the next call: calls already in progress keep the terms they started under.

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 campaign's id.
publisherIdRequireduuidThe publisher's id.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in.

Request body

NameTypeDescription
activeOptionalbooleanfalse stops the lane taking calls without closing it.
payoutModeOptionalenumHow the publisher is paid.inherit, fixed, revshare
payoutCentsOptionalinteger or nullThe amount per converted call. Required when payoutMode is fixed.0 to 10000000
revsharePctOptionalnumber or nullThe publisher's share of revenue. Required when payoutMode is revshare.0 to 100
minPayoutCentsOptionalinteger or nullA floor under a revenue-share payout. It must not exceed maxPayoutCents.0 to 10000000
maxPayoutCentsOptionalinteger or nullA ceiling over a revenue-share payout.0 to 10000000
bufferSecondsOptionalinteger or nullThe lane's own settlement buffer. null uses the campaign's.0 to 3600
dedupeWindowSecondsOptionalintegerHow long a caller from this publisher counts as a duplicate.0 to 7776000 · 0 switches it off
hourlyCapOptionalintegerCalls this publisher may send 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
concurrencyCapOptionalintegerHow many of this publisher's calls may be live at once.0 to 10000 · 0 is unlimited
dailyPayoutCapCentsOptionalintegerThe most the lane may earn the publisher in a UTC day.0 to 1000000000 · 0 is unlimited
blockWhenCappedOptionalbooleantrue refuses the publisher's pings once a cap is full, so they can send the call elsewhere. false accepts them unpaid.
statesOptionalarray of strings or nullTwo-letter state codes this publisher may send from.at most 62 codes
hoursScheduleOptionalobject or nullWhen this publisher may send, as { tz, days }. null is always open.
timezoneOptionalstring or nullAn IANA zone for the lane's hours.up to 64 characters
notesPublicOptionalstring or nullA note the publisher can read in their portal.up to 4000 characters
notesInternalOptionalstring or nullA note only your team can read.up to 4000 characters

Example request

curl -X PUT "https://api.buy3.io/api/tracking/campaigns/6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d/publishers/c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "payoutMode": "fixed",
    "payoutCents": 2750,
    "dailyCap": 400,
    "concurrencyCap": 25,
    "blockWhenCapped": true,
    "states": [
      "TX",
      "OK",
      "NM"
    ],
    "notesPublic": "Send health cover calls only. No aged leads."
  }'

Responses

  • 201The lane was created. key is the publisher's routing key in full — store it now, it cannot be read again — and keyNote says the same thing in words.
  • 200The lane already existed and its terms were updated. No key is returned, and the existing key is untouched.
{
  "lane": {
    "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
    "publisherId": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
    "publisher": {
      "id": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
      "name": "Northwind Media",
      "status": "live",
      "routingPrefix": "NW"
    },
    "active": true,
    "payoutMode": "fixed",
    "payoutCents": 2750,
    "revsharePct": null,
    "minPayoutCents": null,
    "maxPayoutCents": null,
    "bufferSeconds": null,
    "effectiveBufferSeconds": 0,
    "dedupeWindowSeconds": 86400,
    "hourlyCap": 0,
    "dailyCap": 400,
    "monthlyCap": 0,
    "concurrencyCap": 25,
    "dailyPayoutCapCents": 0,
    "blockWhenCapped": true,
    "states": [
      "TX",
      "OK",
      "NM"
    ],
    "hoursSchedule": null,
    "timezone": "America/Chicago",
    "notesPublic": "Send health cover calls only. No aged leads.",
    "notesInternal": "Renegotiate the payout in October.",
    "usage": {
      "liveCalls": 1,
      "hour": 6,
      "day": 142,
      "month": 2988,
      "dayPayoutCents": 390500
    },
    "key": {
      "id": "1f2e3d4c-5b6a-4798-8c9d-0e1f2a3b4c5e",
      "prefix": "b3k_live_7Qx4",
      "state": "live",
      "protocol": "rtb_v1",
      "environment": "production",
      "requireSignature": false,
      "lastUsedAt": "2026-09-20T18:19:55.000Z",
      "useCount": 18422,
      "issuedAt": "2026-07-05T13:11:02.000Z"
    },
    "numbers": 2,
    "compat": {
      "enabled": false,
      "url": null
    },
    "assignedAt": "2026-07-05T13:11:02.000Z",
    "termsFrozenAt": "2026-09-01T00:00:00.000Z"
  },
  "created": true,
  "key": "b3k_live_7Qx4mV0c1SxamPLeKEy",
  "keyNote": "This is the only time this key is shown. Send it to the publisher and store it somewhere safe."
}

Errors

StatusCodeWhen
400validation_errorA field has the wrong type or is out of range, or a required companion field is missing — payoutCents for a fixed lane, revsharePct for a revshare one, or a minimum above the maximum.
404not_foundPublisher not found — the publisher is not one of yours.
409conflictThe campaign is archived, or the publisher is terminated and cannot be given a new lane.
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 tracking campaign with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Detach a publisher

DELETE/api/tracking/campaigns/:id/publishers/:publisherId

Console API. Closes the lane and revokes every active key for the pair, with no grace window — the publisher's next ping is refused — and switches off the single-step ping URL. The publisher's tracking numbers on the campaign are not touched: they keep ringing, and the response counts them so you can decide what to do about it. Calls already settled keep their payouts.

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 campaign's id.
publisherIdRequireduuidThe publisher's id.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in.

Example request

curl -X DELETE "https://api.buy3.io/api/tracking/campaigns/6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d/publishers/c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200What was removed, how many keys were revoked, and how many of the publisher's numbers still point at the campaign.
{
  "removed": true,
  "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
  "publisherId": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
  "revokedKeys": 1,
  "numbersStillAssigned": 2,
  "note": "Two tracking numbers are still attributed to this publisher on this campaign. Reassign or release them."
}

Errors

StatusCodeWhen
404not_foundPublisher lane not found — this publisher has no lane on this campaign.
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 tracking campaign with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Switch the single-step ping URL on or off

POST/api/tracking/campaigns/:id/publishers/:publisherId/compat

Console API. Turns the Ringba-compatible single-step ping on or off for this lane. Switching it on mints a token if there is none, and widens a legacy key's protocol so both doors work. Switching it off forgets the token: turning it on again gives a new URL, and the old one never works again. The token is never written to the activity log.

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 campaign's id.
publisherIdRequireduuidThe publisher's id.

Query parameters

NameTypeDescription
agencyIdOptionaluuidThe workspace to act in.

Request body

NameTypeDescription
enabledRequiredbooleantrue opens the door and returns the URL; false closes it and forgets the token.

Example request

curl -X POST "https://api.buy3.io/api/tracking/campaigns/6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d/publishers/c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b/compat" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true
  }'

Responses

  • 200Whether the door is open, the URL in full when it is, and the protocol the lane's key now speaks.
{
  "enabled": true,
  "url": "https://api.buy3.io/api/rtb/compat/ringba/7f3a9c1e5b2d48a06e1f7c3b9d5a2e84c6b1f0d37a9e2c58.json",
  "protocol": "rtb_v1"
}

Errors

StatusCodeWhen
404not_foundPublisher lane not found — this publisher has no lane on this campaign.
409conflictThe lane has no live routing key to hang the URL on.
409compat_needs_unsigned_keyYou asked to switch the URL on for a key that requires signed requests, which a Ringba-style platform cannot send. details carries the keyPrefix. Turn signature enforcement off on the key first — see Change a key's settings.
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 tracking campaign with that id in this workspace, or you named a workspace with ?agencyId= that is not yours.

Console API · Change a publisher key's settings

PATCH/api/publishers/keys/:keyId

Console API. Everything about a live key that is configuration rather than credential: its label, which protocol it speaks, its IP allowlist, its own rate limits and whether it must sign its requests. The key itself is never returned — only its prefix and the flags. Every write here is filed under both your workspace and the publisher, so it appears in your activity log and in the publisher's portal.

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
keyIdRequireduuidThe key's id, as lane.key.id. A missing, malformed or foreign key is the same 404 Key not found.

Request body

NameTypeDescription
labelOptionalstring or nullA name for the key, so a publisher with several can tell them apart.up to 120 characters
protocolOptionalenumWhich ping protocol this key may use.legacy, rtb_v1, both
environmentOptionalenumWhat the key is for.production, test, sandbox
reserveLaneOptionalbooleanWhether a winning bid on this key holds a concurrency slot until it is claimed.
requireSignatureOptionalbooleanWhether every request on this key must be signed. A signing secret has to exist first. See Keys and security.
allowCidrsOptionalarray of strings or nullAddresses this key may be used from. An empty array or null means no allowlist.at most 64 entries · an IPv4 address, an IPv4 CIDR block or an IPv6 address
rateLimitPerMinuteOptionalintegerThe key's own per-minute limit.1 to 100000
rateLimitPerHourOptionalintegerThe key's own per-hour limit.1 to 5000000
rateLimitPerDayOptionalintegerThe key's own per-day limit.1 to 100000000
maxLiveBidsPerCallerOptionalintegerHow many live bids this key may hold for one caller at a time.1 to 100
allowSelfRotateOptionalbooleanWhether the publisher may rotate this key themselves from their portal.
compatEnabledOptionalbooleanThe same switch as the lane's compat route: true mints the single-step token when there is none, false switches the door off and forgets the token.

Example request

curl -X PATCH "https://api.buy3.io/api/publishers/keys/1f2e3d4c-5b6a-4798-8c9d-0e1f2a3b4c5e" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Northwind — production",
    "allowCidrs": [
      "203.0.113.0/24"
    ],
    "rateLimitPerMinute": 600
  }'

Responses

  • 200The key's settings as they now stand. compatUrl is the single-step URL when that door is open. The bearer key, the signing secret and the bare compat token are never served.
{
  "key": {
    "id": "1f2e3d4c-5b6a-4798-8c9d-0e1f2a3b4c5e",
    "publisherId": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
    "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
    "keyPrefix": "b3k_live_7Qx4",
    "label": "Northwind — production",
    "protocol": "rtb_v1",
    "environment": "production",
    "reserveLane": true,
    "active": true,
    "requireSignature": false,
    "allowSelfRotate": false,
    "allowCidrs": [
      "203.0.113.0/24"
    ],
    "rateLimitPerMinute": 600,
    "rateLimitPerHour": 20000,
    "rateLimitPerDay": 300000,
    "maxLiveBidsPerCaller": 3,
    "useCount": 18422,
    "lastUsedAt": "2026-09-20T18:19:55.000Z",
    "issuedAt": "2026-07-05T13:11:02.000Z",
    "graceUntil": null,
    "revokedAt": null,
    "rotatedFromId": null,
    "createdAt": "2026-07-05T13:11:02.000Z",
    "signingSecretLast4": null,
    "hasSigningSecret": false,
    "compatEnabled": false,
    "compatUrl": null
  }
}

Errors

StatusCodeWhen
400bad_requestNo fields to update, an entry in allowCidrs that is not an address or block — details.allowCidrs names them — or requireSignature: true on a key that has no signing secret yet.
404not_foundKey not found — the id is missing, malformed, or belongs to a publisher you do not own.
409conflictThe key has been revoked.
409compat_needs_unsigned_keycompatEnabled: true on a key that requires signed requests. details carries the keyPrefix.
409compat_enabledrequireSignature: true on a key whose single-step URL is switched on. The two cannot both be on. details carries the keyPrefix.
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 key with that id belongs to a publisher you own, or you named a workspace with ?agencyId= that is not yours.

Console API · Rotate the single-step ping URL

POST/api/publishers/keys/:keyId/compat-token/rotate

Console API. Mints a new single-step ping URL without touching the bearer key. There is no grace window: the previous URL stops working the moment this returns, so send the new one to the publisher before they next ping, or they will be refused. It takes no body. The token is never written to the activity log.

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

Example request

curl -X POST "https://api.buy3.io/api/publishers/keys/1f2e3d4c-5b6a-4798-8c9d-0e1f2a3b4c5e/compat-token/rotate" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

Responses

  • 201The key, with compatUrl set to the new URL, and a note repeating that the old one is dead.
{
  "key": {
    "id": "1f2e3d4c-5b6a-4798-8c9d-0e1f2a3b4c5e",
    "publisherId": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
    "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
    "keyPrefix": "b3k_live_7Qx4",
    "protocol": "both",
    "compatEnabled": true,
    "compatUrl": "https://api.buy3.io/api/rtb/compat/ringba/2b8e4f1a7c3d59e06b1f8a4c2d7e5039f6a1b8c4d2e7f0a3.json",
    "…": "…"
  },
  "note": "The previous ping URL stopped working immediately. Send the new one to the publisher."
}

Errors

StatusCodeWhen
404not_foundKey not found.
409conflictThe key has been revoked — its replacement holds the ping URL now — or the single-step door is switched off. Switch it on 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 key with that id belongs to a publisher you own, or you named a workspace with ?agencyId= that is not yours.

Console API · Rotate a key's signing secret

POST/api/publishers/keys/:keyId/signing-secret/rotate

Console API. Mints a new signing secret for the key and returns it once. There is no grace window — the secret is one value, overwritten in place — so the publisher's next signed request has to use the new one. By default this also switches signature enforcement on. To rotate without an outage: switch requireSignature off, rotate with enforce: false, let the publisher deploy the secret, then switch requireSignature back on.

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

Request body

NameTypeDescription
enforceOptionalbooleanWhether to switch requireSignature on at the same time. false mints the secret and leaves enforcement as it is.default true

Example request

curl -X POST "https://api.buy3.io/api/publishers/keys/1f2e3d4c-5b6a-4798-8c9d-0e1f2a3b4c5e/signing-secret/rotate" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "enforce": false
  }'

Responses

  • 200The key, and signingSecret in full — the only time it is shown. previousSecretValid is always false: there is no overlap.
{
  "key": {
    "id": "1f2e3d4c-5b6a-4798-8c9d-0e1f2a3b4c5e",
    "keyPrefix": "b3k_live_7Qx4",
    "requireSignature": true,
    "hasSigningSecret": true,
    "signingSecretLast4": "9c1f",
    "…": "…"
  },
  "signingSecret": "b3s_sAmPLeSigNingSecret000000",
  "effective": "immediately",
  "previousSecretValid": false,
  "note": "This is the only time this secret is shown. The previous one stopped working immediately."
}

Errors

StatusCodeWhen
404not_foundKey not found.
409conflictThe key has been revoked.
409compat_enabledenforce is true and the key's single-step URL is switched on. The two cannot both be on. details carries the keyPrefix.
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 key with that id belongs to a publisher you own, or you named a workspace with ?agencyId= that is not yours.