Skip to content
Markets open

API reference

Onboarding

The guided first-call flow for new accounts.

4 operationsBase URL https://packetexchange.io/api/v1Postman collection

Get everything the first-call guide needs

GET/api/v1/first-call/state

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

FieldTypeDescription
datarequiredobject-
balancerequireddata.balancemoneyUSD as a decimal string with exactly 6 places, e.g. "0.012500".
balanceNumericrequireddata.balanceNumericnumber-
emailVerifiedrequireddata.emailVerifiedboolean-
accountStatusrequireddata.accountStatusstring | null-
activePurchasesrequireddata.activePurchasesobject[]-
purchaseIdrequireddata.activePurchases[].purchaseIdstring (uuid)-
routeIdrequireddata.activePurchases[].routeIdstring (uuid)-
routeNamerequireddata.activePurchases[].routeNamestring | nullThe 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[].countryCodestring | null-
prefixesrequireddata.activePurchases[].prefixesstring | nullComma-joined prefixes
typerequireddata.activePurchases[].typestring-
statusrequireddata.activePurchases[].statusstring-
billingIncrementrequireddata.activePurchases[].billingIncrementstring | nulle.g. "60/60"
sipUsernamerequireddata.activePurchases[].sipUsernamestring | null-
hasSipPasswordrequireddata.activePurchases[].hasSipPasswordboolean-
sendTorequireddata.activePurchases[].sendTostring | nullOur address to send this traffic to, from the live brief
sendToPortrequireddata.activePurchases[].sendToPortinteger | null-
recentCallsrequireddata.recentCallsobject[]-
idrequireddata.recentCalls[].idstring (uuid)-
createdAtrequireddata.recentCalls[].createdAtstring (date-time)ISO-8601 timestamp (UTC)
directionrequireddata.recentCalls[].directionstring-
kindrequireddata.recentCalls[].kindstring-
toNumberrequireddata.recentCalls[].toNumberstring | null-
fromNumberrequireddata.recentCalls[].fromNumberstring | null-
statusrequireddata.recentCalls[].statusstring | null-
sipCoderequireddata.recentCalls[].sipCodeinteger | null-
hangupCauserequireddata.recentCalls[].hangupCausestring | null-
durationSecondsrequireddata.recentCalls[].durationSecondsinteger | null-
billedSecondsrequireddata.recentCalls[].billedSecondsinteger | nullSeconds the charge was computed from, after increment rounding
buyerCostrequireddata.recentCalls[].buyerCostmoney | nullUS 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[].ratePerUnitmoney | nullUS 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[].isTestboolean-
hasAnyCallrequireddata.hasAnyCallboolean-
briefrequireddata.briefobject | nullThe account connectivity brief: authorised source addresses, purchased routes and where to send them
briefErrorrequireddata.briefErrorstring | null-

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

Check first-call progress

GET/api/v1/first-call/status

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

FieldTypeDescription
datarequiredobject-
hasAnyCallrequireddata.hasAnyCallboolean-
hasActivePurchaserequireddata.hasActivePurchaseboolean-
hasBalancerequireddata.hasBalanceboolean-

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

Activate your own switch address

POST/api/v1/first-call/switch-address

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)

FieldTypeDescription
confirmboolean-

Response 200

FieldTypeDescription
datarequiredobjectWhat was (or would be) changed

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 409CONFLICT (or a code-specific 409): the change clashes with existing state.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

Check whether a call to a destination would be accepted

POST/api/v1/first-call/readiness

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)

FieldTypeDescription
destinationrequiredstringDestination number in international formatmax 24 chars

Response 200

FieldTypeDescription
datarequiredobject-
checkablerequireddata.checkableboolean-
acceptedrequireddata.acceptedboolean-
identityCheckedrequireddata.identityCheckedstring | null-
identitiesTriedrequireddata.identitiesTriedstring[]-
rejectionrequireddata.rejectionobject | null-
coderequireddata.rejection.codeintegerStable PacketExchange cause code (not the SIP code)
blockedByrequireddata.rejection.blockedBystringWhose move it is to unblock the call
reasonrequireddata.rejection.reasonstring-
remedyrequireddata.rejection.remedystring-
sipCoderequireddata.rejection.sipCodeinteger-
noterequireddata.notestring | null-

Errors

  • 400VALIDATION_ERROR, INVALID_INPUT or BAD_REQUEST. For VALIDATION_ERROR, error.details is an array of { path, message }.
  • 401UNAUTHORIZED: missing, invalid, expired or revoked credential.
  • 403FORBIDDEN: not allowed - a scoped key lacks the scope, or the endpoint is session-only.
  • 409CONFLICT (or a code-specific 409): the change clashes with existing state.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.