API reference
Onboarding
The guided first-call flow for new accounts.
4 operationsBase URL https://packetexchange.io/api/v1Postman collection
BrowseOnboarding
Get everything the first-call guide needs
GET/
- Access
- API key. Scoped keys need
account:read. - Rate limit
- 100 requests per second (the default)
Balance, verification, active purchases (with where to send traffic and the billing increment), the five most recent calls and the account connectivity brief. If the brief cannot be read, brief is null and briefError says why; no fallback address is ever guessed.
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
balancerequireddata.balance | money | USD as a decimal string with exactly 6 places, e.g. "0.012500". |
balanceNumericrequireddata.balanceNumeric | number | - |
emailVerifiedrequireddata.emailVerified | boolean | - |
accountStatusrequireddata.accountStatus | string | null | - |
activePurchasesrequireddata.activePurchases | object[] | - |
purchaseIdrequireddata.activePurchases[].purchaseId | string (uuid) | - |
routeIdrequireddata.activePurchases[].routeId | string (uuid) | - |
routeNamerequireddata.activePurchases[].routeName | string | null | The listing name as a buyer reads it: generated by the platform, never the seller's text, and ending in the listing reference (e.g. "Niger Mobile · Ref 7K2Q", "A-Z Voice · Premium · Full CLI · Ref 3MX9"). Your own listings keep the name you gave them. |
countryCoderequireddata.activePurchases[].countryCode | string | null | - |
prefixesrequireddata.activePurchases[].prefixes | string | null | Comma-joined prefixes |
typerequireddata.activePurchases[].type | string | - |
statusrequireddata.activePurchases[].status | string | - |
billingIncrementrequireddata.activePurchases[].billingIncrement | string | null | e.g. "60/60" |
sipUsernamerequireddata.activePurchases[].sipUsername | string | null | - |
hasSipPasswordrequireddata.activePurchases[].hasSipPassword | boolean | - |
sendTorequireddata.activePurchases[].sendTo | string | null | Our address to send this traffic to, from the live brief |
sendToPortrequireddata.activePurchases[].sendToPort | integer | null | - |
recentCallsrequireddata.recentCalls | object[] | - |
idrequireddata.recentCalls[].id | string (uuid) | - |
createdAtrequireddata.recentCalls[].createdAt | string (date-time) | ISO-8601 timestamp (UTC) |
directionrequireddata.recentCalls[].direction | string | - |
kindrequireddata.recentCalls[].kind | string | - |
toNumberrequireddata.recentCalls[].toNumber | string | null | - |
fromNumberrequireddata.recentCalls[].fromNumber | string | null | - |
statusrequireddata.recentCalls[].status | string | null | - |
sipCoderequireddata.recentCalls[].sipCode | integer | null | - |
hangupCauserequireddata.recentCalls[].hangupCause | string | null | - |
durationSecondsrequireddata.recentCalls[].durationSeconds | integer | null | - |
billedSecondsrequireddata.recentCalls[].billedSeconds | integer | null | Seconds the charge was computed from, after increment rounding |
buyerCostrequireddata.recentCalls[].buyerCost | money | null | US dollars as a decimal string with exactly 6 decimal places, e.g. "0.012500". Do money arithmetic with a decimal type, not floating point. USD as a decimal string with exactly 6 places, e.g. "0.012500". |
ratePerUnitrequireddata.recentCalls[].ratePerUnit | money | null | US dollars as a decimal string with exactly 6 decimal places, e.g. "0.012500". Do money arithmetic with a decimal type, not floating point. USD as a decimal string with exactly 6 places, e.g. "0.012500". |
isTestrequireddata.recentCalls[].isTest | boolean | - |
hasAnyCallrequireddata.hasAnyCall | boolean | - |
briefrequireddata.brief | object | null | The account connectivity brief: authorised source addresses, purchased routes and where to send them |
briefErrorrequireddata.briefError | string | null | - |
Errors
- 400
VALIDATION_ERROR,INVALID_INPUTorBAD_REQUEST. ForVALIDATION_ERROR,error.detailsis an array of{ path, message }. - 401
UNAUTHORIZED: missing, invalid, expired or revoked credential. - 403
FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only. - 429
RATE_LIMITED: slow down and retry after theRetry-Afterseconds. - 500
INTERNAL_ERROR: unexpected failure. QuoteX-Request-Idto support.
Check first-call progress
GET/
- Access
- API key. Scoped keys need
account:read. - Rate limit
- 100 requests per second (the default)
Three cheap yes/no checks for a progress badge: any call made, any active purchase, a positive balance.
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
hasAnyCallrequireddata.hasAnyCall | boolean | - |
hasActivePurchaserequireddata.hasActivePurchase | boolean | - |
hasBalancerequireddata.hasBalance | boolean | - |
Errors
- 400
VALIDATION_ERROR,INVALID_INPUTorBAD_REQUEST. ForVALIDATION_ERROR,error.detailsis an array of{ path, message }. - 401
UNAUTHORIZED: missing, invalid, expired or revoked credential. - 403
FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only. - 429
RATE_LIMITED: slow down and retry after theRetry-Afterseconds. - 500
INTERNAL_ERROR: unexpected failure. QuoteX-Request-Idto support.
Activate your own switch address
POST/
- Access
- API key. Scoped keys need
account:write. - Rate limit
- 10 requests per minute
Moves the account onto its own ingress address. Call without confirm to preview what changes; send confirm: true to apply.
Request body (application/json)
| Field | Type | Description |
|---|---|---|
confirm | boolean | - |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | What was (or would be) changed |
Errors
- 400
VALIDATION_ERROR,INVALID_INPUTorBAD_REQUEST. ForVALIDATION_ERROR,error.detailsis an array of{ path, message }. - 401
UNAUTHORIZED: missing, invalid, expired or revoked credential. - 403
FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only. - 409
CONFLICT(or a code-specific 409): the change clashes with existing state. - 429
RATE_LIMITED: slow down and retry after theRetry-Afterseconds. - 500
INTERNAL_ERROR: unexpected failure. QuoteX-Request-Idto support.
Check whether a call to a destination would be accepted
POST/
- Access
- API key. Scoped keys need
account:read. - Rate limit
- 20 requests per minute
Dry-runs inbound admission for a destination against each way your traffic can be recognised (source address or SIP credentials). Places no call and moves no money. When refused, rejection explains why and whose move it is.
Request body (application/json)
| Field | Type | Description |
|---|---|---|
destinationrequired | string | Destination number in international formatmax 24 chars |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
checkablerequireddata.checkable | boolean | - |
acceptedrequireddata.accepted | boolean | - |
identityCheckedrequireddata.identityChecked | string | null | - |
identitiesTriedrequireddata.identitiesTried | string[] | - |
rejectionrequireddata.rejection | object | null | - |
coderequireddata.rejection.code | integer | Stable PacketExchange cause code (not the SIP code) |
blockedByrequireddata.rejection.blockedBy | string | Whose move it is to unblock the call |
reasonrequireddata.rejection.reason | string | - |
remedyrequireddata.rejection.remedy | string | - |
sipCoderequireddata.rejection.sipCode | integer | - |
noterequireddata.note | string | null | - |
Errors
- 400
VALIDATION_ERROR,INVALID_INPUTorBAD_REQUEST. ForVALIDATION_ERROR,error.detailsis an array of{ path, message }. - 401
UNAUTHORIZED: missing, invalid, expired or revoked credential. - 403
FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only. - 409
CONFLICT(or a code-specific 409): the change clashes with existing state. - 429
RATE_LIMITED: slow down and retry after theRetry-Afterseconds. - 500
INTERNAL_ERROR: unexpected failure. QuoteX-Request-Idto support.