Get an API key
Open an account, then create a key under Dashboard, API keys. Live keys (wmmn_live_sk_) send real traffic and charge your balance. Test keys (wmmn_test_sk_) run the same request through routing and pricing without delivering it, and draw on test credit, which comes with an invite code.
A key restricted to scopes needs dialer:write. You can test any public marketplace route before you buy it, a private route you hold, a route blend, or your own switch with POST /cli-tests/quick.
Every sample reads the key from the environment. Keep it out of source control.
export PACKETEXCHANGE_API_KEY=wmmn_live_sk_...Fund the balance
PacketExchange is prepaid. Top up from $5 by card or crypto, or from $100 by wire, under Dashboard, Billing. An agent can top itself up with x402. There is no contract and no monthly fee.
Keep enough balance for the tests you run. A test that never rang is not charged.
Cost$0.50 per test, charged only if the route rang. GET /cli-tests/quota returns the live price and how many tests you have left this hour.
Make the request
Queue a test with the routeId, the caller ID to present (displayCli) and the route's destination country, then read the result until it is final.
# 1. Queue a test: a real call over your route to a handset in the route's country.
test_id=$(curl -s https://packetexchange.io/api/v1/cli-tests \
-H "Authorization: Bearer $PACKETEXCHANGE_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"routeId\": \"$ROUTE_ID\", \"displayCli\": \"+14155550100\", \"testCountry\": \"United Kingdom\"}" |
jq -r .data.id)
# 2. Read the result until it is final (completed, failed, not_tested or cancelled).
curl -s "https://packetexchange.io/api/v1/cli-tests/$test_id" \
-H "Authorization: Bearer $PACKETEXCHANGE_API_KEY" |
jq '.data | {status, displayCli, reportedCli, displayedCorrectly}'Handle the response
The test starts queued and moves through dispatching and in_progress. A final result says what the handset displayed and whether it matched.
{
"success": true,
"data": {
"id": "<uuid>",
"routeId": "<route id>",
"displayCli": "+14155550100",
"testCountry": "United Kingdom",
"testNumber": "<the handset that received the call>",
"status": "completed",
"reportedCli": "<what the handset displayed>",
"displayedCorrectly": true,
"resultNotes": "<notes or null>",
"completedAt": "<ISO-8601 UTC>"
}
}Values in angle brackets are placeholders for your own ids, times and amounts.
- status
- scheduled, pending, dispatching, in_progress, then completed, failed, not_tested or cancelled.
- reportedCli
- What the handset actually showed, or null.
- displayedCorrectly
- true when it matched displayCli, false when it did not, null when unknown.
- recurrence (request)
- none, daily or weekly, to keep testing a route on a schedule.
When something goes wrong
- Tests are limited per hour; GET /cli-tests/quota shows usedThisHour, limitPerHour and remaining.
- testCountry must be the destination country of the route you test.
- Errors use the standard envelope: { "success": false, "error": { "code", "message", "details" } }.
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input",
"details": [
{ "path": "to", "message": "<why the field was refused>" }
]
}
}