Ringba-compatible ping

A single-step ping URL, per publisher lane, that accepts the parameters a Ringba-style platform already sends and answers in the field names it already parses — so a partner can send you calls without writing new code.

After this page you can switch on a single-step ping URL for one publisher lane, hand it over, and know exactly what their platform will send, what it will get back, and the three differences from ping and post that change how a call behaves.

Many publishers already have a ping integration built for an incumbent platform, and asking them to write another is how a new partnership stalls. Each lane can therefore expose a compatible ping URL: one request that takes the parameters they already send and answers in the field names they already parse.

It is a dialect, not a second engine. Behind the URL are the same auction, the same bid rows, the same reservation, the same call record, the same rate limits, the same IP allowlist and the same ping ledger as the two-step protocol. Nothing about a call that arrives this way is different once it has arrived.

How it differs from ping and post

Ping and postSingle-step URL
RequestsTwo: ping, then post.One. The auction runs and the bid is claimed in the same request.
AuthenticationA bearer key in a header.A token in the URL itself, because that is what the sending platform can carry.
MethodPOST, or GET with the same fields.GET or POST. Both take the same parameters and answer the same shapes.
ResponseThe platform's own field names, money in cents.bidId, bidAmount, expireInSeconds, phoneNumber and the rest. Money in dollars.
A no-bidok: false with a reject object.bidAmount: 0 with a rejectReason string carrying the code in brackets.
ReleasingPOST /v1/release frees the slot at once.There is no release in the dialect. An unused claim closes itself.
Signed requestsSupported, and enforceable per key.Not possible — see the warning below.

The URL

One URL per lane
https://api.buy3.io/api/rtb/compat/ringba/<token>.json
  • The .json suffix is optional and ignored. Keep it: it is what a platform expecting a JSON endpoint is usually configured with.
  • The token is the credential and is specific to one publisher on one campaign. Treat it as opaque — letters, digits, _ and - — and never parse it.
  • It is a separate credential from the routing key, so handing out the URL never exposes the bearer key, and either can be replaced without the other.
  • A rotated routing key carries its URL forward, so a key rotation does not break a partner who only uses this door.

What the publisher sends

A GET with a query string, or a POST with a JSON body — and a POST may carry both, in which case the body wins. Parameter names are matched without regard to case, because CID, cid and Cid all arrive in the wild.

ParameterTypeRules
CIDstringUp to 32 characters. Ten digits, eleven digits starting with 1, or E.164. Anything else is a no-bid with code 1101; a campaign that requires a caller ID and gets none answers 1100.
zipcodestringUp to 12 characters.
statestringA two-letter code drives the campaign's and the lane's state filters. Anything longer is kept as the tag state instead of being refused — it is still something the publisher said about the caller.
exposeCallerIdflagWhether the caller's number may be passed to the buyer. Defaults to the campaign's setting.
sipOkflagThe publisher can take a SIP handoff. Without it no sipAddress comes back.
publisherInboundCallIdstringUp to 120 characters. The publisher's own id for the call — and the idempotency key.
anything elsescalarBecomes a tag with the source publisher: at most 40 tags, a key cut at 64 characters and a value at 512. Objects, arrays and empty values are dropped.
A flag accepts yes, y, true, 1 or on for true and no, n, false, 0 or off for false. Anything else is treated as unset.
curl -sS "https://api.buy3.io/api/rtb/compat/ringba/$BUY3_COMPAT_TOKEN.json\
?CID=2145550142\
&state=TX\
&zipcode=75201\
&exposeCallerId=yes\
&sipOk=yes\
&publisherInboundCallId=nw-88231\
&sub_id=fb-42"
The same request as a POST
curl -sS -X POST "https://api.buy3.io/api/rtb/compat/ringba/$BUY3_COMPAT_TOKEN.json" \
  -H "Content-Type: application/json" \
  -d '{
    "CID": "2145550142",
    "state": "TX",
    "zipcode": "75201",
    "sipOk": "yes",
    "publisherInboundCallId": "nw-88231",
    "sub_id": "fb-42"
  }'

A bid

200 — a bid, already claimed
{
  "bidId": "7c6b5a49-3827-4615-9048-372615049382",
  "bidAmount": 27.5,
  "expireInSeconds": 58,
  "bidExpireDT": "2026-09-20T14:04:09.000Z",
  "bidExpireEpoch": 1789913049000,
  "phoneNumber": "+18885550100",
  "phoneNumberNoPlus": "18885550100",
  "sipAddress": "sip:7c6b5a4938274615904837261504@sip.example.com",
  "bidTerms": [
    { "code": 101, "message": "Call must connect" },
    { "code": 100, "message": "Minimum connected duration 90 seconds" }
  ],
  "warnings": [],
  "callId": "0b9f6c1e-52a4-4a0e-9f0a-3d1c7e8b2a11",
  "ref": "CA-482137",
  "auctionId": "3f2e1d0c-9b8a-4765-8432-10fedcba9876",
  "postback": {
    "url": "https://api.buy3.io/api/rtb/v1/conversion",
    "key": "b3p_4f1d2c3b4a5e6f70"
  }
}
FieldMeaning
bidIdThe claimed bid. Quote it in any support conversation.
bidAmountWhat this publisher is paid, in dollars as a number. It is never what the buyer pays.
expireInSecondsHow long is left to send the caller.
bidExpireDT, bidExpireEpochThe same moment as an ISO 8601 instant and as epoch milliseconds.
phoneNumber, phoneNumberNoPlusWhere to send the caller, with and without the leading +. Present when there is a number to give.
sipAddressPresent when the ping said sipOk and the platform has a SIP domain. Prefer it: a SIP leg always reaches the platform.
bidTermsWhat the payout depends on, as { code, message }. The codes are in Reject codes.
warningsAdvisory only, as { code, description }. Never changes the outcome. See Warnings.
callId, refBeyond the dialect, and ignored by a client that only knows it: the call this became, and the short reference both consoles show.
auctionIdThe auction's reference. It finds this exact request in either ledger.
postbackA URL and a per-call key for the buyer to report a conversion. Pass them on with the call. See Conversion postback.

A no-bid

200 — a no-bid
{
  "bidId": "3f2e1d0c-9b8a-4765-8432-10fedcba9876",
  "bidAmount": 0,
  "rejectReason": "That state is not being bought (Code: 1008)"
}
  • bidAmount is 0. Read that, not the status — a no-bid is a 200, as the dialect expects.
  • bidId carries the auction's id when there was an auction, so a refusal can still be looked up, and null when the request was refused before one existed.
  • rejectReason is the description and the code in one string: "That state is not being bought (Code: 1008)". Branch on the number in the brackets; the words may be improved at any time.
  • A claim that lost its capacity between the two internal steps comes back as a no-bid too, with code 1000, and a full publisher lane as 1013.

What is not a bid of zero

StatusBodyWhen
400{ "ok": false, "error": "malformed_request" }The body is not valid JSON.
401{ "ok": false, "error": "invalid_key" }The token is unknown, malformed or revoked.
403{ "ok": false, "error": "compat_disabled" }The URL is switched off for that lane.
403publisher_not_live · campaign_not_liveThe account or the campaign is not live.
403{ "ok": false, "error": "ip_not_allowed" }The address is not on the key's allowlist.
409The no-bid shape, (Code: 1025) or (Code: 1003)An identical request is still being scored, or that caller already holds a live bid on this campaign.
429The no-bid shape, (Code: 1024), with Retry-AfterThe key's rate limit. Honour the header.
A bad token, a switched-off URL and an address off the allowlist are errors in the platform's own shape, not prices of zero — a platform that is pinging a dead URL needs to be told so in a way its HTTP client notices.

Retries

Send publisherInboundCallId on every request. It is the publisher's own id for the inbound call and the idempotency key: a retried ping for the same call replays the first answer byte for byte instead of selling one caller twice. Without it, an Idempotency-Key header is honoured in its place.

That matters more here than on the two-step protocol, because one request both prices and claims. A retry without an idempotency key is a second claim on the same person.

Switching it on

  1. 1

    Check the lane has a live routing key

    The URL sits on the key. A lane with none is refused with a sentence saying so; re-issue or rotate one first.

  2. 2

    Turn signatures off on that key

    A platform that pings a bare URL cannot sign a request, so the two cannot both be on. Switching the URL on against a signing key is refused with 409 compat_needs_unsigned_key, and requiring signatures on a key with the URL on is refused with 409 compat_enabled.

  3. 3

    Switch the URL on

    From the campaign's Publishers tab, or with the endpoint below. Switching it on mints the token and answers with the finished URL. A key that was on the older routing surface is widened so it can also answer the auction — never narrowed, so an existing integration keeps working.

    curl
    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 }'
  4. 4

    Send the URL to the publisher

    It is shown in full on the lane and in their own portal, because it is their credential for their own lane.

Switching the URL off forgets the token, so turning it back on gives a new URL. A token that has been in a URL has been in places nobody can enumerate, and "off" should mean the old one is dead for good. Replacing the URL while leaving it on is its own action and takes effect immediately.

Moving a publisher across

For a partner already integrated with another platform, the change is usually one field in their configuration. Their existing parameters map straight across, and the fields they parse out of the answer have the same names.

What they changeWhat stays the same
The ping URL.The parameter names they send, including their own sub-id fields, which arrive here as tags.
Nothing else, in most stacks.The response fields they read: bidId, bidAmount, expireInSeconds, phoneNumber, sipAddress.

Three things to tell them before the first live call:

  1. Only ping with a caller ready. A bid is already a claim, so a speculative ping holds a buyer's slot and produces a call that never arrives.
  2. Transfer from the number they pinged with. When they are given a phoneNumber rather than a SIP address, the call is matched by the caller's own number against the claim, so the caller must arrive from the CID they sent, inside the expiry. Sending sipOk avoids the whole question.
  3. Send publisherInboundCallId. It is what makes a retry safe, and it is the handle that ties their records to the platform's when something has to be looked up.

A worked example

Northwind Media have a dialler already configured against another platform and want to start sending to Acme Health inbound this week.

  1. Their lane already exists and has a live routing key. The key does not require signatures, so nothing blocks the URL.
  2. You switch the URL on from the campaign's Publishers tab and send them the result.
  3. They change one field in their platform's configuration and point a test caller at it. The first request answers bidAmount: 0 with "This campaign requires a caller ID (Code: 1100)" — their test harness was not sending CID.
  4. They fix the mapping and ping again with CID=2145550142, state=TX and publisherInboundCallId=nw-88231. The answer is a bid of 27.50 with 58 seconds to use it, a platform number and a SIP address.
  5. They transfer to the SIP address. The call appears in your call log with their sub-id as a tag, and in the ping ledger as one row on the compat door — with the door named and the token nowhere in it.
  6. Two weeks later they move to the two-step protocol for better control over releases. They already hold the bearer key; you switch the URL off, and the old one stops working that second.

Field-by-field detail is in the Ringba-compatible ping reference.

Next steps