API reference
Purchases
Buy access to marketplace routes and manage what you bought.
14 operationsBase URL https://packetexchange.io/api/v1Postman collection
BrowsePurchases
List your route purchases
GET/
- Access
- API key. Full-access keys only; not reachable by scoped keys.
- Rate limit
- 100 requests per second (the default)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
status | query | string | -One of active, paused, cancelled |
cursor | query | string (uuid) | - |
limit | query | integer | -Default 25 |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object[] | - |
idrequireddata[].id | string (uuid) | - |
buyerIdrequireddata[].buyerId | string (uuid) | Your account id |
routeIdrequireddata[].routeId | string (uuid) | - |
statusrequireddata[].status | string | -One of active, paused, cancelled, pending_review |
sipUsernamerequireddata[].sipUsername | string | null | - |
createdAtrequireddata[].createdAt | string (date-time) | ISO-8601 timestamp (UTC) |
cancelledAtrequireddata[].cancelledAt | string (date-time) | null | ISO-8601 timestamp (UTC) |
agreedPricerequireddata[].agreedPrice | money | null | Negotiated price (from an accepted offer) that overrides the list price USD as a decimal string with exactly 6 places, e.g. "0.012500". |
heldOldRaterequireddata[].heldOldRate | money | null | Set 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[].heldNewRate | money | null | The increased rate waiting for your acceptance USD as a decimal string with exactly 6 places, e.g. "0.012500". |
routingPriorityrequireddata[].routingPriority | integer | null | Your routing position (1 = first); null = unranked |
sipPasswordrequireddata[].sipPassword | string | null | Your own SIP password, for your connection card |
heldDeckVersionrequireddata[].heldDeckVersion | string | null | Deck fingerprint to pass as deckVersion when accepting a deck increase |
routerequireddata[].route | object | - |
typerequireddata[].route.type | string | -One of voice, sms |
countryrequireddata[].route.country | string | - |
prefixrequireddata[].route.prefix | string[] | - |
countryCoderequireddata[].route.countryCode | string | - |
destinationNamerequireddata[].route.destinationName | string | 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. |
pricePerUnitrequireddata[].route.pricePerUnit | money | USD as a decimal string with exactly 6 places, e.g. "0.012500". |
billingIncrementrequireddata[].route.billingIncrement | string | null | - |
jingleSipIprequireddata[].route.jingleSipIp | enum | null | Always null: the seller endpoint is never disclosedOne of null |
capacityrequireddata[].route.capacity | integer | - |
expectedAsrrequireddata[].route.expectedAsr | string | null | - |
expectedAcdrequireddata[].route.expectedAcd | string | null | - |
dialerCompatiblerequireddata[].route.dialerCompatible | boolean | - |
rateCountrequireddata[].route.rateCount | integer | - |
nextCursorrequired | string | null | Pass as cursor to fetch the next page; null on the last page. |
hasMorerequired | boolean | True when another page exists. |
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.
Buy access to a marketplace route
POST/
- 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)
| Field | Type | Description |
|---|---|---|
routeIdrequired | string (uuid) | - |
Response 201
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
idrequireddata.id | string (uuid) | - |
buyerIdrequireddata.buyerId | string (uuid) | Your account id |
routeIdrequireddata.routeId | string (uuid) | - |
statusrequireddata.status | string | -One of active, paused, cancelled, pending_review |
sipUsernamerequireddata.sipUsername | string | null | - |
createdAtrequireddata.createdAt | string (date-time) | ISO-8601 timestamp (UTC) |
cancelledAtrequireddata.cancelledAt | string (date-time) | null | ISO-8601 timestamp (UTC) |
agreedPricerequireddata.agreedPrice | money | null | Negotiated price (from an accepted offer) that overrides the list price USD as a decimal string with exactly 6 places, e.g. "0.012500". |
heldOldRaterequireddata.heldOldRate | money | null | Set 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.heldNewRate | money | null | The increased rate waiting for your acceptance USD as a decimal string with exactly 6 places, e.g. "0.012500". |
routingPriorityrequireddata.routingPriority | integer | null | Your routing position (1 = first); null = unranked |
requiresUsCompliancerequireddata.requiresUsCompliance | boolean | A US voice route that needs STIR/SHAKEN review before traffic flows |
reviewedAtrequireddata.reviewedAt | string (date-time) | null | ISO-8601 timestamp (UTC) |
reviewNoterequireddata.reviewNote | string | null | The compliance reviewer's note to you, if any |
sipPasswordSetrequireddata.sipPasswordSet | boolean | Whether 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.smppSystemId | string | null | - |
offerIdrequireddata.offerId | string (uuid) | null | The accepted offer that set agreedPrice |
routerequireddata.route | object | null | - |
idrequireddata.route.id | string (uuid) | - |
typerequireddata.route.type | string | -One of voice, sms |
countryrequireddata.route.country | string | - |
destinationNamerequireddata.route.destinationName | string | 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. |
listingRefrequireddata.route.listingRef | string | Anonymous 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.pricePerUnit | money | USD as a decimal string with exactly 6 places, e.g. "0.012500". |
billingIncrementrequireddata.route.billingIncrement | string | null | - |
jingleSipIprequireddata.route.jingleSipIp | enum | null | -One of null |
sipPublicIprequireddata.sipPublicIp | string | Where to send your SIP traffic |
sipPasswordrequireddata.sipPassword | string | null | Plaintext SIP password, returned once here |
pendingCompliancerequireddata.pendingCompliance | boolean | True when US STIR/SHAKEN compliance review must finish before traffic flows |
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.
Get your voice routing order
GET/
- 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
| Field | Type | Description |
|---|---|---|
datarequired | object[] | - |
purchaseIdrequireddata[].purchaseId | string (uuid) | - |
routeIdrequireddata[].routeId | string (uuid) | - |
statusrequireddata[].status | string | -One of active, paused, cancelled, pending_review |
routingPriorityrequireddata[].routingPriority | integer | null | Your position (1 = first); null = unranked |
createdAtrequireddata[].createdAt | string (date-time) | When you bought it (the tie-break among unranked routes) |
destinationNamerequireddata[].destinationName | string | 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. |
countryrequireddata[].country | string | - |
countryCoderequireddata[].countryCode | string | - |
prefixrequireddata[].prefix | string[] | - |
rateSheetrequireddata[].rateSheet | boolean | Priced by an A-Z rate sheet, so it can overlap any destination |
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.
Replace your voice routing order
PUT/
- 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)
| Field | Type | Description |
|---|---|---|
purchaseIdsrequired | string (uuid)[] | Your voice purchases in the order to try them; an empty list clears the order |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
orderrequireddata.order | object[] | - |
purchaseIdrequireddata.order[].purchaseId | string (uuid) | - |
routingPriorityrequireddata.order[].routingPriority | integer | - |
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.
See your first-choice and backup routes per destination
GET/
- 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
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
enabledrequireddata.enabled | boolean | Whether automatic failover is switched on |
maxAttemptsrequireddata.maxAttempts | integer | The most routes tried for one call, the first included |
destinationsrequireddata.destinations | object[] | - |
prefixrequireddata.destinations[].prefix | string | "+" and the listed prefix |
countryrequireddata.destinations[].country | string | - |
routesrequireddata.destinations[].routes | object[] | - |
purchaseIdrequireddata.destinations[].routes[].purchaseId | string (uuid) | - |
routeIdrequireddata.destinations[].routes[].routeId | string (uuid) | - |
destinationNamerequireddata.destinations[].routes[].destinationName | string | 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. |
positionrequireddata.destinations[].routes[].position | integer | 1 is tried first |
rolerequireddata.destinations[].routes[].role | string | not_tried: beyond maxAttempts, so never reached by one callOne of first, backup, not_tried |
apiOnlyrequireddata.destinations[].routes[].apiOnly | boolean | true 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[].singleRoute | boolean | True when only one of your routes covers this prefix: no backup |
rateSheetsrequireddata.rateSheets | object[] | - |
purchaseIdrequireddata.rateSheets[].purchaseId | string (uuid) | - |
routeIdrequireddata.rateSheets[].routeId | string (uuid) | - |
destinationNamerequireddata.rateSheets[].destinationName | string | 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. |
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.
See which of your routes would carry a number
GET/
- 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
| Name | In | Type | Description |
|---|---|---|---|
torequired | query | string | The number to check, international format |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
numberrequireddata.number | string | The number as checked, "+" and digits |
carriedByrequireddata.carriedBy | object | null | The route that would carry it (candidates[0]); null when none covers it |
positionrequireddata.carriedBy.position | integer | 1 = the route that would carry the call |
carriesrequireddata.carriedBy.carries | boolean | - |
purchaseIdrequireddata.carriedBy.purchaseId | string (uuid) | null | null for one of your own listings |
routeIdrequireddata.carriedBy.routeId | string (uuid) | - |
ownRouterequireddata.carriedBy.ownRoute | boolean | True when this is a route you sell yourself |
destinationNamerequireddata.carriedBy.destinationName | string | 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. |
countryrequireddata.carriedBy.country | string | - |
matchedPrefixrequireddata.carriedBy.matchedPrefix | string | null | - |
matchedDigitsrequireddata.carriedBy.matchedDigits | integer | Length of the matching prefix |
routingPriorityrequireddata.carriedBy.routingPriority | integer | null | - |
raterequireddata.carriedBy.rate | money | null | What 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.rateSheet | boolean | The route is priced by an A-Z rate sheet |
purchasedAtrequireddata.carriedBy.purchasedAt | string (date-time) | null | ISO-8601 timestamp (UTC) |
behindBecauserequireddata.carriedBy.behindBecause | string | null | For every route after the first: why the route above it winsOne of prefix, routing_order, rate, age, route_id |
reasonrequireddata.reason | string | Why that route wins, in words |
decidedByrequireddata.decidedBy | string | null | The step that decided between the first two routes; null with fewer than twoOne of prefix, routing_order, rate, age, route_id |
candidatesrequireddata.candidates | object[] | Same fields as RouteForCandidate, shown earlier on this page. |
refusedrequireddata.refused | object | null | - |
reasonrequireddata.refused.reason | string | -One of embargoed, no_balance |
messagerequireddata.refused.message | string | - |
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.
Set a purchase's routing position
PATCH/
- 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
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) | - |
Request body (application/json)
| Field | Type | Description |
|---|---|---|
priorityrequired | integer | null | 1 = tried first; null clears the position1 to 9999 |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
idrequireddata.id | string (uuid) | - |
routingPriorityrequireddata.routingPriority | integer | 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. - 404
NOT_FOUND: no such resource on your account. - 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.
Clear a purchase's routing position
DELETE/
- 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
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) | - |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
idrequireddata.id | string (uuid) | - |
routingPriorityrequireddata.routingPriority | integer | 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. - 404
NOT_FOUND: no such resource on your account. - 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.
Get a purchase
GET/
- Access
- API key. Full-access keys only; not reachable by scoped keys.
- Rate limit
- 100 requests per second (the default)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string | - |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
idrequireddata.id | string (uuid) | - |
buyerIdrequireddata.buyerId | string (uuid) | Your account id |
routeIdrequireddata.routeId | string (uuid) | - |
statusrequireddata.status | string | -One of active, paused, cancelled, pending_review |
sipUsernamerequireddata.sipUsername | string | null | - |
createdAtrequireddata.createdAt | string (date-time) | ISO-8601 timestamp (UTC) |
cancelledAtrequireddata.cancelledAt | string (date-time) | null | ISO-8601 timestamp (UTC) |
agreedPricerequireddata.agreedPrice | money | null | Negotiated price (from an accepted offer) that overrides the list price USD as a decimal string with exactly 6 places, e.g. "0.012500". |
heldOldRaterequireddata.heldOldRate | money | null | Set 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.heldNewRate | money | null | The increased rate waiting for your acceptance USD as a decimal string with exactly 6 places, e.g. "0.012500". |
routingPriorityrequireddata.routingPriority | integer | null | Your routing position (1 = first); null = unranked |
requiresUsCompliancerequireddata.requiresUsCompliance | boolean | A US voice route that needs STIR/SHAKEN review before traffic flows |
reviewedAtrequireddata.reviewedAt | string (date-time) | null | ISO-8601 timestamp (UTC) |
reviewNoterequireddata.reviewNote | string | null | The compliance reviewer's note to you, if any |
sipPasswordSetrequireddata.sipPasswordSet | boolean | Whether 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.smppSystemId | string | null | - |
offerIdrequireddata.offerId | string (uuid) | null | The accepted offer that set agreedPrice |
routerequireddata.route | object | null | - |
idrequireddata.route.id | string (uuid) | - |
typerequireddata.route.type | string | -One of voice, sms |
countryrequireddata.route.country | string | - |
destinationNamerequireddata.route.destinationName | string | 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. |
listingRefrequireddata.route.listingRef | string | Anonymous 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.pricePerUnit | money | USD as a decimal string with exactly 6 places, e.g. "0.012500". |
billingIncrementrequireddata.route.billingIncrement | string | null | - |
jingleSipIprequireddata.route.jingleSipIp | enum | null | -One of null |
sipPublicIprequireddata.sipPublicIp | string | Where to send your SIP traffic |
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. - 404
NOT_FOUND: no such resource on your account. - 429
RATE_LIMITED: slow down and retry after theRetry-Afterseconds. - 500
INTERNAL_ERROR: unexpected failure. QuoteX-Request-Idto support.
Pause or resume a purchase
PATCH/
- Access
- API key. Full-access keys only; not reachable by scoped keys.
- Rate limit
- 100 requests per second (the default)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string | - |
Request body (application/json)
| Field | Type | Description |
|---|---|---|
statusrequired | string | -One of active, paused |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
idrequireddata.id | string (uuid) | - |
buyerIdrequireddata.buyerId | string (uuid) | Your account id |
routeIdrequireddata.routeId | string (uuid) | - |
statusrequireddata.status | string | -One of active, paused, cancelled, pending_review |
sipUsernamerequireddata.sipUsername | string | null | - |
createdAtrequireddata.createdAt | string (date-time) | ISO-8601 timestamp (UTC) |
cancelledAtrequireddata.cancelledAt | string (date-time) | null | ISO-8601 timestamp (UTC) |
agreedPricerequireddata.agreedPrice | money | null | Negotiated price (from an accepted offer) that overrides the list price USD as a decimal string with exactly 6 places, e.g. "0.012500". |
heldOldRaterequireddata.heldOldRate | money | null | Set 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.heldNewRate | money | null | The increased rate waiting for your acceptance USD as a decimal string with exactly 6 places, e.g. "0.012500". |
routingPriorityrequireddata.routingPriority | integer | null | Your routing position (1 = first); null = unranked |
requiresUsCompliancerequireddata.requiresUsCompliance | boolean | A US voice route that needs STIR/SHAKEN review before traffic flows |
reviewedAtrequireddata.reviewedAt | string (date-time) | null | ISO-8601 timestamp (UTC) |
reviewNoterequireddata.reviewNote | string | null | The compliance reviewer's note to you, if any |
sipPasswordSetrequireddata.sipPasswordSet | boolean | Whether 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.smppSystemId | string | null | - |
offerIdrequireddata.offerId | string (uuid) | null | The accepted offer that set agreedPrice |
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. - 404
NOT_FOUND: no such resource on your account. - 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.
Cancel a purchase
DELETE/
- Access
- API key. Full-access keys only; not reachable by scoped keys.
- Rate limit
- 100 requests per second (the default)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string | - |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | Same fields as PurchaseRow, shown earlier on this page. |
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. - 404
NOT_FOUND: no such resource on your account. - 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.
Get call counts, ASR and spend for a purchase
GET/
- Access
- API key. Full-access keys only; not reachable by scoped keys.
- Rate limit
- 100 requests per second (the default)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string | - |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
calls24hrequireddata.calls24h | integer | - |
calls7drequireddata.calls7d | integer | - |
calls30drequireddata.calls30d | integer | - |
totalCallsrequireddata.totalCalls | integer | - |
lastActivityAtrequireddata.lastActivityAt | string (date-time) | null | ISO-8601 timestamp (UTC) |
spend30ddata.spend30d | money | Absent when nothing was spent USD as a decimal string with exactly 6 places, e.g. "0.012500". |
measuredAsrdata.measuredAsr | number | - |
measuredAsrSessionsdata.measuredAsrSessions | integer | - |
measuredAsrPopulationdata.measuredAsrPopulation | string | - |
measuredAsrWindowdata.measuredAsrWindow | string | - |
measuredAcddata.measuredAcd | number | - |
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. - 404
NOT_FOUND: no such resource on your account. - 429
RATE_LIMITED: slow down and retry after theRetry-Afterseconds. - 500
INTERNAL_ERROR: unexpected failure. QuoteX-Request-Idto support.
Accept a seller's rate increase
POST/
- 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
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string | - |
Request body (application/json)
| Field | Type | Description |
|---|---|---|
acceptedRate | string | number | The new rate you are agreeing to, as shown to you (guards against a further change in between) |
deckVersion | string | For A-Z decks: the heldDeckVersion you reviewed |
changeId | string (uuid) | Accept a SCHEDULED change in advance (id from upcoming-rate-changes). Nothing resumes or bills now. |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
iddata.id | string (uuid) | - |
buyerIddata.buyerId | string (uuid) | Your account id |
routeIddata.routeId | string (uuid) | - |
statusdata.status | string | -One of active, paused, cancelled, pending_review |
sipUsernamedata.sipUsername | string | null | - |
createdAtdata.createdAt | string (date-time) | ISO-8601 timestamp (UTC) |
cancelledAtdata.cancelledAt | string (date-time) | null | ISO-8601 timestamp (UTC) |
agreedPricedata.agreedPrice | money | null | Negotiated price (from an accepted offer) that overrides the list price USD as a decimal string with exactly 6 places, e.g. "0.012500". |
heldOldRatedata.heldOldRate | money | null | Set 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.heldNewRate | money | null | The increased rate waiting for your acceptance USD as a decimal string with exactly 6 places, e.g. "0.012500". |
routingPrioritydata.routingPriority | integer | null | Your routing position (1 = first); null = unranked |
requiresUsCompliancedata.requiresUsCompliance | boolean | A US voice route that needs STIR/SHAKEN review before traffic flows |
reviewedAtdata.reviewedAt | string (date-time) | null | ISO-8601 timestamp (UTC) |
reviewNotedata.reviewNote | string | null | The compliance reviewer's note to you, if any |
sipPasswordSetdata.sipPasswordSet | boolean | Whether 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.smppSystemId | string | null | - |
offerIddata.offerId | string (uuid) | null | The accepted offer that set agreedPrice |
accepteddata.accepted | boolean | -One of true |
changeIddata.changeId | string (uuid) | - |
purchaseIddata.purchaseId | string (uuid) | - |
effectiveDatedata.effectiveDate | string (date-time) | ISO-8601 timestamp (UTC) |
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. - 404
NOT_FOUND: no such resource on your account. - 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.
List scheduled rate changes on a route you bought
GET/
- 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
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) | - |
Response 200
| Field | Type | Description |
|---|---|---|
datarequired | object | - |
purchaseIdrequireddata.purchaseId | string (uuid) | - |
affectsYourequireddata.affectsYou | boolean | True when you pay the list rate, so this change would pause you until accepted |
changesrequireddata.changes | object[] | - |
changeIdrequireddata.changes[].changeId | string (uuid) | Pass as changeId to POST /purchases/{id}/accept-rate |
kindrequireddata.changes[].kind | string | flat = the route's single price changes; deck = rate-sheet rows changeOne of flat, deck |
effectiveDaterequireddata.changes[].effectiveDate | string (date-time) | ISO-8601 timestamp (UTC) |
increasesrequireddata.changes[].increases | integer | - |
decreasesrequireddata.changes[].decreases | integer | - |
addedrequireddata.changes[].added | integer | - |
removedrequireddata.changes[].removed | integer | - |
largestIncreasePctrequireddata.changes[].largestIncreasePct | number | null | - |
acceptedrequireddata.changes[].accepted | boolean | - |
acceptedAtrequireddata.changes[].acceptedAt | string (date-time) | null | ISO-8601 timestamp (UTC) |
rowsrequireddata.changes[].rows | object[] | - |
destinationrequireddata.changes[].rows[].destination | string | null | - |
prefixrequireddata.changes[].rows[].prefix | string | null | - |
oldRaterequireddata.changes[].rows[].oldRate | 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". |
newRaterequireddata.changes[].rows[].newRate | 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". |
oldIncrementrequireddata.changes[].rows[].oldIncrement | string | null | - |
newIncrementrequireddata.changes[].rows[].newIncrement | string | null | - |
deltaPctrequireddata.changes[].rows[].deltaPct | number | null | - |
removedrequireddata.changes[].rows[].removed | boolean | - |
totalRowsrequireddata.changes[].totalRows | integer | Rows in the change for this listing (rows is capped at 200) |
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. - 404
NOT_FOUND: no such resource on your account. - 429
RATE_LIMITED: slow down and retry after theRetry-Afterseconds. - 500
INTERNAL_ERROR: unexpected failure. QuoteX-Request-Idto support.