Skip to main content

Error Codes

Every non-2xx response from the DialStack REST API shares the same shape:

JSON
{
"error": "Human-readable message describing what went wrong.",
"code": "machine_readable_code",
"details": { "field_name": "reason" }
}

error is always present. code is present on errors that have a stable machine-readable identifier (listed below). details is optional and carries field-level validation context when applicable.

info

Branch on code, not on error. The error string may be rephrased without notice; code values are stable and versioned. When no code is present, fall back to the HTTP status.

HTTP status codes​

StatusMeaningRetry?
400Malformed request — invalid JSON, missing required fields.No — fix the request.
401Missing, invalid, expired or revoked credentials, or the wrong kind of credential for this operation.No — check the key or token.
403Valid credentials, but no access to this account or operation.No — check DialStack-Account, or the session's components.
404Resource does not exist on this account.No.
409Conflict — e.g., duplicate handle, already assigned.No.
422Validation error — payload is well-formed but semantically wrong.No — fix the payload.
429Rate limit exceeded. Some limits send a Retry-After header.Yes, after Retry-After or a backoff.
5xxServer error. Transient unless documented otherwise.Yes, with exponential backoff.

Authentication errors​

Authentication failures carry no code; tell them apart by status.

401 Unauthorized means the request's credentials weren't accepted for this operation:

  • The Authorization header is missing, or isn't Bearer <token>.
  • The API key is unknown, or the token is invalid, expired or revoked.
  • The credential is the wrong kind for the operation. A user token sent to a platform or account operation, or a session token sent to a user-token operation such as /v1/webrtc, is rejected with 401. Session tokens and user tokens are both JWTs, but they are not interchangeable.

403 Forbidden means the credentials are valid but don't reach what was asked for:

  • The DialStack-Account header names an account that belongs to another platform.
  • A session token calls an operation that none of its enabled components allow.

See Authentication for which credential each operation takes.

Stable error codes​

The following code values are stable and safe to branch on. Codes not listed here are either legacy or platform-internal; do not rely on them.

CodeHTTPMeaning
validation_error400The request body failed validation. See details for field-level reasons.
invalid_pagination400The pagination cursor is malformed. Restart from the first page.
invalid_sandbox_phone_number400The supplied phone number is not a valid sandbox number for this platform.
sandbox_feature_unsupported422The requested feature is not available in sandbox mode for this platform.

Phone numbers​

CodeHTTPMeaning
phone_number_limit_exceeded409The request would take the account past its phone number limit. Raise the limit and retry.
phone_numbers_already_claimed409One or more of the requested numbers are already in service and cannot be ordered. The error string names them.
port_order_invalid_state409The port order's current status does not allow the action.
invalid_search400A number search filter is missing or badly formatted, or quantity is out of range. The error string names the parameter.
zip_not_available400The number search doesn't support this ZIP code, even though it is well-formed. details.zip is the ZIP code searched. Try a nearby ZIP code or search by area code.
search_rejected400The number search rejected the filters for another reason. Try different values.
search_unavailable503Phone number search is not available. Retry later.
toll_free_not_supported422Toll-free numbers do not support a caller ID name or a directory listing.

phone_number_limit_exceeded is returned when buying numbers, when creating or editing a port order, and when recovering a released number, since each of those grows the account's count. Raising the account's limit — or releasing numbers it no longer needs — clears it.

phone_numbers_already_claimed names the numbers that are unavailable — in the error string for a human, and in a details object for your code:

JSON
{
"code": "phone_numbers_already_claimed",
"error": "phone number(s) already claimed — already on this account: +17702126011",
"details": {
"already_on_account": ["+17702126011"],
"in_service_elsewhere": []
}
}

This is the only code that carries details, because which of your numbers conflicted is the one thing here you cannot get from any other request. Both keys are always present; an empty group is []. The two are separated because they call for different actions:

  • Already on your account. The number is yours; it does not need to be ordered again. Take it off the request.
  • Already in service elsewhere. The number is live on another account with the same carrier, so there is no port to run — moving it is an intra-carrier transfer, which support arranges. Contact support rather than retrying.

A number counts as claimed only while it is live — active or inactive — on any account. A number you released is not claimed, so it will not be reported here; request it back with POST /v1/phone-numbers/{phone_number_id} and status: active instead, which is the one case that can return phone_number_limit_exceeded on an update.

Note that a conflict confirms the number is in service with this carrier. That is not treated as a disclosure boundary: you supplied the number in the request, so the response tells you nothing about it you did not already know, beyond whether it is available — which is the question you asked. Abuse of this endpoint to enumerate numbers is addressed by rate limiting, not by withholding the answer.

port_order_invalid_state is returned when the action does not fit the order's current status — editing an order that has moved past the point where changes are accepted, cancelling one in a terminal status, submitting one that is not approved, approving one that is no longer a draft, or uploading a document to an order that is neither a draft nor in exception. The error message names both the status and the attempted action, so it reads as port order is in "submitted" status, cannot update.

The code is the whole machine-readable contract on these responses. There is no details object — everything one could carry is either already known to the caller (the numbers they sent, the endpoint they called, their own cap) or not theirs to see. Read error for a human-readable description.

Some conflicts on these endpoints carry no code, because there is no action a client can take automatically: a caller ID change already being processed, an account that already has a directory listing on another number, a number that is not in a recoverable state, a recovery the carrier declined, and a disconnect refused inside the 30-day minimum hold. Branch on the status and surface the error message for those.

Hardware orders​

POST /v1/hardware-orders/{order_id}/checkout places the order and debits your bank account for it. It refuses with 409 for several unrelated reasons, and the fix for each is somewhere different, so branch on code to decide where to send the operator.

CodeHTTPMeaning
order_not_draft409The order is not in a state checkout can place: a rejected order, which cannot be resumed, or an order DialStack placed on your behalf, whose payment DialStack collects outside checkout.
shipping_not_quoted409No shipping service has been chosen. Create a shipping quote, choose a service from it on the order, and retry — an order with no freight price is not a free-shipping one.
ship_to_incomplete409The order has no confirmed shipping address, or no ship_to_recipient and an account with no name for the label. Set ship_to_address (and a recipient or the account's name) and retry.
shipping_quote_expired409The shipping quote the service was chosen from has expired. Create a new shipping quote, choose a service from it on the order, and retry.
no_debit_authority409You have no verified bank account with a live ACH authorization. Finish bank setup and retry; the order is left as a draft, not placed.
no_default_payment_method409You have a verified, authorized bank account, but none is set as the default. Set one as the default and retry; the order is left as a draft, not placed.
payment_method_pending_verification409Your bank account is still being verified (microdeposits). Complete verification and retry; the order is left as a draft, not placed.
catalog_item_not_priced409A line on the order is unpriced or withdrawn from the catalog since the draft was built. Remove or replace it.
hardware_line_backordered409Part of this order is on backorder with no restock date, so we will not charge for goods we cannot ship. The shipping quote you chose from lists the held lines in backordered_lines. Remove or reduce the line, choose a service from a new quote, and retry; the draft survives.
hardware_order_already_debited409A debit already exists for this order, so it is no longer checkout-able. Poll the order for its payment status.
checkout_resume_window_elapsed409The order was placed more than 12 hours ago without a debit. Past that window a retry is no longer guaranteed against charging twice — contact support.
resale_certificate_required409You have no accepted, unexpired resale certificate on file. Hardware is sold for resale, so one is required before ordering. Upload or renew it and retry; the order is left as a draft.
location_conflict409A phone or DECT base on the order is going somewhere other than where a user staged on it is seated (the location chosen for the order, a location staged on the unit, or a base whose handsets have users at two sites). Align the locations, or move the user, and retry; the order is left as a draft.
debit_parameters_changed409Your bank account or authorization changed between the first attempt and this one, so the debit cannot be retried under the same key. Contact support.
hardware_checkout_live_only409The order ships to a sandbox or demo account. Checkout debits real money and orders real hardware, so it is only available for live accounts; drafts and shipping quotes still work.

no_debit_authority, no_default_payment_method, payment_method_pending_verification and hardware_checkout_live_only are raised before the order is placed, deliberately: placement freezes the prices and cannot be repeated, so discovering a missing authorization afterwards would strand the order rather than let you retry it.

Device provisioning​

CodeHTTPMeaning
unsupported_vendor400The phone vendor/model is not supported for provisioning.
invalid_configuration400The provisioning request is missing required device fields.
credentials_not_found404The device's user has no calling credentials to provision.
generation_failed500The provisioning file could not be generated. Safe to retry.

Handling errors​

How to branch on these responses, and when a retry is safe, is covered in Making requests.