Errors and HTTP status codes
Error format
Errors are JSON with a human readable detail and, when available, a stable machine code:
{ "detail": "invalid, expired or revoked API key", "code": "unauthorized" }
Validation errors add an errors list, one entry per problem, with the dotted path of the
field (loc) and a message (msg):
{
"detail": "invalid scenario",
"code": "invalid_scenario",
"errors": [{ "loc": "target.phone", "msg": "Value error, phone must be E.164, e.g. +15550123456" }]
}
Match on code and the status, never on detail: texts can change. Every error has a
code. New codes can appear: treat an unknown code like the generic code of its status.
Status codes
| Status | Generic code |
Meaning | What to do |
|---|---|---|---|
200 201 204 |
Success. 201 created, 204 no body |
||
202 |
CI mode: the run is still going at the wait timeout | Poll GET /runs/{id} |
|
400 |
bad_request |
The request is valid JSON but cannot be done (see below) | Read code and detail |
401 |
unauthorized |
Missing, malformed, unknown, expired or revoked key, or suspended organization | Check the key |
403 |
forbidden |
Not allowed: see the specific codes below | Add the scope, fix the network or upgrade |
404 |
not_found |
Unknown id, or an object of another organization | Check the id |
409 |
conflict |
Conflict: see the specific codes below | Read detail |
410 |
endpoint_sunset |
The endpoint was removed after its deprecation period | See Versioning |
412 |
CI mode: the run finished and failed the quality gate. Body: the run | Fail the pipeline | |
422 |
invalid_request |
The request does not match the schema (wrong type, missing field, out of range) | Fix the fields in errors |
422 |
invalid_scenario |
The scenario or variant is invalid | Fix the fields in errors |
429 |
rate_limited |
Too many requests | Wait Retry-After seconds |
503 |
service_unavailable |
A dependency is not available | Retry later |
5xx |
Server error | Retry GET requests with backoff; quote X-Request-Id to support |
Specific codes
400
code |
Endpoint | Meaning |
|---|---|---|
invalid_threshold |
POST /scenarios/{id}/runs |
threshold must be between 0 and 100 |
run_not_possible |
POST /scenarios/{id}/runs |
The scenario is archived, or none of the requested variants exist or are enabled |
replay_not_possible |
POST /replays |
The bundle's target number is not registered in this organization |
invalid_bundle, missing_bundle |
POST /replays, .../variants/from-bundle |
invalid bundle at <field>: <message>, or neither call_id nor bundle given |
invalid_target |
POST /targets |
The number is invalid, or its destination cannot be called |
attestation_not_accepted, attestation_signer_missing, verification_failed |
POST /targets/{id}/verify, .../verification/attest, .../verification/start |
The attestation was not accepted (accept is not true, or the signer name is missing or shorter than 3 characters) |
attestation_required |
.../verification/start |
Sign the attestation first (in the same request, or with .../verification/attest) |
method_unavailable, unknown_method, wrong_method |
.../verification/start, .../verification/call |
The verification method is not available for this number, unknown, or not the one of the active code |
no_active_code |
.../verification/call |
No active code: ask for a new one (.../verification/retry) |
unsafe_url |
POST /webhooks, PATCH /webhooks/{id} |
The URL is not https, or its host resolves to a private address |
unknown_event |
POST /webhooks, PATCH /webhooks/{id} |
An event type does not exist, or the list is empty |
invalid_filters |
POST /webhooks, PATCH /webhooks/{id} |
A filter names an unknown scenario, tag or environment, or an unknown filter key |
invalid_tags, invalid_request |
POST /tags/bulk |
Nothing to add or remove, no object, or an unknown object kind |
unknown_environment, invalid_environment |
Runs, versions, environments, comparisons | The environment (slug, name or id) does not exist in your organization |
unknown_version, invalid_version |
POST /scenarios/{id}/runs, versions, comparisons |
The version does not exist in that environment, or its label is invalid |
unknown_target, unknown_scenario |
Versions, environment bindings | The number or the scenario does not exist in your organization |
invalid_name, invalid_color, invalid_schedule, invalid_source_url, invalid_action |
Environments, versions, bindings | A field is invalid (see detail) |
invalid_period |
GET /environments/stability, GET /environments/compare |
days must be 7, 30 or 90 |
invalid_alert_channel |
POST /alert-channels, PATCH /alert-channels/{id} |
Not a Slack incoming webhook URL, invalid email address, more than 10 recipients, or no rule |
invalid_cidr |
POST /api-keys, PATCH /api-keys/{id} |
An allowed network is not a valid IP address or CIDR, or there are more than 20 |
key_not_active |
POST /api-keys/{id}/rotate |
The key is already revoked or expired |
invalid_url |
POST /wallet/checkout, /billing/... |
A redirect URL is not https |
unknown_plan |
POST /billing/subscription/checkout |
Not a public plan that can be bought online |
no_stripe_customer |
POST /billing/portal |
No subscription was ever bought online: subscribe first |
key_creator_missing |
POST /invitations |
The admin who created the key no longer exists: create a new key |
not_allowed |
POST /invitations, PATCH /members/{id}, DELETE /members/{id} |
Already a member, or the last admin (the seat limit is a 403, below) |
403
A plan refusal (feature_not_in_plan, country_not_in_plan) also carries
required_plan (the cheapest plan that lifts it) and upgrade_url (a one click upgrade page),
when such a plan exists. An out of range top-up (POST /wallet/checkout) is a 422
invalid_request on amount_cents, like any schema error.
code |
Meaning | Example detail |
|---|---|---|
missing_scope |
The key lacks the scope the endpoint needs | This API key lacks the 'write' scope (scenario.create). |
ip_not_allowed |
The key is restricted to IP networks and the request comes from elsewhere | This API key cannot be used from this IP address. |
feature_not_in_plan |
Your plan does not include the feature | Custom test data injection is not included in your plan (Starter). Upgrade to Growth to use it. |
feature_not_in_plan (feature: seats) |
POST /invitations: every seat of the plan is taken (pending invitations count) |
Your plan includes 3 seats, all taken (pending invitations count). Upgrade to Growth to invite more people. |
country_not_in_plan |
The number's country exceeds your plan's countries | Your plan allows 1 phone country (US); GB is not one of them. Growth includes 5 phone countries. |
blocked_policy |
POST /scenarios/{id}/runs: the scenario is held for a human review or refused by the compliance review |
Refused by the compliance review of this scenario (acceptable use policy): ... Change the scenario, or contact CallQA support to ask for a human review if you think this is a mistake. |
409
code |
Meaning |
|---|---|
already_exists |
A tag, an environment or a version with this name or label already exists |
target_in_use |
The number has scenarios or call history: archive it instead |
scenario_in_use |
The scenario cannot be deleted: archive it instead |
environment_in_use, default_required |
The environment has runs or versions (it is kept so their history stays readable), or it is the default environment: make another one the default first |
run_not_finished, call_not_finished |
DELETE /runs/{id} or DELETE /calls/{id} while a call is queued or in progress: cancel it first |
verification_record |
DELETE /calls/{id} on a number verification call, kept as evidence |
export_period_over, too_large, export_error |
POST /exports: the export period of a closed organization is over, or the archive is too large (contact support) |
call_not_judged |
The call has not been judged yet |
subscription_exists |
Already subscribed: change the plan in the billing portal |
already_verified |
Number verification: the number is already verified |
not_pending |
Number verification: start a verification first |
call_in_progress, verification_in_progress |
Number verification: a verification call is already running (for this number, or another number of your organization) |
number_suspended |
Number verification: the number is suspended by CallQA support |
429 (besides rate_limited)
code |
Meaning |
|---|---|
number_locked |
Number verification: 3 failed attempts in 24 hours, the number is locked for 24 hours |
org_daily_limit |
Number verification: at most 10 verification calls per organization and 24 hours |
503
code |
Meaning |
|---|---|
generator_unavailable |
The scenario generator is not configured or not reachable |
payments_unavailable |
Payments are not configured on this server |
Call statuses and block reasons
A call can fail without any HTTP error: the trigger succeeded, but the call itself did not
go through. Look at the call's status, block_reason and error.
status |
Final | Meaning |
|---|---|---|
queued |
no | Waiting for a slot |
dispatching, dialing, ringing, in_progress |
no | Being placed or in conversation |
judging |
no | The call ended, verdicts are being computed |
completed |
yes | Judged: see verdict and score |
error |
yes | Failed after being queued: launch failure, caller error, or no result after the maximum time (the call's limits.max_duration_s plus 10 minutes, 40 minutes at least: error says "No completion received from the caller agent (timed out)."). Verdict error, reservation released |
canceled |
yes | Canceled before dialing |
blocked_policy |
yes | Not placed: a rule forbids it (see block_reason) |
blocked_insufficient_credit |
yes | Not placed: not enough quota or wallet credit |
block_reason values:
block_reason |
status |
Meaning | Fix |
|---|---|---|---|
number_not_verified |
blocked_policy |
The target is not verified and has no attestation | Verify it |
number_locked |
blocked_policy |
3 failed verification attempts in 24 hours | Wait 24 hours or contact support |
number_suspended |
blocked_policy |
Suspended by CallQA support | Contact support |
sandbox_no_batch |
blocked_policy |
Scheduled or CI run on an unverified number | Verify the number |
sandbox_daily_cap |
blocked_policy |
More than 3 calls in 24 hours to an unverified number | Verify the number |
attestation_outdated |
blocked_policy |
The attestation was signed under an older text | Sign the new attestation |
target_archived |
blocked_policy |
The target was archived | Use an active number |
country_not_in_plan |
blocked_policy |
The number's country exceeds your plan | Upgrade the plan |
blocked_destination |
blocked_policy |
This kind of line cannot be called (premium rate, shared cost...) | Use another number |
invalid_number |
blocked_policy |
Not a valid, dialable number | Fix the number |
emergency_number |
blocked_policy |
Emergency and public safety numbers (911, 988, 112, 999, 15, 17, 18, 116xxx...) are never called | |
compliance_blocked |
blocked_policy |
The compliance review refused the content of this call; error_message gives the reasons |
Change the scenario, or ask support for a human review |
compliance_review |
blocked_policy |
The content of this call is held for a human compliance review by CallQA staff | Wait for the decision, or contact support |
daily_spend_cap |
blocked_policy |
CallQA's platform wide daily safety limit is reached (not your account) | Retry later or contact support |
org_suspended |
blocked_policy |
Your organization is suspended | Contact support |
org_closed |
blocked_policy |
Your organization is closed | |
subscription_inactive |
blocked_policy |
No active subscription | Choose a plan |
insufficient_credit |
blocked_insufficient_credit |
The wallet cannot cover the call | Top up the wallet |
quota_exhausted |
blocked_insufficient_credit |
Quota used up, the plan has no overage | Upgrade the plan |
spend_cap_reached |
blocked_insufficient_credit |
Monthly spending cap reached | An admin raises the cap (PATCH /billing/settings) |
overage_disabled |
blocked_insufficient_credit |
Quota and bonus calls used up, and paid overage is off | An admin turns overage on, or upgrades |
Some conditions only delay a call, it stays queued: a compliance review
still in progress (a few seconds), your plan's concurrent call limit,
the maximum number of calls per hour to the same number (30), for scheduled runs the call
window of the scenario in that environment, and, for an unverified number in the sandbox, one
call at a time between 8 am and 9 pm local time (see
Number verification).
Retrying safely
GETrequests are safe to retry.- A
429is answered before anything happens: retrying any request afterRetry-Afteris safe. POST /scenarios/{id}/runsandPOST /replayscreate a new run each time. After a network error, checkGET /runs?scenario_id=...&since=...before triggering again, or you may pay for two runs.POST /scenarios/importis an upsert by name: retrying it is safe.