Publisher portal

The login you give a publisher: their own calls, their own pings with the reason for each no-bid, their payouts and the changes made to their lanes — and the three things they never see.

After this page you can switch a publisher's portal on, invite the right people to it, choose what each of them may read, and answer confidently when a partner asks what the portal shows them about your buyers. The short answer is nothing.

The publisher portal answers the questions a partner would otherwise send to you — "did my call come through?", "why did you not bid?", "what am I owed?" — from the same records you read, cut down to their own side. It is available on plans that include it; see pricing.

Switching it on

  1. 1

    Turn the portal on for that publisher

    It is off until you do. An invitation to a door that will not open is worse than no invitation, so inviting anybody before this is refused with a sentence saying so.

  2. 2

    Invite a person

    An invitation is per email address and lasts seven days. The token comes back once and is what the invitee exchanges for a login.

    curl
    curl -X POST "https://api.buy3.io/api/publishers/c4e1a2b3-9d8f-4f6e-b5a4-1c2d3e4f5a6b/invitations" \
      -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{ "email": "ops@example.com", "invitedName": "Northwind operations" }'
    201
    {
      "invitation": {
        "id": "8e7d6c5b-4a39-4827-9615-04938271650a",
        "email": "ops@example.com",
        "expiresAt": "2026-09-27T10:14:02.000Z"
      },
      "token": "5b4a39281706495883726150493827160ab1cd2e",
      "note": "Store this token now — it is shown only once."
    }
  3. 3

    Set what they may read

    Each member carries a role and three independent switches. They are switches rather than roles because the questions they answer are genuinely independent — the person who chases missing calls is often not the person who should see money.

SettingWhat it allows
Role — owner, admin, memberWho may manage the publisher's own team. Every role reaches the portal.
Can view callsThe call log and one call's detail. Off hides both.
Can view payoutsThe payouts page and the money on the overview.
Can rotate keysRotating a routing key from the portal — and only when the key also allows self-rotation. Both switches must be on.
One login can belong to several publishers; the portal offers a switcher, and it only ever lists accounts that login already belongs to.

What a publisher sees

PageWhat is on it
OverviewToday and yesterday side by side: calls, connected, converted and what they have earned, on their own time zone rather than your server's.
CampaignsEvery lane they hold: their payout terms in words, the seconds a call has to last, their caps with what is left of each, the states and hours the lane accepts, and any note you wrote for them.
CallsTheir own calls: when, from which state, how long, whether it was billable, the payout and the disposition.
Ping logEvery ping they sent, with your answer, the time it took and — for a no-bid — the reject code, a sentence in plain words and whose move it is.
StatsThe same figures rolled up over a window they choose.
Routing keysEach key's prefix, its state, its limits, its allowlist, whether it must sign, and the single-step URL when the lane has one.
IntegrationA generated integration guide: the exact URLs this deployment serves, their own key prefixes, and a ping example built from their campaign's required tags, so pasting it produces a bid rather than a refusal.
PayoutsStatements by period, with the amount, the method, the status and a reference.
ActivityWhat was done to their account, by whom, from what to what: a cap changed, a key rotated, payout terms edited, a member added.
Shared notesNotes you wrote to them, with who wrote each one.
One row of their ping log
{
  "id": "3f2e1d0c-9b8a-4765-8432-10fedcba9876",
  "ref": "AU-100483",
  "at": "2026-09-20T14:03:11.000Z",
  "campaign": { "id": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d", "name": "Acme Health inbound" },
  "outcome": "no_bid",
  "reason": {
    "code": 1006,
    "reason": "outside_hours",
    "sentence": "The ping arrived outside the hours this campaign, or your lane on it, is open.",
    "owner": "network"
  },
  "payoutCents": 0,
  "elapsedMs": 41,
  "callerMasked": "••••••0142",
  "callerState": "TX",
  "environment": "production",
  "publisherRef": "lead-88231",
  "externalCallId": "pbx-5531902",
  "statusCode": 200,
  "expiresAt": null,
  "callRef": null
}

Every refusal in their ledger carries an owner — whose move it is — and the summary adds them up. It is the one line that tells a publisher whether to look at their own integration or pick up the phone.

OwnerMeans
publisherSomething in the request, or on their side, to change: a bad caller id, a missing tag, a rate limit, a state they are not filtering.
networkA rule you set: status, hours, states, caps, the lane itself. They have to ask you.
demandNobody wanted that caller, or nobody had room. Nothing to fix; it changes call by call.
platformOurs.

What a publisher never sees

  • A buyer price, a bid, or a margin. Not directly, and not as two numbers that divide into one — which is why a revenue-share lane reports its mode, its floor and its ceiling and never the percentage.
  • Any buyer or target, by name or by id, on a call, a ping or a waterfall.
  • Another publisher's anything, and no campaign they do not hold an active lane on.
  • The caller's full number, unless the campaign discloses caller ID. Otherwise it is masked to the last four digits — in the column and inside the stored request, because a mask on one and not the other is decoration.
  • Your internal notes about them, and the internal half of any change: an internal note is recorded in their activity as having changed, and no more.

Key health

A routing key that silently stopped working looks exactly like a campaign that went quiet, so the portal does not leave a publisher to work it out from a table. Routing keys carries a count of problems, and the overview flags them.

ProblemWhat the publisher is told
revokedThe key no longer authenticates. They should ask for a replacement.
disabledThe key has been switched off.
rotatingA replacement is live, and this one stops working at a stated time. This is the normal state mid-rotation, not a fault.
never_usedThe key has never been presented — the integration has not gone out yet. It is the quiet failure that costs the most days.
publisher_not_liveThe account itself is not live, so every request is refused. The reject code is named.

Rotating a key from the portal

A publisher can replace their own routing key only when both switches are on: the person holds Can rotate keys, and the key itself allows self-rotation. The first is their account owner deciding who may; the second is you deciding which lanes are safe to let a partner cut over unsupervised.

  • The replacement keeps the lane's configuration — protocol, allowlist, rate limits, the single-step URL — so a rotation never quietly moves them onto a surface their key cannot use.
  • The previous key keeps working for 24 hours, so they can deploy without dropping pings.
  • A key that signs gets a fresh signing secret with its fresh key, and the two must be deployed together: the old key keeps the old secret for its grace window, and the new key only ever accepts the new one.
  • Every rotation is on your activity log and on theirs, naming who did it.

Terms

A publisher account can carry a terms document and a version. When a version is published and nothing has been accepted, the portal shows it as outstanding until somebody accepts it, and records who accepted which version and when. Accepting twice does not move that record: the first acceptance is the one that happened.

A worked example

Northwind Media email on a Monday: "we sent 400 calls on Friday and only 90 came through — what is wrong with your endpoint?"

  1. You switch their portal on and invite their operations address. They accept and land on Overview.
  2. They open Ping log, set the window to Friday and sort by reason. Two thirds of the no-bids are 1008 state_not_targeted, owner publisher.
  3. They open one of those pings. What the publisher sent shows state: "AR"; What we answered shows the refusal. Nothing about your buyers appears anywhere on the page.
  4. They filter their own media to the three states on the lane. Their fill rate on Stats moves the same afternoon.
  5. The remaining refusals are 1006 outside_hours, owner network. That one is yours: the lane's hours are narrower than they remember agreeing, and the change is on their Activity page with the old value and the new.

No call detail, no buyer name and no price left your side, and the conversation finished without either party guessing.

Next steps