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 type | The test | What comes back |
|---|---|---|
number | The static checks on the destination. | Whether it passed, and if not, what was wrong in words. |
sip | A 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. |
rtb | One 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. |
- 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_urlrather 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:
| Field | Console label | What it proves |
|---|---|---|
callerId | Caller id | That {{caller.e164}} and its relatives expand the way the buyer expects. |
state | State | Two letters. That your state macro reaches their targeting. |
zip | Zip | That a zip a publisher would have sent arrives where they read it. |
tags | Tags | That {{tag.…}} in the body template carries what you think. |
campaignId | As if it arrived on | That the campaign's own route settings apply — its bid ceiling for this target, and its ping budget. |
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"
}'{
"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:
requestis 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.responseis 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".parsedis what your dot paths made of it:rawBidCentsbefore your limits,bidCentsafter the floor and ceiling, the duration, and the destination split into its kind and its value.rejectReasoncarries their own words when they declined.
status | passed | What it means |
|---|---|---|
bid | true | They accepted and named a usable price. This is the only status that passes. |
no_bid | false | They answered and declined — or answered with nothing readable at your price path. Not a fault: check rejectReason. |
rejected | false | The answer was refused before it became a bid. |
timeout | false | They did not answer inside the ping time-out. |
error | false | The 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.
| Rung | level | Asks |
|---|---|---|
| Static | static | Is the address there and well formed? |
| DNS | dns | Does the host resolve — and to a public address? |
| Connection | tcp_tls | Does the port accept a connection, and does TLS negotiate? |
| OPTIONS | sip_options | Does their equipment answer a SIP OPTIONS probe? |
level. Sending one runs that rung alone — useful when you have just changed a certificate and only want tcp_tls.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" }'{
"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
staticsays nothing about reachability. - Two further levels,
hoursandcaps, 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
OPTIONSrung a401or407is a pass: it proves the far end is alive and speaking SIP, which is all the rung asks. A403means your address is not on their allowlist, and silence usually means a firewall. - A SIP host that does not answer
OPTIONSat 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
- After creating a target, before you put it on a live routing plan.
- After changing a destination, a bid URL, a body template, a response path or a credential.
- When the waterfall shows
errorortimeoutagainst a buyer and you want their response in front of you without waiting for the next caller. - When a buyer says they are not receiving pings. The test's
requestblock is the evidence: the URL, the headers and the body that left, with the latency of their own answer. - 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.
