API reference

Webhooks

Create webhook and pixel endpoints, send a test, read their deliveries, replay failures and rotate signing secrets.

This group manages the endpoints the platform delivers events to. A webhook is a signed JSON envelope posted to your server. A pixel is a URL of yours, templated with macros, fired when an event happens — an ad network's conversion URL, usually. They are the same object with a different kind, so everything below applies to both unless it says otherwise. How deliveries behave is in Webhooks, and the events themselves are listed in Events.

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. No API key can create, edit, test or replay an endpoint: a URL this platform will call from inside your workspace is a person's decision. The one exception is the event vocabulary, which any valid key may read at /api/v1/webhooks/events.

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, testing, replaying and rotating a secret need a plan that includes webhooks and pixels. Pausing and deleting never do — a workspace that has changed plan must still be able to stop what it already has.
  • How many endpoints a workspace may hold is a plan limit, counting webhooks and pixels together. See pricing.
  • Every write here is recorded in the activity log under the kind webhook. headers and bodyTemplate are recorded as *changed*, never printed: a header is where a receiver's bearer token lives.

What you can do

OperationNotes
Read the vocabularyEvery event, every macro a pixel may use, a fully filled sample payload and the retry schedule — enough to build an editor against.
List and create endpointsChoose the kind, the URL, the events, and optionally one campaign or one publisher to narrow it to.
Update and delete an endpointIncluding pausing it. A paused endpoint is never called, and nothing is queued for it while it is paused.
Send a testQueues one delivery built from your own most recent call, so you test your handler against your own data.
Read the delivery logOne entry per event, its attempts inside it, with the request, the response and any error.
ReplayOne delivery, or every event still failing on an endpoint.
Rotate the signing secretA new secret, shown once. The old one stops verifying at once.

The endpoint object

FieldTypeMeaning
iduuidThe endpoint's id.
namestring | nullYour name for it.
kindstringwebhook or pixel. It cannot be changed after creation.
urlstringWhere deliveries go. For a pixel, a template.
methodstringGET or POST. A webhook is always POST.
eventPatternstring[]The events subscribed to, or ["*"] for all of them.
statusstringactive, degraded or paused. See Retries and health.
campaignId, campaignNameuuid, string | nullThe campaign delivery is narrowed to.
publisherId, publisherNameuuid, string | nullThe publisher delivery is narrowed to.
headersobjectThe extra headers sent with each delivery.
bodyTemplatestring | nullA POST pixel's body template.
notestring | nullYour own note.
signedbooleanWhether deliveries carry a signature. false only for a GET pixel, which has no body to sign.
secretHintstring | nullThe first and last few characters of the signing secret, so you can tell two apart. Never the secret.
deliveredCount, failedCountintegerLifetime counters.
statsobjectThe last 24 hours: { window, attempts, delivered, failed, p50, p95, lastStatus, lastOk, lastAt }. p50 and p95 are milliseconds. A lastStatus of 0 means the request never reached a server.
createdBy, createdAt, updatedAtuuid | null, ISO 8601Who made it and when it last changed.

What a receiver gets

A webhook is a POST of this envelope. data is the payload core — the same 26 keys for every call event, with null where the call has no such fact. GET /api/tracking/webhooks/events returns a fully filled sample of it, and a test send is that exact shape plus "test": true.

The envelope
{
  "id": "req_5f0c2a91b7d34e10",
  "event": "call.converted",
  "createdAt": "2026-09-20T14:06:24.512Z",
  "data": {
    "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
    "ref": "CA-482137",
    "status": "completed",
    "campaignName": "Medicare — Inbound",
    "publisherName": "Northwind Media",
    "buyerName": "Acme Health",
    "targetName": "Acme Health — Dallas floor",
    "callerNumber": "+14155550142",
    "dialedNumber": "+18885550100",
    "talkSeconds": 184,
    "converted": true,
    "revenueCents": 4200,
    "payoutCents": 2750,
    "tags": { "gclid": "Cj0KCQjw" }
  }
}
HeaderValue
content-typeapplication/json
user-agentbuy3-webhooks/1
buy3-eventThe event name.
buy3-request-idThe envelope's id. Stable across retries and replays — de-duplicate on it.
buy3-attempt1 to 4. Anything higher is a replay somebody asked for.
buy3-signaturet=<unix seconds>,v1=<hex>. Absent on a GET pixel, which has no body to sign.
your ownUp to 20 headers of your choosing, macros expanded.
createdAt is the time of this send, so it — and therefore the signature — differs between attempts. Verify over the raw bytes before parsing: Verifying a webhook signature.

Retries and health

  • Success is any 2xx. A 3xx is a failure: redirects are never followed, because the address that was vetted is the address that gets called.
  • Each attempt times out after 10 seconds.
  • Four attempts in all, so three waits: 1 second after the first failure, 5 after the second, 25 after the third. The dispatcher runs on a 15-second tick, so a wait is a floor rather than a promise. GET /webhooks/events publishes the same schedule as retries.
  • After the fourth failure the machine gives up. Only a replay revives the event, as attempt 5 under the same request id.
  • Two consecutive failures mark the endpoint degraded and write an activity entry with the system as the actor. The next success sets it active again.
  • A paused endpoint is never called, nothing new is queued for it, and anything already queued waits until it is resumed.
  • The log keeps the first 2,048 bytes of your answer, and for a POST pixel the first 8,192 bytes of what was sent.

The delivery log

GET /webhooks/:id/deliveries returns one entry per event, with its attempts inside it. A delivery that failed three times and then landed is one story with a happy ending, and a flat list of attempts prints it as three failures and a success on four unrelated rows.

stateMeans
deliveredSome attempt was accepted.
queuedNone was, and one is still waiting — a retry inside its backoff, or a replay.
failedNone was, and nothing is waiting. These are what replay failures replays.
One vocabulary: the word the state filter takes is the word an entry and each attempt inside it carry.

Pixels, macros and the caller's number

  • A pixel's url is a template. Every value is percent-encoded as it goes in.
  • A caller's number never reaches a URL. {{caller.e164}}, {{caller.digits}}, {{caller.nanp}} and {{caller.suffix}} are sent as {{caller.hash}} instead, and a tag whose value contains the caller's ten digits becomes the hash too. There is no way to switch this off. Those tokens do expand in a POST body.
  • The host may not contain a macro. Whoever controls a tag would otherwise choose where this platform sends a request.
  • A token the vocabulary does not know is a 400 when you save, naming it — not an empty value discovered a month later. A {{tag.…}} key is never unknown.
  • A missing value expands to nothing. Write {{{ for a literal {{. There is no expression language.

Endpoints

The event and macro vocabulary

GET/api/tracking/webhooks/events

Everything an editor needs: the ten events with a sentence each, every macro a pixel may use, a sample envelope and payload with all 26 keys filled, the headers a delivery carries and the retry schedule the dispatcher actually runs. Takes no parameters and changes nothing. The same answer is on the REST API at GET /api/v1/webhooks/events, where any valid key opens it and no scope is needed.

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.

Example request

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

Responses

  • 200Abbreviated: events lists all ten, macros the whole pixel vocabulary. Every token in macros expands to something against sample.payload — that is held by a test.
{
  "events": [
    {
      "event": "call.completed",
      "description": "The call ended. Sent for every call, converted or not, with its final duration and figures."
    },
    {
      "event": "call.converted",
      "description": "The call earned its price — by talk time, or because the buyer reported a conversion."
    }
  ],
  "macros": [
    {
      "group": "Call",
      "token": "{{call.ref}}",
      "description": "The call's public reference.",
      "example": "CA-482137"
    },
    {
      "group": "Caller",
      "token": "{{caller.e164}}",
      "description": "The caller's number.",
      "example": "+14155550142",
      "inUrl": "hashed"
    }
  ],
  "sample": {
    "event": "call.converted",
    "envelope": {
      "id": "req_5f0c2a91b7d34e10",
      "event": "call.converted",
      "createdAt": "2026-09-20T14:06:24.512Z",
      "data": {
        "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
        "ref": "CA-482137",
        "status": "completed",
        "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
        "campaignName": "Medicare — Inbound",
        "publisherId": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
        "publisherName": "Northwind Media",
        "publisherRef": "nw-88231",
        "buyerId": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
        "buyerName": "Acme Health",
        "targetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
        "targetName": "Acme Health — Dallas floor",
        "targetExternalRef": "acme-dal-01",
        "callerNumber": "+14155550142",
        "callerState": "CA",
        "callerCity": "San Francisco",
        "callerZip": "94103",
        "dialedNumber": "+18885550100",
        "startedAt": "2026-09-20T14:03:11.000Z",
        "answeredAt": "2026-09-20T14:03:19.000Z",
        "endedAt": "2026-09-20T14:06:23.000Z",
        "talkSeconds": 184,
        "converted": true,
        "revenueCents": 4200,
        "payoutCents": 2750,
        "tags": {
          "gclid": "Cj0KCQjw",
          "utm_source": "google"
        }
      }
    },
    "payload": {
      "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
      "ref": "CA-482137",
      "status": "completed",
      "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
      "campaignName": "Medicare — Inbound",
      "publisherId": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
      "publisherName": "Northwind Media",
      "publisherRef": "nw-88231",
      "buyerId": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
      "buyerName": "Acme Health",
      "targetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
      "targetName": "Acme Health — Dallas floor",
      "targetExternalRef": "acme-dal-01",
      "callerNumber": "+14155550142",
      "callerState": "CA",
      "callerCity": "San Francisco",
      "callerZip": "94103",
      "dialedNumber": "+18885550100",
      "startedAt": "2026-09-20T14:03:11.000Z",
      "answeredAt": "2026-09-20T14:03:19.000Z",
      "endedAt": "2026-09-20T14:06:23.000Z",
      "talkSeconds": 184,
      "converted": true,
      "revenueCents": 4200,
      "payoutCents": 2750,
      "tags": {
        "gclid": "Cj0KCQjw",
        "utm_source": "google"
      }
    },
    "pixel": {
      "template": "https://ads.example.com/conversion?click={{tag.gclid}}&value={{call.revenue}}&ref={{call.ref}}&caller={{caller.e164}}",
      "expanded": "https://ads.example.com/conversion?click=Cj0KCQjw&value=42.00&ref=CA-482137&caller=9c1185a5c5e9fc54"
    }
  },
  "headers": [
    "buy3-event",
    "buy3-request-id",
    "buy3-attempt",
    "buy3-signature"
  ],
  "retries": {
    "maxAttempts": 4,
    "backoffSeconds": [
      1,
      5,
      25
    ]
  }
}

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_foundAn agencyId that is not a workspace you administer answers the same 404.

The event and macro vocabulary, for a key

GET/api/v1/webhooks/events

The same answer as the console route above, byte for byte — it is the same handler behind a different door. It is the only route in this group a key can reach, and the only route on the REST API that asks for no scope: the vocabulary is the same for every workspace, so any valid key may read it. Everything else here needs a person, because a URL this platform will call from inside your workspace is a person's decision.

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

Example request

curl "https://api.buy3.io/api/v1/webhooks/events" \
  -H "Authorization: Bearer $BUY3_API_KEY"

Responses

  • 200As above: events, macros, sample, headers and retries. Abbreviated here.
{
  "events": [
    {
      "event": "call.completed",
      "description": "The call ended. Sent for every call, converted or not, with its final duration and figures."
    }
  ],
  "macros": [
    {
      "group": "Call",
      "token": "{{call.ref}}",
      "description": "The call's public reference.",
      "example": "CA-482137"
    }
  ],
  "headers": [
    "buy3-event",
    "buy3-request-id",
    "buy3-attempt",
    "buy3-signature"
  ],
  "retries": {
    "maxAttempts": 4,
    "backoffSeconds": [
      1,
      5,
      25
    ]
  }
}

Errors

StatusCodeWhen
401missing_key, invalid_keyNo Authorization: Bearer header, or a key that is not recognised, revoked, or past its rotation grace.
402subscription_inactive, plan_upgrade_requiredThe workspace's subscription is not active, or its plan does not include call tracking or API access. The REST API has no read-open rule: a lapsed workspace is refused here as well.
403ip_not_allowed, workspace_requiredThe request came from an address that is not on the key's allowlist, or the key belongs to no workspace.
429rate_limitedThe key is over its per-minute limit. Retry-After says when to come back.

List endpoints

GET/api/tracking/webhooks

The workspace's webhooks and pixels, newest first, each with its last 24 hours of stats — so one request answers which receiver is slow as well as which is failing. 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
kindOptionalstringOnly endpoints of this kind.webhook or pixel
statusOptionalstringOnly endpoints in this state.active, degraded or paused
campaignIdOptionaluuidOnly endpoints narrowed to this campaign.
publisherIdOptionaluuidOnly endpoints narrowed to this publisher.
qOptionalstringMatches part of the name, the URL or the note, 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/webhooks?kind=webhook&status=degraded" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200One page of endpoints in the standard list envelope.
{
  "data": [
    {
      "id": "7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071",
      "name": "Conversion sync",
      "kind": "webhook",
      "url": "https://hooks.example.com/buy3/calls",
      "method": "POST",
      "eventPattern": [
        "call.completed",
        "call.converted"
      ],
      "status": "active",
      "campaignId": null,
      "campaignName": null,
      "publisherId": null,
      "publisherName": null,
      "headers": {
        "x-team": "revops"
      },
      "bodyTemplate": null,
      "note": null,
      "signed": true,
      "secretHint": "whsec_9Qd2••••••••x71A",
      "deliveredCount": 18204,
      "failedCount": 37,
      "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
      "createdAt": "2026-08-02T11:20:41.000Z",
      "updatedAt": "2026-09-18T09:02:12.000Z",
      "stats": {
        "window": "24h",
        "attempts": 412,
        "delivered": 409,
        "failed": 3,
        "p50": 118,
        "p95": 840,
        "lastStatus": 200,
        "lastOk": true,
        "lastAt": "2026-09-20T14:06:24.981Z"
      }
    },
    {
      "id": "9c8b7a65-4321-4098-8765-43210fedcba9",
      "name": "Ad network conversion",
      "kind": "pixel",
      "url": "https://ads.example.com/conversion?click={{tag.gclid}}&value={{call.revenue}}&ref={{call.ref}}",
      "method": "GET",
      "eventPattern": [
        "call.converted"
      ],
      "status": "active",
      "campaignId": null,
      "campaignName": null,
      "publisherId": null,
      "publisherName": null,
      "headers": {},
      "bodyTemplate": null,
      "note": null,
      "signed": false,
      "secretHint": null,
      "deliveredCount": 0,
      "failedCount": 0,
      "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
      "createdAt": "2026-09-20T14:00:02.000Z",
      "updatedAt": "2026-09-20T14:00:02.000Z",
      "stats": {
        "window": "24h",
        "attempts": 0,
        "delivered": 0,
        "failed": 0,
        "p50": 0,
        "p95": 0,
        "lastStatus": null,
        "lastOk": null,
        "lastAt": null
      }
    }
  ],
  "page": 1,
  "pageSize": 25,
  "total": 2,
  "totalPages": 1
}

Errors

StatusCodeWhen
400validation_errorkind or status is not one of the allowed words, or an id filter is not a UUID.
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 an endpoint

POST/api/tracking/webhooks

Creates a webhook or a pixel and, for anything that signs, mints its signing secret — returned in this response and never again. The URL is resolved and vetted before the row exists, and again at every delivery, because the name behind it is yours to change. Recorded in the activity log (webhook, high severity). It sends nothing: the first delivery is the first matching event.

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
nameRequiredstringYour name for the endpoint. It is what the activity log and the delivery log call it.up to 120 characters
urlRequiredstringWhere deliveries go. For a pixel this is a template and may carry macros in the path and the query string — but never in the host.up to 2000 characters · an http(s):// URL with no spaces · must pass the address rules below
kindOptionalstringWhat this is. It cannot be changed afterwards — make a new one instead.webhook or pixel · default webhook
eventPatternRequiredstring[] | stringThe events to subscribe to, or "*" (or ["*"]) for all of them. A tenant endpoint only ever hears the ten tenant events, so * can never come to mean somebody else's traffic.at most 20 event names · at least one
methodOptionalstringHow the endpoint is called. A webhook must be a POST. A pixel is a GET unless you say otherwise, which is what an ad network's conversion URL is.GET or POST · default POST for a webhook, GET for a pixel
campaignIdOptionaluuidNarrow delivery to one campaign of yours. A narrowed endpoint never hears an event that names no campaign.
publisherIdOptionaluuidNarrow delivery to one publisher of yours. A narrowed endpoint never hears an event that names no publisher — a call on your own media, for instance.
headersOptionalobjectExtra request headers. Values may use macros. Send null to clear them.at most 20 entries · each value a single line of at most 1000 characters · hop-by-hop names and any name starting buy3- are refused
bodyTemplateOptionalstringThe body of a POST pixel. Without one, a pixel posts the standard envelope. It is escaped for its own content type: JSON by default, percent-encoded for application/x-www-form-urlencoded, verbatim for anything else.up to 10,000 characters · a POST pixel only · must expand to valid JSON unless a non-JSON content-type header is set
noteOptionalstringA note to yourself. It is searched by q.up to 500 characters

Example request

curl -X POST "https://api.buy3.io/api/tracking/webhooks" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
      "name": "Conversion sync",
      "url": "https://hooks.example.com/buy3/calls",
      "eventPattern": ["call.completed", "call.converted"],
      "headers": {
        "x-team": "revops"
      }
    }'

Responses

  • 201The endpoint, plus signingSecret for anything that signs and notes — sentences worth showing a person, such as the fact that a caller macro in a URL is sent hashed.
{
  "webhook": {
    "id": "7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071",
    "name": "Conversion sync",
    "kind": "webhook",
    "url": "https://hooks.example.com/buy3/calls",
    "method": "POST",
    "eventPattern": [
      "call.completed",
      "call.converted"
    ],
    "status": "active",
    "campaignId": null,
    "campaignName": null,
    "publisherId": null,
    "publisherName": null,
    "headers": {
      "x-team": "revops"
    },
    "bodyTemplate": null,
    "note": null,
    "signed": true,
    "secretHint": "whsec_9Qd2••••••••x71A",
    "deliveredCount": 0,
    "failedCount": 0,
    "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
    "createdAt": "2026-09-20T14:00:02.000Z",
    "updatedAt": "2026-09-20T14:00:02.000Z",
    "stats": {
      "window": "24h",
      "attempts": 0,
      "delivered": 0,
      "failed": 0,
      "p50": 0,
      "p95": 0,
      "lastStatus": null,
      "lastOk": null,
      "lastAt": null
    }
  },
  "signingSecret": "whsec_9Qd2rN7xKp0aVb3cLm5eTw8hx71A",
  "note": "Store this signing secret now — it is shown only once.",
  "notes": []
}

Errors

StatusCodeWhen
400validation_errorA required field is missing, or a field has the wrong type or is too long. details maps each field to what was wrong with it.
400bad_requestThe configuration cannot work. details names the part: method (a webhook must be a POST), url (a macro in the host), macros (naming each unknown token), bodyTemplate (it applies to a POST pixel, or it does not expand to valid JSON), headers (a reserved name, or a value that is too long or spans two lines) or eventPattern (it names no event).
400unsafe_urlThe URL is not one this platform will call: it is not https in production, it carries credentials in front of the host, or the name resolves to a private, loopback, link-local or metadata address. details.rule says which rule it broke. A pixel is judged on its template expanded against the sample event.
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 webhooks and pixels (details.feature is webhooks). It is also the answer when the workspace already holds as many endpoints as its plan allows — then details.feature is maxWebhooks and details carries limit and current beside it.
403forbiddenYou named no workspace and hold no owner or admin seat on any.
404not_foundThe campaignId or publisherId is not in the workspace. An agencyId that is not a workspace you administer answers the same 404.

Read an endpoint

GET/api/tracking/webhooks/:id

One endpoint with its last 24 hours of stats. Changes nothing, and never returns the signing secret.

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

Example request

curl "https://api.buy3.io/api/tracking/webhooks/7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200The endpoint.
{
  "webhook": {
    "id": "7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071",
    "name": "Conversion sync",
    "kind": "webhook",
    "url": "https://hooks.example.com/buy3/calls",
    "method": "POST",
    "eventPattern": [
      "call.completed",
      "call.converted"
    ],
    "status": "active",
    "campaignId": null,
    "campaignName": null,
    "publisherId": null,
    "publisherName": null,
    "headers": {
      "x-team": "revops"
    },
    "bodyTemplate": null,
    "note": null,
    "signed": true,
    "secretHint": "whsec_9Qd2••••••••x71A",
    "deliveredCount": 18204,
    "failedCount": 37,
    "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
    "createdAt": "2026-08-02T11:20:41.000Z",
    "updatedAt": "2026-09-18T09:02:12.000Z",
    "stats": {
      "window": "24h",
      "attempts": 412,
      "delivered": 409,
      "failed": 3,
      "p50": 118,
      "p95": 840,
      "lastStatus": 200,
      "lastOk": true,
      "lastAt": "2026-09-20T14:06:24.981Z"
    }
  }
}

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 such endpoint in the workspace. Another workspace's endpoint 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.

Update an endpoint

PATCH/api/tracking/webhooks/:id

Changes what you send. kind is not among the fields — a webhook cannot become a pixel. name and url cannot be cleared; headers: null clears them to {}. The URL and the template are re-vetted only when url, method, headers or bodyTemplate changes. A body of exactly { "status": "paused" } needs no plan feature, so a workspace can always stop an endpoint. Recorded in the activity log (webhook, medium severity) as paused, resumed or updated.

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

Request body

NameTypeDescription
nameOptionalstringA new name.up to 120 characters
urlOptionalstringA new URL, vetted as on create.up to 2000 characters
eventPatternOptionalstring[] | stringReplaces the subscription outright — it is not merged.at most 20 event names · at least one
statusOptionalstringpaused stops deliveries; active resumes them, and also clears degraded. degraded is the dispatcher's word and cannot be set by hand.active or paused
methodOptionalstringHow the endpoint is called. A webhook must be a POST. A pixel is a GET unless you say otherwise, which is what an ad network's conversion URL is.GET or POST · default POST for a webhook, GET for a pixel
campaignIdOptionaluuidNarrow delivery to one campaign of yours. A narrowed endpoint never hears an event that names no campaign.
publisherIdOptionaluuidNarrow delivery to one publisher of yours. A narrowed endpoint never hears an event that names no publisher — a call on your own media, for instance.
headersOptionalobjectExtra request headers. Values may use macros. Send null to clear them.at most 20 entries · each value a single line of at most 1000 characters · hop-by-hop names and any name starting buy3- are refused
bodyTemplateOptionalstringThe body of a POST pixel. Without one, a pixel posts the standard envelope. It is escaped for its own content type: JSON by default, percent-encoded for application/x-www-form-urlencoded, verbatim for anything else.up to 10,000 characters · a POST pixel only · must expand to valid JSON unless a non-JSON content-type header is set
noteOptionalstringA note to yourself. It is searched by q.up to 500 characters

Example request

curl -X PATCH "https://api.buy3.io/api/tracking/webhooks/7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
      "status": "paused"
    }'

Responses

  • 200The endpoint as it now stands, with any notes the new configuration raised.
{
  "webhook": {
    "id": "7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071",
    "name": "Conversion sync",
    "kind": "webhook",
    "url": "https://hooks.example.com/buy3/calls",
    "method": "POST",
    "eventPattern": [
      "call.completed",
      "call.converted"
    ],
    "status": "paused",
    "campaignId": null,
    "campaignName": null,
    "publisherId": null,
    "publisherName": null,
    "headers": {
      "x-team": "revops"
    },
    "bodyTemplate": null,
    "note": null,
    "signed": true,
    "secretHint": "whsec_9Qd2••••••••x71A",
    "deliveredCount": 18204,
    "failedCount": 37,
    "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
    "createdAt": "2026-08-02T11:20:41.000Z",
    "updatedAt": "2026-09-18T09:02:12.000Z",
    "stats": {
      "window": "24h",
      "attempts": 412,
      "delivered": 409,
      "failed": 3,
      "p50": 118,
      "p95": 840,
      "lastStatus": 200,
      "lastOk": true,
      "lastAt": "2026-09-20T14:06:24.981Z"
    }
  },
  "notes": []
}

Errors

StatusCodeWhen
400validation_errorA field has the wrong type or is out of range.
400bad_requestThe body is empty ("Nothing to update"), or name or url was sent as null. The configuration cannot work. details names the part: method (a webhook must be a POST), url (a macro in the host), macros (naming each unknown token), bodyTemplate (it applies to a POST pixel, or it does not expand to valid JSON), headers (a reserved name, or a value that is too long or spans two lines) or eventPattern (it names no event).
400unsafe_urlThe URL is not one this platform will call: it is not https in production, it carries credentials in front of the host, or the name resolves to a private, loopback, link-local or metadata address. details.rule says which rule it broke. A pixel is judged on its template expanded against the sample event.
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 webhooks and pixels (details.feature is webhooks). It is also the answer when the workspace already holds as many endpoints as its plan allows — then details.feature is maxWebhooks and details carries limit and current beside it.
403forbiddenYou named no workspace and hold no owner or admin seat on any.
404not_foundNo such endpoint in the workspace. Another workspace's endpoint 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.

Delete an endpoint

DELETE/api/tracking/webhooks/:id

Deletes the endpoint and its whole delivery log with it. There is no undo and no export afterwards, so read the deliveries you need first. Never gated on the plan. Recorded in the activity log (webhook, medium severity) with the lifetime counters kept in the entry.

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

Example request

curl -X DELETE "https://api.buy3.io/api/tracking/webhooks/7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 204Deleted. No body.

Errors

StatusCodeWhen
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 endpoint in the workspace. Another workspace's endpoint 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.

Send a test delivery

POST/api/tracking/webhooks/:id/test

Queues one delivery built from the workspace's most recent real call — inside the endpoint's own campaign or publisher narrowing — falling back to sample data when the workspace has no calls yet. It is built by the same function every live event is built by, so apart from "test": true it is, key for key, what that call's own event carried. Its request id begins test_. Nothing is sent while you wait: the answer is 202 and the delivery log has the result within about 15 seconds. Recorded in the activity log (webhook, low 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 endpoint's id.

Request body

NameTypeDescription
eventOptionalstringWhich event to imitate.one of the ten event names · default: the endpoint's first subscribed event, else call.completed

Example request

curl -X POST "https://api.buy3.io/api/tracking/webhooks/7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071/test" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
      "event": "call.converted"
    }'

Responses

  • 202source says whether the payload came from one of your calls or from the fixture. The delivery is returned in full, queued: no status code yet.
{
  "delivery": {
    "id": "9928640",
    "requestId": "test_1f0a9b8c-7d6e-4f50-a1b2-c3d4e5f60718",
    "event": "call.converted",
    "endpointId": "7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071",
    "endpoint": "https://hooks.example.com/buy3/calls",
    "method": "POST",
    "code": null,
    "ms": 0,
    "attempt": 0,
    "ok": false,
    "state": "queued",
    "error": null,
    "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
    "at": null,
    "url": "https://hooks.example.com/buy3/calls",
    "payload": {
      "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
      "ref": "CA-482137",
      "status": "completed",
      "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
      "campaignName": "Medicare — Inbound",
      "publisherId": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
      "publisherName": "Northwind Media",
      "publisherRef": "nw-88231",
      "buyerId": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
      "buyerName": "Acme Health",
      "targetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
      "targetName": "Acme Health — Dallas floor",
      "targetExternalRef": "acme-dal-01",
      "callerNumber": "+14155550142",
      "callerState": "CA",
      "callerCity": "San Francisco",
      "callerZip": "94103",
      "dialedNumber": "+18885550100",
      "startedAt": "2026-09-20T14:03:11.000Z",
      "answeredAt": "2026-09-20T14:03:19.000Z",
      "endedAt": "2026-09-20T14:06:23.000Z",
      "talkSeconds": 184,
      "converted": true,
      "revenueCents": 4200,
      "payoutCents": 2750,
      "tags": {
        "gclid": "Cj0KCQjw",
        "utm_source": "google"
      },
      "test": true
    },
    "requestBody": null,
    "responseBody": null,
    "retryAfter": null
  },
  "source": "call",
  "note": "Queued a test call.converted built from your most recent call (CA-482137). It is sent within 15 seconds; the delivery log shows the request and the response."
}

Errors

StatusCodeWhen
400validation_errorevent is not one of the ten event names.
409conflictThe endpoint is paused — a paused endpoint is never called, so the test would sit queued for ever. Or five test deliveries are already waiting on it.
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 webhooks and pixels (details.feature is webhooks). It is also the answer when the workspace already holds as many endpoints as its plan allows — then details.feature is maxWebhooks and details carries limit and current beside it.
403forbiddenYou named no workspace and hold no owner or admin seat on any.
404not_foundNo such endpoint in the workspace. Another workspace's endpoint 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.

List deliveries

GET/api/tracking/webhooks/:id/deliveries

One entry per event, newest event first, with every attempt inside it and the payload printed once. Each attempt carries the address actually called — a pixel's template expanded — and the first 2,048 bytes of the answer. 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 endpoint's id.

Query parameters

NameTypeDescription
stateOptionalstringThe state of the event, not of one attempt.delivered, queued or failed
eventOptionalstringOnly this event name.up to 100 characters
callIdOptionaluuidOnly deliveries about this call. This is how you answer "did our CRM hear about this call".
pageOptionalintegerThe page to return.from 1, default 1
pageSizeOptionalintegerEvents per page. A larger value is clamped, not refused.1–200 · default 25

Example request

curl "https://api.buy3.io/api/tracking/webhooks/7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071/deliveries?state=failed&pageSize=50" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200attempts counts the attempts actually made, so a queued retry is not counted yet. lastCode is the most recent HTTP status; 0 means the request never reached a server.
{
  "data": [
    {
      "requestId": "req_4c1b0a98d7e64f22",
      "event": "call.completed",
      "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
      "state": "delivered",
      "attempts": 2,
      "firstAt": "2026-09-20T14:06:24.102Z",
      "lastAt": "2026-09-20T14:06:25.640Z",
      "lastCode": 200,
      "payload": {
        "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
        "ref": "CA-482137",
        "status": "completed",
        "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
        "campaignName": "Medicare — Inbound",
        "publisherId": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
        "publisherName": "Northwind Media",
        "publisherRef": "nw-88231",
        "buyerId": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
        "buyerName": "Acme Health",
        "targetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
        "targetName": "Acme Health — Dallas floor",
        "targetExternalRef": "acme-dal-01",
        "callerNumber": "+14155550142",
        "callerState": "CA",
        "callerCity": "San Francisco",
        "callerZip": "94103",
        "dialedNumber": "+18885550100",
        "startedAt": "2026-09-20T14:03:11.000Z",
        "answeredAt": "2026-09-20T14:03:19.000Z",
        "endedAt": "2026-09-20T14:06:23.000Z",
        "talkSeconds": 184,
        "converted": true,
        "revenueCents": 4200,
        "payoutCents": 2750,
        "tags": {
          "gclid": "Cj0KCQjw",
          "utm_source": "google"
        }
      },
      "deliveries": [
        {
          "id": "9928470",
          "requestId": "req_4c1b0a98d7e64f22",
          "event": "call.completed",
          "endpointId": "7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071",
          "endpoint": "https://hooks.example.com/buy3/calls",
          "method": "POST",
          "code": 502,
          "ms": 240,
          "attempt": 1,
          "ok": false,
          "state": "failed",
          "error": "Bad Gateway",
          "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
          "at": "2026-09-20T14:06:24.102Z",
          "url": "https://hooks.example.com/buy3/calls",
          "requestBody": null,
          "responseBody": "upstream connect error",
          "retryAfter": null
        },
        {
          "id": "9928471",
          "requestId": "req_4c1b0a98d7e64f22",
          "event": "call.completed",
          "endpointId": "7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071",
          "endpoint": "https://hooks.example.com/buy3/calls",
          "method": "POST",
          "code": 200,
          "ms": 118,
          "attempt": 2,
          "ok": true,
          "state": "delivered",
          "error": null,
          "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
          "at": "2026-09-20T14:06:25.640Z",
          "url": "https://hooks.example.com/buy3/calls",
          "requestBody": null,
          "responseBody": "{\"ok\":true}",
          "retryAfter": null
        }
      ]
    }
  ],
  "page": 1,
  "pageSize": 25,
  "total": 1,
  "totalPages": 1
}

Errors

StatusCodeWhen
400validation_errorstate is not one of the three words, or callId is not a UUID.
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 endpoint in the workspace. Another workspace's endpoint 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.

Replay one delivery

POST/api/tracking/webhook-deliveries/:id/replay

Re-queues that event under the same request id, with the attempt number carried forward — so a fifth attempt in the log is a person overriding the give-up rule, which is exactly what it was. Two clicks are one replay, because your receiver de-duplicates on the request id. Answers 202; the send follows within about 15 seconds. Recorded in the activity log (webhook, medium 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
idRequiredstringThe delivery's id, from the delivery log. It is a large integer sent as a string, not a UUID.1–18 digits

Example request

curl -X POST "https://api.buy3.io/api/tracking/webhook-deliveries/9928470/replay" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 202The re-queued attempt.
{
  "delivery": {
    "id": "9928702",
    "requestId": "req_5f0c2a91b7d34e10",
    "event": "call.converted",
    "endpointId": "7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071",
    "endpoint": "https://hooks.example.com/buy3/calls",
    "method": "POST",
    "code": null,
    "ms": 0,
    "attempt": 5,
    "ok": false,
    "state": "queued",
    "error": null,
    "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
    "at": null,
    "url": "https://hooks.example.com/buy3/calls",
    "payload": {
      "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
      "ref": "CA-482137",
      "status": "completed",
      "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d",
      "campaignName": "Medicare — Inbound",
      "publisherId": "c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b",
      "publisherName": "Northwind Media",
      "publisherRef": "nw-88231",
      "buyerId": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
      "buyerName": "Acme Health",
      "targetId": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
      "targetName": "Acme Health — Dallas floor",
      "targetExternalRef": "acme-dal-01",
      "callerNumber": "+14155550142",
      "callerState": "CA",
      "callerCity": "San Francisco",
      "callerZip": "94103",
      "dialedNumber": "+18885550100",
      "startedAt": "2026-09-20T14:03:11.000Z",
      "answeredAt": "2026-09-20T14:03:19.000Z",
      "endedAt": "2026-09-20T14:06:23.000Z",
      "talkSeconds": 184,
      "converted": true,
      "revenueCents": 4200,
      "payoutCents": 2750,
      "tags": {
        "gclid": "Cj0KCQjw",
        "utm_source": "google"
      }
    },
    "requestBody": null,
    "responseBody": null,
    "retryAfter": null
  },
  "note": "Queued. It is sent within 15 seconds and the log records the result."
}

Errors

StatusCodeWhen
404not_foundNo such delivery in the workspace. Another workspace's delivery, and an id that is not 1–18 digits, answer exactly as a missing one does.
409conflictThe delivery is still queued — there is nothing to replay yet — or its endpoint is paused.
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 webhooks and pixels (details.feature is webhooks). It is also the answer when the workspace already holds as many endpoints as its plan allows — then details.feature is maxWebhooks 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.

Replay every failed event

POST/api/tracking/webhooks/:id/replay-failures

One replay per failed event — its latest attempt — skipping anything that has since succeeded or is already queued, so no receiver is sent an event it already took. It looks at the endpoint's newest 1,000 stamped attempts. This is the button on the banner after an outage of yours. Answers 202. Recorded in the activity log (webhook, medium 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 endpoint's id.

Request body

NameTypeDescription
limitOptionalintegerThe most events to re-queue in this call. Run it again for more.1–500 · default 200

Example request

curl -X POST "https://api.buy3.io/api/tracking/webhooks/7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071/replay-failures" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
      "limit": 100
    }'

Responses

  • 202How many events were queued, in order.
  • 202 (nothing to do)Nothing was failing.
{
  "queued": 37,
  "note": "Queued in order. Watch the delivery log for the results."
}

Errors

StatusCodeWhen
400validation_errorlimit is not an integer between 1 and 500.
409conflictThe endpoint is paused. Resume it first — a paused endpoint is never called.
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 webhooks and pixels (details.feature is webhooks). It is also the answer when the workspace already holds as many endpoints as its plan allows — then details.feature is maxWebhooks and details carries limit and current beside it.
403forbiddenYou named no workspace and hold no owner or admin seat on any.
404not_foundNo such endpoint in the workspace. Another workspace's endpoint 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 the signing secret

POST/api/tracking/webhooks/:id/secret

Mints a new signing secret and returns it once. A stored secret is never readable, so the way to recover a lost one is a new one. Takes no body. Recorded in the activity log (webhook, high severity) as the fact that the secret changed — never its value.

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

Example request

curl -X POST "https://api.buy3.io/api/tracking/webhooks/7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071/secret" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN"

Responses

  • 200The endpoint with its new secretHint, and the secret itself for the only time.
{
  "webhook": {
    "id": "7a1b2c3d-4e5f-4061-8a7b-2c3d4e5f6071",
    "name": "Conversion sync",
    "kind": "webhook",
    "url": "https://hooks.example.com/buy3/calls",
    "method": "POST",
    "eventPattern": [
      "call.completed",
      "call.converted"
    ],
    "status": "active",
    "campaignId": null,
    "campaignName": null,
    "publisherId": null,
    "publisherName": null,
    "headers": {
      "x-team": "revops"
    },
    "bodyTemplate": null,
    "note": null,
    "signed": true,
    "secretHint": "whsec_2Lp8••••••••q4Zk",
    "deliveredCount": 18204,
    "failedCount": 37,
    "createdBy": "5e4d3c2b-1a09-4887-b6c5-d4e3f2a1b0c9",
    "createdAt": "2026-08-02T11:20:41.000Z",
    "updatedAt": "2026-09-18T09:02:12.000Z",
    "stats": {
      "window": "24h",
      "attempts": 412,
      "delivered": 409,
      "failed": 3,
      "p50": 118,
      "p95": 840,
      "lastStatus": 200,
      "lastOk": true,
      "lastAt": "2026-09-20T14:06:24.981Z"
    }
  },
  "signingSecret": "whsec_2Lp8vR1yNc6fJt9wEs0bq4Zk",
  "note": "Store this signing secret now — it is shown only once. Deliveries are signed with it from the next one; update your receiver before it rejects them."
}

Errors

StatusCodeWhen
409conflictThe endpoint is a GET pixel. It has no body to sign, so it has no secret to rotate.
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 webhooks and pixels (details.feature is webhooks). It is also the answer when the workspace already holds as many endpoints as its plan allows — then details.feature is maxWebhooks and details carries limit and current beside it.
403forbiddenYou named no workspace and hold no owner or admin seat on any.
404not_foundNo such endpoint in the workspace. Another workspace's endpoint 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.