Skip to content
Markets open

API reference

Purchases

Buy access to marketplace routes and manage what you bought.

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

List your route purchases

GET/api/v1/purchases

Access
API key. Full-access keys only; not reachable by scoped keys.
Rate limit
100 requests per second (the default)

Parameters

NameInTypeDescription
statusquerystring-One of active, paused, cancelled
cursorquerystring (uuid)-
limitqueryinteger-Default 25

Response 200

FieldTypeDescription
datarequiredobject[]-
idrequireddata[].idstring (uuid)-
buyerIdrequireddata[].buyerIdstring (uuid)Your account id
routeIdrequireddata[].routeIdstring (uuid)-
statusrequireddata[].statusstring-One of active, paused, cancelled, pending_review
sipUsernamerequireddata[].sipUsernamestring | null-
createdAtrequireddata[].createdAtstring (date-time)ISO-8601 timestamp (UTC)
cancelledAtrequireddata[].cancelledAtstring (date-time) | nullISO-8601 timestamp (UTC)
agreedPricerequireddata[].agreedPricemoney | nullNegotiated price (from an accepted offer) that overrides the list price USD as a decimal string with exactly 6 places, e.g. "0.012500".
heldOldRaterequireddata[].heldOldRatemoney | nullSet while the purchase is paused by a seller rate increase: the rate you were paying USD as a decimal string with exactly 6 places, e.g. "0.012500".
heldNewRaterequireddata[].heldNewRatemoney | nullThe increased rate waiting for your acceptance USD as a decimal string with exactly 6 places, e.g. "0.012500".
routingPriorityrequireddata[].routingPriorityinteger | nullYour routing position (1 = first); null = unranked
sipPasswordrequireddata[].sipPasswordstring | nullYour own SIP password, for your connection card
heldDeckVersionrequireddata[].heldDeckVersionstring | nullDeck fingerprint to pass as deckVersion when accepting a deck increase
routerequireddata[].routeobject-
typerequireddata[].route.typestring-One of voice, sms
countryrequireddata[].route.countrystring-
prefixrequireddata[].route.prefixstring[]-
countryCoderequireddata[].route.countryCodestring-
destinationNamerequireddata[].route.destinationNamestringThe 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.
pricePerUnitrequireddata[].route.pricePerUnitmoneyUSD as a decimal string with exactly 6 places, e.g. "0.012500".
billingIncrementrequireddata[].route.billingIncrementstring | null-
jingleSipIprequireddata[].route.jingleSipIpenum | nullAlways null: the seller endpoint is never disclosedOne of null
capacityrequireddata[].route.capacityinteger-
expectedAsrrequireddata[].route.expectedAsrstring | null-
expectedAcdrequireddata[].route.expectedAcdstring | null-
dialerCompatiblerequireddata[].route.dialerCompatibleboolean-
rateCountrequireddata[].route.rateCountinteger-
nextCursorrequiredstring | nullPass as cursor to fetch the next page; null on the last page.
hasMorerequiredbooleanTrue when another page exists.

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.

Buy access to a marketplace route

POST/api/v1/purchases

Access
API key. Scoped keys need purchases:write.
Rate limit
100 requests per second (the default)
Moves money
Buys access to a marketplace route.

No upfront cost: usage is billed per minute or message from your balance. Requires a LIVE API key: a test key is refused with 400.

Request body (application/json)

FieldTypeDescription
routeIdrequiredstring (uuid)-

Response 201

FieldTypeDescription
datarequiredobject-
idrequireddata.idstring (uuid)-
buyerIdrequireddata.buyerIdstring (uuid)Your account id
routeIdrequireddata.routeIdstring (uuid)-
statusrequireddata.statusstring-One of active, paused, cancelled, pending_review
sipUsernamerequireddata.sipUsernamestring | null-
createdAtrequireddata.createdAtstring (date-time)ISO-8601 timestamp (UTC)
cancelledAtrequireddata.cancelledAtstring (date-time) | nullISO-8601 timestamp (UTC)
agreedPricerequireddata.agreedPricemoney | nullNegotiated price (from an accepted offer) that overrides the list price USD as a decimal string with exactly 6 places, e.g. "0.012500".
heldOldRaterequireddata.heldOldRatemoney | nullSet while the purchase is paused by a seller rate increase: the rate you were paying USD as a decimal string with exactly 6 places, e.g. "0.012500".
heldNewRaterequireddata.heldNewRatemoney | nullThe increased rate waiting for your acceptance USD as a decimal string with exactly 6 places, e.g. "0.012500".
routingPriorityrequireddata.routingPriorityinteger | nullYour routing position (1 = first); null = unranked
requiresUsCompliancerequireddata.requiresUsCompliancebooleanA US voice route that needs STIR/SHAKEN review before traffic flows
reviewedAtrequireddata.reviewedAtstring (date-time) | nullISO-8601 timestamp (UTC)
reviewNoterequireddata.reviewNotestring | nullThe compliance reviewer's note to you, if any
sipPasswordSetrequireddata.sipPasswordSetbooleanWhether a SIP password exists. The value is not returned here: it is shown once at purchase, on your purchased-routes list and on Interconnections.
smppSystemIdrequireddata.smppSystemIdstring | null-
offerIdrequireddata.offerIdstring (uuid) | nullThe accepted offer that set agreedPrice
routerequireddata.routeobject | null-
idrequireddata.route.idstring (uuid)-
typerequireddata.route.typestring-One of voice, sms
countryrequireddata.route.countrystring-
destinationNamerequireddata.route.destinationNamestringThe 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.
listingRefrequireddata.route.listingRefstringAnonymous short reference of the listing (4 characters, e.g. "7K2Q"): stable for the life of the listing, unique enough to tell two same-named listings apart, and derived from the listing alone (it says nothing about the seller). Absent on your own listings.
pricePerUnitrequireddata.route.pricePerUnitmoneyUSD as a decimal string with exactly 6 places, e.g. "0.012500".
billingIncrementrequireddata.route.billingIncrementstring | null-
jingleSipIprequireddata.route.jingleSipIpenum | null-One of null
sipPublicIprequireddata.sipPublicIpstringWhere to send your SIP traffic
sipPasswordrequireddata.sipPasswordstring | nullPlaintext SIP password, returned once here
pendingCompliancerequireddata.pendingCompliancebooleanTrue when US STIR/SHAKEN compliance review must finish before traffic flows

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.

Get your voice routing order

GET/api/v1/purchases/routing-order

Access
API key. Scoped keys need routes:read.
Rate limit
100 requests per second (the default)

Your SIP credentials and whitelisted IPs identify your ACCOUNT, not a route, so every active voice purchase can carry any call it covers. When several cover a number equally well (same prefix length), your routing order decides, then the cheaper rate, then the older purchase. This lists every voice purchase that can hold a position (active, paused or pending review): ranked ones first by position, then unranked ones oldest first. Destination names are cleaned of seller details. rateSheet routes are priced by an A-Z deck, so they can overlap any destination.

Response 200

FieldTypeDescription
datarequiredobject[]-
purchaseIdrequireddata[].purchaseIdstring (uuid)-
routeIdrequireddata[].routeIdstring (uuid)-
statusrequireddata[].statusstring-One of active, paused, cancelled, pending_review
routingPriorityrequireddata[].routingPriorityinteger | nullYour position (1 = first); null = unranked
createdAtrequireddata[].createdAtstring (date-time)When you bought it (the tie-break among unranked routes)
destinationNamerequireddata[].destinationNamestringThe 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.
countryrequireddata[].countrystring-
countryCoderequireddata[].countryCodestring-
prefixrequireddata[].prefixstring[]-
rateSheetrequireddata[].rateSheetbooleanPriced by an A-Z rate sheet, so it can overlap any destination

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.

Replace your voice routing order

PUT/api/v1/purchases/routing-order

Access
API key. Scoped keys need purchases:write.
Rate limit
100 requests per second (the default)

purchaseIds[0] becomes position 1, the next 2, and so on; every other voice purchase of yours is cleared to unranked. Atomic: a reorder never leaves two routes half-swapped. An empty list clears the whole order (back to cheapest, then oldest). A repeated id is 400 VALIDATION_ERROR; an id that is not one of your rankable voice purchases is 404 and nothing changes.

Request body (application/json)

FieldTypeDescription
purchaseIdsrequiredstring (uuid)[]Your voice purchases in the order to try them; an empty list clears the order

Response 200

FieldTypeDescription
datarequiredobject-
orderrequireddata.orderobject[]-
purchaseIdrequireddata.order[].purchaseIdstring (uuid)-
routingPriorityrequireddata.order[].routingPriorityinteger-

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.

See your first-choice and backup routes per destination

GET/api/v1/purchases/failover

Access
API key. Scoped keys need routes:read.
Rate limit
100 requests per second (the default)

For each prefix your active voice routes list: the route tried first and the backups behind it, in the order a call would use them. When automatic failover is on (enabled: true), a call that a route refuses before it is answered (for example SIP 403, 480, 5xx or no response) is tried straight away on the next route, up to maxAttempts routes, and only the route that connects is billed. A wrong number, a busy or declined call and an unanswered call are never retried. Over a SIP trunk only server errors and congestion (5xx) move a call on; apiOnly marks backups SIP trunk calls never use. singleRoute: true marks a destination with no backup. A-Z rate sheets are listed separately because they have no single prefix. Names are cleaned of seller details. Read-only.

Response 200

FieldTypeDescription
datarequiredobject-
enabledrequireddata.enabledbooleanWhether automatic failover is switched on
maxAttemptsrequireddata.maxAttemptsintegerThe most routes tried for one call, the first included
destinationsrequireddata.destinationsobject[]-
prefixrequireddata.destinations[].prefixstring"+" and the listed prefix
countryrequireddata.destinations[].countrystring-
routesrequireddata.destinations[].routesobject[]-
purchaseIdrequireddata.destinations[].routes[].purchaseIdstring (uuid)-
routeIdrequireddata.destinations[].routes[].routeIdstring (uuid)-
destinationNamerequireddata.destinations[].routes[].destinationNamestringThe 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.
positionrequireddata.destinations[].routes[].positioninteger1 is tried first
rolerequireddata.destinations[].routes[].rolestringnot_tried: beyond maxAttempts, so never reached by one callOne of first, backup, not_tried
apiOnlyrequireddata.destinations[].routes[].apiOnlybooleantrue for a backup that calls over your SIP trunk never use (it needs SIP credentials, call screening or a reshaped number, or the first choice does); Voice API calls still fail over to it
singleRouterequireddata.destinations[].singleRoutebooleanTrue when only one of your routes covers this prefix: no backup
rateSheetsrequireddata.rateSheetsobject[]-
purchaseIdrequireddata.rateSheets[].purchaseIdstring (uuid)-
routeIdrequireddata.rateSheets[].routeIdstring (uuid)-
destinationNamerequireddata.rateSheets[].destinationNamestringThe 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.

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.

See which of your routes would carry a number

GET/api/v1/purchases/route-for

Access
API key. Scoped keys need routes:read.
Rate limit
60 requests per minute

Runs the same ranking the live SIP path uses, for your account and with the same number normalisation, so the answer is what a real call would do. Lists every route of yours that covers the number, in order, with the step of the rule that put each one behind the one above. Also reports the two refusals that sit in front of routing: an embargoed destination and an empty balance; when refused is set, no route carries the call. Read-only.

Parameters

NameInTypeDescription
torequiredquerystringThe number to check, international format

Response 200

FieldTypeDescription
datarequiredobject-
numberrequireddata.numberstringThe number as checked, "+" and digits
carriedByrequireddata.carriedByobject | nullThe route that would carry it (candidates[0]); null when none covers it
positionrequireddata.carriedBy.positioninteger1 = the route that would carry the call
carriesrequireddata.carriedBy.carriesboolean-
purchaseIdrequireddata.carriedBy.purchaseIdstring (uuid) | nullnull for one of your own listings
routeIdrequireddata.carriedBy.routeIdstring (uuid)-
ownRouterequireddata.carriedBy.ownRoutebooleanTrue when this is a route you sell yourself
destinationNamerequireddata.carriedBy.destinationNamestringThe 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.
countryrequireddata.carriedBy.countrystring-
matchedPrefixrequireddata.carriedBy.matchedPrefixstring | null-
matchedDigitsrequireddata.carriedBy.matchedDigitsintegerLength of the matching prefix
routingPriorityrequireddata.carriedBy.routingPriorityinteger | null-
raterequireddata.carriedBy.ratemoney | nullWhat you are billed per minute for this number on this route USD as a decimal string with exactly 6 places, e.g. "0.012500".
rateSheetrequireddata.carriedBy.rateSheetbooleanThe route is priced by an A-Z rate sheet
purchasedAtrequireddata.carriedBy.purchasedAtstring (date-time) | nullISO-8601 timestamp (UTC)
behindBecauserequireddata.carriedBy.behindBecausestring | nullFor every route after the first: why the route above it winsOne of prefix, routing_order, rate, age, route_id
reasonrequireddata.reasonstringWhy that route wins, in words
decidedByrequireddata.decidedBystring | nullThe step that decided between the first two routes; null with fewer than twoOne of prefix, routing_order, rate, age, route_id
candidatesrequireddata.candidatesobject[]Same fields as RouteForCandidate, shown earlier on this page.
refusedrequireddata.refusedobject | null-
reasonrequireddata.refused.reasonstring-One of embargoed, no_balance
messagerequireddata.refused.messagestring-

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.

Set a purchase's routing position

PATCH/api/v1/purchases/{id}/routing-priority

Access
API key. Scoped keys need purchases:write.
Rate limit
100 requests per second (the default)

Sets (priority: n) or clears (priority: null) one voice purchase's position in your routing order; 1 is tried first. Positions do not have to be contiguous, and two purchases may share one (then rate, then age decides). Voice purchases only, and not a cancelled one (400 VALIDATION_ERROR). To reorder several at once use PUT /purchases/routing-order.

Parameters

NameInTypeDescription
idrequiredpathstring (uuid)-

Request body (application/json)

FieldTypeDescription
priorityrequiredinteger | null1 = tried first; null clears the position1 to 9999

Response 200

FieldTypeDescription
datarequiredobject-
idrequireddata.idstring (uuid)-
routingPriorityrequireddata.routingPriorityinteger | 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.
  • 404NOT_FOUND: no such resource on your account.
  • 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.

Clear a purchase's routing position

DELETE/api/v1/purchases/{id}/routing-priority

Access
API key. Scoped keys need purchases:write.
Rate limit
100 requests per second (the default)

The purchase goes back to unranked: after every ranked route, cheapest then oldest.

Parameters

NameInTypeDescription
idrequiredpathstring (uuid)-

Response 200

FieldTypeDescription
datarequiredobject-
idrequireddata.idstring (uuid)-
routingPriorityrequireddata.routingPriorityinteger | 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.
  • 404NOT_FOUND: no such resource on your account.
  • 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.

Get a purchase

GET/api/v1/purchases/{id}

Access
API key. Full-access keys only; not reachable by scoped keys.
Rate limit
100 requests per second (the default)

Parameters

NameInTypeDescription
idrequiredpathstring-

Response 200

FieldTypeDescription
datarequiredobject-
idrequireddata.idstring (uuid)-
buyerIdrequireddata.buyerIdstring (uuid)Your account id
routeIdrequireddata.routeIdstring (uuid)-
statusrequireddata.statusstring-One of active, paused, cancelled, pending_review
sipUsernamerequireddata.sipUsernamestring | null-
createdAtrequireddata.createdAtstring (date-time)ISO-8601 timestamp (UTC)
cancelledAtrequireddata.cancelledAtstring (date-time) | nullISO-8601 timestamp (UTC)
agreedPricerequireddata.agreedPricemoney | nullNegotiated price (from an accepted offer) that overrides the list price USD as a decimal string with exactly 6 places, e.g. "0.012500".
heldOldRaterequireddata.heldOldRatemoney | nullSet while the purchase is paused by a seller rate increase: the rate you were paying USD as a decimal string with exactly 6 places, e.g. "0.012500".
heldNewRaterequireddata.heldNewRatemoney | nullThe increased rate waiting for your acceptance USD as a decimal string with exactly 6 places, e.g. "0.012500".
routingPriorityrequireddata.routingPriorityinteger | nullYour routing position (1 = first); null = unranked
requiresUsCompliancerequireddata.requiresUsCompliancebooleanA US voice route that needs STIR/SHAKEN review before traffic flows
reviewedAtrequireddata.reviewedAtstring (date-time) | nullISO-8601 timestamp (UTC)
reviewNoterequireddata.reviewNotestring | nullThe compliance reviewer's note to you, if any
sipPasswordSetrequireddata.sipPasswordSetbooleanWhether a SIP password exists. The value is not returned here: it is shown once at purchase, on your purchased-routes list and on Interconnections.
smppSystemIdrequireddata.smppSystemIdstring | null-
offerIdrequireddata.offerIdstring (uuid) | nullThe accepted offer that set agreedPrice
routerequireddata.routeobject | null-
idrequireddata.route.idstring (uuid)-
typerequireddata.route.typestring-One of voice, sms
countryrequireddata.route.countrystring-
destinationNamerequireddata.route.destinationNamestringThe 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.
listingRefrequireddata.route.listingRefstringAnonymous short reference of the listing (4 characters, e.g. "7K2Q"): stable for the life of the listing, unique enough to tell two same-named listings apart, and derived from the listing alone (it says nothing about the seller). Absent on your own listings.
pricePerUnitrequireddata.route.pricePerUnitmoneyUSD as a decimal string with exactly 6 places, e.g. "0.012500".
billingIncrementrequireddata.route.billingIncrementstring | null-
jingleSipIprequireddata.route.jingleSipIpenum | null-One of null
sipPublicIprequireddata.sipPublicIpstringWhere to send your SIP traffic

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.
  • 404NOT_FOUND: no such resource on your account.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

Pause or resume a purchase

PATCH/api/v1/purchases/{id}

Access
API key. Full-access keys only; not reachable by scoped keys.
Rate limit
100 requests per second (the default)

Parameters

NameInTypeDescription
idrequiredpathstring-

Request body (application/json)

FieldTypeDescription
statusrequiredstring-One of active, paused

Response 200

FieldTypeDescription
datarequiredobject-
idrequireddata.idstring (uuid)-
buyerIdrequireddata.buyerIdstring (uuid)Your account id
routeIdrequireddata.routeIdstring (uuid)-
statusrequireddata.statusstring-One of active, paused, cancelled, pending_review
sipUsernamerequireddata.sipUsernamestring | null-
createdAtrequireddata.createdAtstring (date-time)ISO-8601 timestamp (UTC)
cancelledAtrequireddata.cancelledAtstring (date-time) | nullISO-8601 timestamp (UTC)
agreedPricerequireddata.agreedPricemoney | nullNegotiated price (from an accepted offer) that overrides the list price USD as a decimal string with exactly 6 places, e.g. "0.012500".
heldOldRaterequireddata.heldOldRatemoney | nullSet while the purchase is paused by a seller rate increase: the rate you were paying USD as a decimal string with exactly 6 places, e.g. "0.012500".
heldNewRaterequireddata.heldNewRatemoney | nullThe increased rate waiting for your acceptance USD as a decimal string with exactly 6 places, e.g. "0.012500".
routingPriorityrequireddata.routingPriorityinteger | nullYour routing position (1 = first); null = unranked
requiresUsCompliancerequireddata.requiresUsCompliancebooleanA US voice route that needs STIR/SHAKEN review before traffic flows
reviewedAtrequireddata.reviewedAtstring (date-time) | nullISO-8601 timestamp (UTC)
reviewNoterequireddata.reviewNotestring | nullThe compliance reviewer's note to you, if any
sipPasswordSetrequireddata.sipPasswordSetbooleanWhether a SIP password exists. The value is not returned here: it is shown once at purchase, on your purchased-routes list and on Interconnections.
smppSystemIdrequireddata.smppSystemIdstring | null-
offerIdrequireddata.offerIdstring (uuid) | nullThe accepted offer that set agreedPrice

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.
  • 404NOT_FOUND: no such resource on your account.
  • 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.

Cancel a purchase

DELETE/api/v1/purchases/{id}

Access
API key. Full-access keys only; not reachable by scoped keys.
Rate limit
100 requests per second (the default)

Parameters

NameInTypeDescription
idrequiredpathstring-

Response 200

FieldTypeDescription
datarequiredobjectSame fields as PurchaseRow, shown earlier on this page.

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.
  • 404NOT_FOUND: no such resource on your account.
  • 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.

Get call counts, ASR and spend for a purchase

GET/api/v1/purchases/{id}/usage

Access
API key. Full-access keys only; not reachable by scoped keys.
Rate limit
100 requests per second (the default)

Parameters

NameInTypeDescription
idrequiredpathstring-

Response 200

FieldTypeDescription
datarequiredobject-
calls24hrequireddata.calls24hinteger-
calls7drequireddata.calls7dinteger-
calls30drequireddata.calls30dinteger-
totalCallsrequireddata.totalCallsinteger-
lastActivityAtrequireddata.lastActivityAtstring (date-time) | nullISO-8601 timestamp (UTC)
spend30ddata.spend30dmoneyAbsent when nothing was spent USD as a decimal string with exactly 6 places, e.g. "0.012500".
measuredAsrdata.measuredAsrnumber-
measuredAsrSessionsdata.measuredAsrSessionsinteger-
measuredAsrPopulationdata.measuredAsrPopulationstring-
measuredAsrWindowdata.measuredAsrWindowstring-
measuredAcddata.measuredAcdnumber-

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.
  • 404NOT_FOUND: no such resource on your account.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.

Accept a seller's rate increase

POST/api/v1/purchases/{id}/accept-rate

Access
API key. Scoped keys need purchases:write.
Rate limit
100 requests per second (the default)
Moves money
Accepts a higher rate on a route you buy.

A purchase paused by a price rise resumes at the new rate. Pass the rate (or deck version) you reviewed so a further change is not accepted by mistake.

Parameters

NameInTypeDescription
idrequiredpathstring-

Request body (application/json)

FieldTypeDescription
acceptedRatestring | numberThe new rate you are agreeing to, as shown to you (guards against a further change in between)
deckVersionstringFor A-Z decks: the heldDeckVersion you reviewed
changeIdstring (uuid)Accept a SCHEDULED change in advance (id from upcoming-rate-changes). Nothing resumes or bills now.

Response 200

FieldTypeDescription
datarequiredobject-
iddata.idstring (uuid)-
buyerIddata.buyerIdstring (uuid)Your account id
routeIddata.routeIdstring (uuid)-
statusdata.statusstring-One of active, paused, cancelled, pending_review
sipUsernamedata.sipUsernamestring | null-
createdAtdata.createdAtstring (date-time)ISO-8601 timestamp (UTC)
cancelledAtdata.cancelledAtstring (date-time) | nullISO-8601 timestamp (UTC)
agreedPricedata.agreedPricemoney | nullNegotiated price (from an accepted offer) that overrides the list price USD as a decimal string with exactly 6 places, e.g. "0.012500".
heldOldRatedata.heldOldRatemoney | nullSet while the purchase is paused by a seller rate increase: the rate you were paying USD as a decimal string with exactly 6 places, e.g. "0.012500".
heldNewRatedata.heldNewRatemoney | nullThe increased rate waiting for your acceptance USD as a decimal string with exactly 6 places, e.g. "0.012500".
routingPrioritydata.routingPriorityinteger | nullYour routing position (1 = first); null = unranked
requiresUsCompliancedata.requiresUsCompliancebooleanA US voice route that needs STIR/SHAKEN review before traffic flows
reviewedAtdata.reviewedAtstring (date-time) | nullISO-8601 timestamp (UTC)
reviewNotedata.reviewNotestring | nullThe compliance reviewer's note to you, if any
sipPasswordSetdata.sipPasswordSetbooleanWhether a SIP password exists. The value is not returned here: it is shown once at purchase, on your purchased-routes list and on Interconnections.
smppSystemIddata.smppSystemIdstring | null-
offerIddata.offerIdstring (uuid) | nullThe accepted offer that set agreedPrice
accepteddata.acceptedboolean-One of true
changeIddata.changeIdstring (uuid)-
purchaseIddata.purchaseIdstring (uuid)-
effectiveDatedata.effectiveDatestring (date-time)ISO-8601 timestamp (UTC)

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.
  • 404NOT_FOUND: no such resource on your account.
  • 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.

List scheduled rate changes on a route you bought

GET/api/v1/purchases/{id}/upcoming-rate-changes

Access
API key. Scoped keys need routes:read.
Rate limit
100 requests per second (the default)

Rate changes the seller has scheduled on a route you bought and that are not yet in effect, soonest first: the effective date, the old and new rate per destination (biggest increases first, at most 200 rows per change; totalRows is the full count), and whether you have already accepted each one. For a per-country listing of an A-Z deck, only that listing's destinations are shown. Accept a change in advance with POST /purchases/{id}/accept-rate and changeId: at the effective date an accepted purchase keeps running instead of being paused for re-consent. affectsYou is false when you do not pay the list rate (an accepted offer or a private price), so a change will not pause you. Destination names are cleaned of seller details.

Parameters

NameInTypeDescription
idrequiredpathstring (uuid)-

Response 200

FieldTypeDescription
datarequiredobject-
purchaseIdrequireddata.purchaseIdstring (uuid)-
affectsYourequireddata.affectsYoubooleanTrue when you pay the list rate, so this change would pause you until accepted
changesrequireddata.changesobject[]-
changeIdrequireddata.changes[].changeIdstring (uuid)Pass as changeId to POST /purchases/{id}/accept-rate
kindrequireddata.changes[].kindstringflat = the route's single price changes; deck = rate-sheet rows changeOne of flat, deck
effectiveDaterequireddata.changes[].effectiveDatestring (date-time)ISO-8601 timestamp (UTC)
increasesrequireddata.changes[].increasesinteger-
decreasesrequireddata.changes[].decreasesinteger-
addedrequireddata.changes[].addedinteger-
removedrequireddata.changes[].removedinteger-
largestIncreasePctrequireddata.changes[].largestIncreasePctnumber | null-
acceptedrequireddata.changes[].acceptedboolean-
acceptedAtrequireddata.changes[].acceptedAtstring (date-time) | nullISO-8601 timestamp (UTC)
rowsrequireddata.changes[].rowsobject[]-
destinationrequireddata.changes[].rows[].destinationstring | null-
prefixrequireddata.changes[].rows[].prefixstring | null-
oldRaterequireddata.changes[].rows[].oldRatemoney | 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".
newRaterequireddata.changes[].rows[].newRatemoney | 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".
oldIncrementrequireddata.changes[].rows[].oldIncrementstring | null-
newIncrementrequireddata.changes[].rows[].newIncrementstring | null-
deltaPctrequireddata.changes[].rows[].deltaPctnumber | null-
removedrequireddata.changes[].rows[].removedboolean-
totalRowsrequireddata.changes[].totalRowsintegerRows in the change for this listing (rows is capped at 200)

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.
  • 404NOT_FOUND: no such resource on your account.
  • 429RATE_LIMITED: slow down and retry after the Retry-After seconds.
  • 500INTERNAL_ERROR: unexpected failure. Quote X-Request-Id to support.