Testing a target

Send one real request at a buyer before live traffic depends on them: the reachability ladder for a phone or SIP destination, and a single real ping for a bidding endpoint with their raw answer and what your paths made of it.

After this guide you will be able to prove a destination is reachable and a bidding endpoint bids, read the rung a SIP ladder stopped at, and tell the two questions apart: whether the buyer answers, and whether the plan would ever send them a call.

A mistyped digit in a destination is the most expensive typo on the platform: every call routed to it fails over, or is lost. Test on a target sends a real request to the buyer, now, on its own — so the first call to reach them is not the experiment.

What a test does

Target typeThe testWhat comes back
numberThe static checks on the destination.Whether it passed, and if not, what was wrong in words.
sipA ladder: the static checks, then DNS, then the TCP or TLS connection, then a SIP OPTIONS probe — stopping at the first rung that fails.One row per rung with its round-trip time, any SIP response code and the detail.
rtbOne real ping, through the same function the router itself calls.The exact request that was sent, the buyer's raw answer, the latency, and what your response paths made of it.
Nothing is dialled in any of them. No call, bid, auction or reservation is written, and no cap counter moves.
  • A test is written to the activity log, so you can see who tested what and when.
  • It does move the target's health counters for a bidding target, because the ping genuinely happened: an endpoint that times out under a test is an endpoint that is down. Six failures in a row will suppress it exactly as live traffic would.
  • The URL is checked again at send time. A bid URL whose name has started resolving to a private address since you saved it is refused with unsafe_url rather than called.

Testing a bidding endpoint

Test on an external RTB target opens a dialog with the details a ping would carry. All of them are optional, and each one exists so you can prove a particular part of the template:

FieldConsole labelWhat it proves
callerIdCaller idThat {{caller.e164}} and its relatives expand the way the buyer expects.
stateStateTwo letters. That your state macro reaches their targeting.
zipZipThat a zip a publisher would have sent arrives where they read it.
tagsTagsThat {{tag.…}} in the body template carries what you think.
campaignIdAs if it arrived onThat the campaign's own route settings apply — its bid ceiling for this target, and its ping budget.
Send a test ping
curl -X POST "https://api.buy3.io/api/tracking/targets/d3e4f5a6-b7c8-4d9e-8f1a-2b3c4d5e6f7a/test" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "callerId": "+14155550142",
  "state": "CA",
  "zip": "94103",
  "tags": {
    "utm_source": "search"
  },
  "campaignId": "6a2d1f0e-8b7c-4c55-a1d3-0e9f8a7b6c5d"
}'
200 — the whole exchange
{
  "test": true,
  "type": "rtb",
  "passed": true,
  "status": "bid",
  "request": {
    "method": "POST",
    "url": "https://rtb.example.com/v2/bid.json",
    "headers": {
      "content-type": "application/json",
      "accept": "application/json",
      "x-partner": "northwind",
      "authorization": "«redacted»"
    },
    "body": {
      "requestId": "test_9c1185a5-c5e9-4fc5-9a3b-2d4e6f8a0b1c",
      "callerId": "+14155550142",
      "callerHash": "9c1185a5c5e9fc54",
      "state": "CA",
      "zip": "94103",
      "vertical": "medicare",
      "campaign": "Medicare enquiries",
      "publisher": "",
      "sipOk": true
    }
  },
  "response": {
    "httpStatus": 200,
    "latencyMs": 312,
    "body": {
      "bidId": "b-88213",
      "bidAmount": 41,
      "phoneNumber": "+18325550144",
      "bidTerms": [
        {
          "callMinDuration": 75
        }
      ]
    }
  },
  "parsed": {
    "status": "bid",
    "rawBidCents": 4100,
    "bidCents": 4100,
    "durationSeconds": 75,
    "destinationKind": "did",
    "destinationSip": null,
    "destinationE164": "+18325550144",
    "rejectReason": null
  },
  "note": "A real request was sent to the buyer. No call, bid or reservation was written."
}

Read it in three parts:

  1. request is what left the building: the method, the fully expanded URL, every header and the body. Authentication headers show their names with «redacted» in place of the value — proving the header is being sent, without handing a buyer's credential back to a browser.
  2. response is what came back, untouched: the HTTP status, the latency in milliseconds and the body. An answer that was not JSON is kept as a snippet of text, because the HTML error page a buyer returned is usually the answer to "why do they never bid".
  3. parsed is what your dot paths made of it: rawBidCents before your limits, bidCents after the floor and ceiling, the duration, and the destination split into its kind and its value. rejectReason carries their own words when they declined.
statuspassedWhat it means
bidtrueThey accepted and named a usable price. This is the only status that passes.
no_bidfalseThey answered and declined — or answered with nothing readable at your price path. Not a fault: check rejectReason.
rejectedfalseThe answer was refused before it became a bid.
timeoutfalseThey did not answer inside the ping time-out.
errorfalseThe request failed, or they returned an error. A redirect counts here: redirects are never followed.

Testing a phone or SIP destination

A phone number has nothing to probe, so its test is the static checks: is there a destination, is it a usable number, is the endpoint in a state that could take a call. A SIP address has a host, so it gets a ladder — and the rungs are in order because each one depends on the one below it.

RunglevelAsks
StaticstaticIs the address there and well formed?
DNSdnsDoes the host resolve — and to a public address?
Connectiontcp_tlsDoes the port accept a connection, and does TLS negotiate?
OPTIONSsip_optionsDoes their equipment answer a SIP OPTIONS probe?
Run the whole ladder by sending no level. Sending one runs that rung alone — useful when you have just changed a certificate and only want tcp_tls.
Run one rung
curl -X POST "https://api.buy3.io/api/tracking/targets/d3e4f5a6-b7c8-4d9e-8f1a-2b3c4d5e6f7a/test" \
  -H "Authorization: Bearer $BUY3_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "level": "sip_options" }'
200 — a ladder that stopped at the third rung
{
  "test": true,
  "type": "sip",
  "passed": false,
  "runs": [
    {
      "id": 1841,
      "level": "static",
      "passed": true,
      "rttMs": null,
      "sipResponseCode": null,
      "httpStatus": null,
      "detail": {
        "problems": []
      },
      "steps": [],
      "createdAt": "2026-09-20T09:41:30.000Z"
    },
    {
      "id": 1842,
      "level": "dns",
      "passed": true,
      "rttMs": 14,
      "sipResponseCode": null,
      "httpStatus": null,
      "detail": {
        "host": "sip.example.com",
        "addresses": [
          "203.0.113.24"
        ],
        "srv": []
      },
      "steps": [],
      "createdAt": "2026-09-20T09:41:30.000Z"
    },
    {
      "id": 1843,
      "level": "tcp_tls",
      "passed": false,
      "rttMs": 5002,
      "sipResponseCode": null,
      "httpStatus": null,
      "detail": {
        "host": "sip.example.com",
        "port": 5061,
        "error": "connect timed out — firewall or IP allowlist"
      },
      "steps": [],
      "createdAt": "2026-09-20T09:41:35.000Z"
    }
  ]
}
  • The ladder stops at the first failure: everything above it would fail for the same reason and would only bury the cause.
  • A full ladder also refreshes the target's health badge, so a green ladder and a red badge cannot sit side by side. A single rung does not — passing static says nothing about reachability.
  • Two further levels, hours and caps, exist for reading settings rather than reaching a buyer, and are deliberately not part of the whole ladder. The console says so: "The hours and caps rungs are never part of the whole ladder."
  • On the OPTIONS rung a 401 or 407 is a pass: it proves the far end is alive and speaking SIP, which is all the rung asks. A 403 means your address is not on their allowlist, and silence usually means a firewall.
  • A SIP host that does not answer OPTIONS at all is not necessarily broken. Plenty of working equipment ignores probes, which is why a failed probe never takes a phone or SIP target out of a plan by itself.

When to test

  1. After creating a target, before you put it on a live routing plan.
  2. After changing a destination, a bid URL, a body template, a response path or a credential.
  3. When the waterfall shows error or timeout against a buyer and you want their response in front of you without waiting for the next caller.
  4. When a buyer says they are not receiving pings. The test's request block is the evidence: the URL, the headers and the body that left, with the latency of their own answer.
  5. After clearing a circuit breaker. Activating a target resets its failure count; a test tells you at once whether it was really fixed.

Testing is a console act: it takes a signed-in owner's or admin's session, and an API key cannot run one. A key that could fire arbitrary requests at arbitrary URLs from inside the platform would be a different kind of object altogether.

Next steps