API explorer: every endpoint on one page, with a "Test request" button. Paste your API key once, send real requests, copy the curl.
Open the API explorerAPI reference
Generated by tools/gen_api_docs.py from app/api. Do not edit by hand.
Base URL: https://app.callqa.dev/api/v1. Version: 1.0.0. 124 endpoints.
- Try every endpoint in your browser: API explorer. Paste your API key once, then send real requests and copy the curl of each one.
- Machine readable:
api/openapi.json(OpenAPI 3.1, import it in Postman, Insomnia or a client generator). Live schema:/api/v1/openapi.json. - Every request needs
Authorization: Bearer <API key>. See Authentication. - Every endpoint can also answer
401,422(invalid input) and429; endpoints that need a scope answer403when the key lacks it. See Errors.
Scope is the API key scope the endpoint needs (admin implies all). Roles are the dashboard roles allowed to do the same thing in the UI.
Index
- Organization
- Targets
- Scenarios
- Runs
- Calls
- Tags
- Versions
- Environments
- Webhooks
- Alerts
- API keys & logs
- Exports for LLMs
- Billing
- Support
Organization
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/audit-log |
Audit log (admin) | admin |
admin | 200 |
GET |
/api/v1/exports |
List exports | admin |
admin | 200 |
POST |
/api/v1/exports |
Request an export | admin |
admin | 200, 201 |
GET |
/api/v1/exports/{export_id} |
Get an export | admin |
admin | 200 |
POST |
/api/v1/invitations |
Invite a member | admin |
admin | 201 |
GET |
/api/v1/me |
Who am I | read |
admin, member, guest | 200 |
GET |
/api/v1/members |
List members | read |
admin, member, guest | 200 |
PATCH |
/api/v1/members/{member_id} |
Change a member's role | admin |
admin | 200 |
DELETE |
/api/v1/members/{member_id} |
Remove a member | admin |
admin | 204 |
GET /api/v1/audit-log
Audit log (admin).
Who did what in your organization, newest first: members, keys, webhooks, alert channels, billing, number verification, deletions. Never contains secrets. Filter by action (exact, or a prefix ending with . such as webhook.), actor_api_key_prefix and since / until.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
action |
query | string | no | |
actor_api_key_prefix |
query | string | no | |
since |
query | string (date-time) | no | |
until |
query | string (date-time) | no | |
limit |
query | integer | no | 50 |
offset |
query | integer | no | 0 |
Responses: 200 Page_AuditOut_, 401, 403, 422, 429.
GET /api/v1/exports
List exports.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
limit |
query | integer | no | 20 |
offset |
query | integer | no | 0 |
Responses: 200 Page_ExportOut_, 401, 403, 422, 429.
POST /api/v1/exports
Request an export.
A full export of your organization (admin scope): a zip of JSON files with the organization and members, numbers and attestations, scenarios and test cases, runs, one file per call (trace, transcript, result), releases, tags, audit log, legal acceptances, usage and wallet ledger. Secrets are never exported. The archive is built in the background: poll GET /exports/{id} until status is done, then download download_url (signed, valid 24 hours, no API key needed). Archives are deleted after 7 days. If an export is already being built, it is returned (200).
Responses: 200 ExportOut, 201 ExportOut, 401, 403, 409 Error, 422, 429.
GET /api/v1/exports/{export_id}
Get an export.
download_url is set when status is done (a fresh 24 h link on every call).
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
export_id |
path | string (uuid) | yes |
Responses: 200 ExportOut, 401, 403, 404, 422, 429.
POST /api/v1/invitations
Invite a member.
Sends an invitation email valid 7 days. Seats are limited by the plan: when they are all taken (pending invitations count), 403 feature_not_in_plan with required_plan and upgrade_url.
Body (application/json): InvitationIn.
Responses: 201 object, 400 Error, 401, 403 Error, 422, 429.
GET /api/v1/me
Who am I.
The organization, plan and scopes of the API key used for the call.
Responses: 200 MeOut, 401, 403, 422, 429.
GET /api/v1/members
List members.
Responses: 200 list of MemberOut, 401, 403, 422, 429.
PATCH /api/v1/members/{member_id}
Change a member's role.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
member_id |
path | integer | yes |
Body (application/json): RoleIn.
Responses: 200 MemberOut, 400 Error, 401, 403, 404, 422, 429.
DELETE /api/v1/members/{member_id}
Remove a member.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
member_id |
path | integer | yes |
Responses: 204, 400 Error, 401, 403, 404, 422, 429.
Targets
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/targets |
List target numbers | read |
admin, member, guest | 200 |
POST |
/api/v1/targets |
Register a target number | write |
admin, member | 201 |
GET |
/api/v1/targets/{target_id} |
Get a target | read |
admin, member, guest | 200 |
PATCH |
/api/v1/targets/{target_id} |
Update a target | write |
admin, member | 200 |
DELETE |
/api/v1/targets/{target_id} |
Delete a target (admin) | admin |
admin | 204 |
GET |
/api/v1/targets/{target_id}/verification |
Verification status | read |
admin, member, guest | 200 |
POST |
/api/v1/targets/{target_id}/verification/attest |
Sign the attestation | write |
admin, member | 200 |
POST |
/api/v1/targets/{target_id}/verification/call |
Place the verification call | run |
admin, member | 202 |
POST |
/api/v1/targets/{target_id}/verification/retry |
Retry with a new code | none | 201 | |
POST |
/api/v1/targets/{target_id}/verification/start |
Start a verification (new code) | none | 201 | |
POST |
/api/v1/targets/{target_id}/verify |
Sign the attestation (legacy) | write |
admin, member | 200 |
GET /api/v1/targets
List target numbers.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
include_archived |
query | boolean | no | false |
Responses: 200 list of TargetOut, 401, 403, 422, 429.
POST /api/v1/targets
Register a target number.
The number is classified (country, line type). Premium-rate, shared-cost and emergency numbers are refused. A new number starts unverified: verify it (POST /targets/{id}/verification/start) before running tests on it.
Body (application/json): TargetIn.
Responses: 201 TargetOut, 400 Error, 401, 403, 422, 429.
GET /api/v1/targets/{target_id}
Get a target.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
target_id |
path | string (uuid) | yes |
Responses: 200 TargetOut, 401, 403, 404, 422, 429.
PATCH /api/v1/targets/{target_id}
Update a target.
The phone number itself cannot change: register a new target.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
target_id |
path | string (uuid) | yes |
Body (application/json): TargetPatch.
Responses: 200 TargetOut, 401, 403, 404, 422, 429.
DELETE /api/v1/targets/{target_id}
Delete a target (admin).
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
target_id |
path | string (uuid) | yes |
Responses: 204, 401, 403, 404, 409 Error, 422, 429.
GET /api/v1/targets/{target_id}/verification
Verification status.
Why: CallQA only calls numbers you control. Verification proves it; until then a number with a signed attestation only gets the sandbox (3 calls per 24 h, one at a time, 3 minutes max, no schedule or CI run).
Default flow (spoken_code):
1. POST /targets/{id}/verification/start (sign the attestation in the same request the
first time). The response holds a 6-digit code, shown once and never stored in clear.
2. Add the phrase ("Verification code 4 8 2 1 9 3.") to the line's greeting, or to the
agent prompt ("if someone asks for a verification code, answer 4 8 2 1 9 3").
3. POST /targets/{id}/verification/call: we call the number, say "Verification, please."
if the line waits for the caller to speak, and listen for 20 s.
IVR menu in front of the code: give dtmf_gate to start (for example "1w2"): after
the answer we wait 1 s, press the keys (w = 0.5 s pause), then listen. The keys are
remembered on the number for the next verification. renew: true verifies a verified
number again without leaving verified.
4. Poll GET /targets/{id}/verification until status is verified. On failure it stays
pending: last_attempt.failure_hint says what to fix, last_attempt.heard_digits what
we understood, last_attempt.sip_status why a line that never answered did not (480
unavailable, 486 busy, 603 declined...); POST .../verification/retry issues a new code.
Limits: a code lives 24 h and is used by a single call; 3 failed attempts in 24 h lock the
number for 24 h (a call the line does not answer counts: attempts_left, and
attempts_reset_at tells when the oldest one stops counting); 10 verification calls per
organization and 24 h, one at a time. A verification is valid 180 days, then the number is
expired and must be verified again.
Attestation (version 2026-09-26-v2-draft), signed once per number and
again when a new version becomes mandatory (attestation_outdated); the placeholders are
filled in when you sign:
I, {signer_name}, confirm that I am authorized to act on behalf of {organization_name} (the "Organization") and I certify, on behalf of the Organization, that:
- The Organization is the subscriber of the phone number {phone_number}, or holds a prior written authorization from its subscriber to have it called by automated systems using artificial or synthetic voices, for testing purposes. The Organization will keep that authorization and provide it to CallQA on request.
- This number is not an emergency number, a premium rate or special number, or the personal line of a person who has not agreed in writing to receive these calls.
- The calls will only be used to test the telephone system that answers this number. They will not be used for marketing, sales, surveys, or to reach any third party.
- Where a person may answer or listen to these calls, the Organization has informed that person in advance and obtained any consent required by law for automated calls, for the use of an AI voice, and for the recording and transcription of the calls.
- The calls, their content and any test data comply with the laws that apply to them, including laws on automated and artificial voice calls (such as the US Telephone Consumer Protection Act), telemarketing, call recording and data protection, and with the CallQA Terms of Service and Acceptable Use Policy.
- If the Organization stops controlling this number, it will stop calling it and remove it from CallQA without delay.
I understand that CallQA relies on this attestation to place the calls, that it records my name, email, IP address and the date and time of this signature as evidence, that it may suspend calls to this number at any time, and that the Organization is responsible for any claim resulting from calls placed at its request, as set out in the Terms of Service.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
target_id |
path | string (uuid) | yes |
Responses: 200 VerificationOut, 401, 403, 404, 422, 429.
POST /api/v1/targets/{target_id}/verification/attest
Sign the attestation.
Required once per number before verifying, and again when a new attestation version becomes mandatory. On its own it only opens the sandbox. The signed text, your name, email or API key, IP and time are kept as evidence. Text: see GET /targets/{id}/verification.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
target_id |
path | string (uuid) | yes |
Body (application/json): AttestIn.
Responses: 200 VerificationOut, 400 Error, 401, 403 Error, 404, 409 Error, 422, 429 Error.
POST /api/v1/targets/{target_id}/verification/call
Place the verification call.
Calls the number once for the active spoken_code code (after pressing its dtmf_gate keys, if any). The call is free. Poll GET .../verification (or the call) for the result.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
target_id |
path | string (uuid) | yes |
Body (application/json): VerificationCallIn.
Responses: 202 VerificationCallOut, 400 Error, 401, 403 Error, 404, 409 Error, 422, 429 Error.
POST /api/v1/targets/{target_id}/verification/retry
Retry with a new code.
After a failed attempt (or to replace the active code): issues a new code and cancels the previous one. Put the new phrase in the greeting or prompt before calling again.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
target_id |
path | string (uuid) | yes |
Body (application/json): VerificationStartIn.
Responses: 201 VerificationCodeOut, 400 Error, 401, 403 Error, 404, 409 Error, 422, 429 Error.
POST /api/v1/targets/{target_id}/verification/start
Start a verification (new code).
Why: CallQA only calls numbers you control. Verification proves it; until then a number with a signed attestation only gets the sandbox (3 calls per 24 h, one at a time, 3 minutes max, no schedule or CI run).
Default flow (spoken_code):
1. POST /targets/{id}/verification/start (sign the attestation in the same request the
first time). The response holds a 6-digit code, shown once and never stored in clear.
2. Add the phrase ("Verification code 4 8 2 1 9 3.") to the line's greeting, or to the
agent prompt ("if someone asks for a verification code, answer 4 8 2 1 9 3").
3. POST /targets/{id}/verification/call: we call the number, say "Verification, please."
if the line waits for the caller to speak, and listen for 20 s.
IVR menu in front of the code: give dtmf_gate to start (for example "1w2"): after
the answer we wait 1 s, press the keys (w = 0.5 s pause), then listen. The keys are
remembered on the number for the next verification. renew: true verifies a verified
number again without leaving verified.
4. Poll GET /targets/{id}/verification until status is verified. On failure it stays
pending: last_attempt.failure_hint says what to fix, last_attempt.heard_digits what
we understood, last_attempt.sip_status why a line that never answered did not (480
unavailable, 486 busy, 603 declined...); POST .../verification/retry issues a new code.
Limits: a code lives 24 h and is used by a single call; 3 failed attempts in 24 h lock the
number for 24 h (a call the line does not answer counts: attempts_left, and
attempts_reset_at tells when the oldest one stops counting); 10 verification calls per
organization and 24 h, one at a time. A verification is valid 180 days, then the number is
expired and must be verified again.
Attestation (version 2026-09-26-v2-draft), signed once per number and
again when a new version becomes mandatory (attestation_outdated); the placeholders are
filled in when you sign:
I, {signer_name}, confirm that I am authorized to act on behalf of {organization_name} (the "Organization") and I certify, on behalf of the Organization, that:
- The Organization is the subscriber of the phone number {phone_number}, or holds a prior written authorization from its subscriber to have it called by automated systems using artificial or synthetic voices, for testing purposes. The Organization will keep that authorization and provide it to CallQA on request.
- This number is not an emergency number, a premium rate or special number, or the personal line of a person who has not agreed in writing to receive these calls.
- The calls will only be used to test the telephone system that answers this number. They will not be used for marketing, sales, surveys, or to reach any third party.
- Where a person may answer or listen to these calls, the Organization has informed that person in advance and obtained any consent required by law for automated calls, for the use of an AI voice, and for the recording and transcription of the calls.
- The calls, their content and any test data comply with the laws that apply to them, including laws on automated and artificial voice calls (such as the US Telephone Consumer Protection Act), telemarketing, call recording and data protection, and with the CallQA Terms of Service and Acceptable Use Policy.
- If the Organization stops controlling this number, it will stop calling it and remove it from CallQA without delay.
I understand that CallQA relies on this attestation to place the calls, that it records my name, email, IP address and the date and time of this signature as evidence, that it may suspend calls to this number at any time, and that the Organization is responsible for any claim resulting from calls placed at its request, as set out in the Terms of Service.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
target_id |
path | string (uuid) | yes |
Body (application/json): VerificationStartIn.
Responses: 201 VerificationCodeOut, 400 Error, 401, 403 Error, 404, 409 Error, 422, 429 Error.
POST /api/v1/targets/{target_id}/verify
Sign the attestation (legacy).
Kept for existing clients: same as POST .../verification/attest, returns the target. The attestation does not verify the number; see POST .../verification/start.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
target_id |
path | string (uuid) | yes |
Body (application/json): VerifyIn.
Responses: 200 TargetOut, 400 Error, 401, 403, 404, 422, 429.
Scenarios
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
POST |
/api/v1/generation-jobs/from-call/{call_id} |
Suggest a regression variant from a call | write |
admin, member | 202 |
GET |
/api/v1/generation-jobs/{job_id} |
Get a generation job | read |
admin, member, guest | 200 |
GET |
/api/v1/scenarios |
List scenarios | read |
admin, member, guest | 200 |
POST |
/api/v1/scenarios |
Create a scenario | write |
admin, member | 201 |
POST |
/api/v1/scenarios/generate |
Generate scenarios from a description | write |
admin, member | 202 |
POST |
/api/v1/scenarios/import |
Import a scenario from YAML | write |
admin, member | 200, 201 |
GET |
/api/v1/scenarios/schema |
Scenario JSON Schema | read |
admin, member, guest | 200 |
POST |
/api/v1/scenarios/validate |
Validate a scenario without saving | read |
admin, member, guest | 200 |
GET |
/api/v1/scenarios/{scenario_id} |
Get a scenario | read |
admin, member, guest | 200 |
PUT |
/api/v1/scenarios/{scenario_id} |
Replace a scenario | write |
admin, member | 200 |
DELETE |
/api/v1/scenarios/{scenario_id} |
Delete a scenario (admin) | admin |
admin | 204 |
POST |
/api/v1/scenarios/{scenario_id}/archive |
Archive a scenario | write |
admin, member | 200 |
GET |
/api/v1/scenarios/{scenario_id}/export |
Export as YAML or JSON | read |
admin, member, guest | 200 |
GET |
/api/v1/scenarios/{scenario_id}/variants |
List variants | read |
admin, member, guest | 200 |
POST |
/api/v1/scenarios/{scenario_id}/variants |
Add a variant (test case) | write |
admin, member | 201 |
POST |
/api/v1/scenarios/{scenario_id}/variants/derive |
Auto-derive variants | write |
admin, member | 202 |
POST |
/api/v1/scenarios/{scenario_id}/variants/from-bundle |
Turn a call into a regression test | write |
admin, member | 201 |
PATCH |
/api/v1/variants/{variant_id} |
Update a variant | write |
admin, member | 200 |
DELETE |
/api/v1/variants/{variant_id} |
Delete a variant (admin) | admin |
admin | 204 |
POST /api/v1/generation-jobs/from-call/{call_id}
Suggest a regression variant from a call.
Proposes one MANUAL variant (not saved) that reproduces what went wrong in a judged call. Add it with POST /scenarios/{id}/variants.
Generation takes 10 to 60 seconds, so it runs in the background: the answer is 202 with a job; poll GET /generation-jobs/{id} (its url) until status is done (then read result) or error.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
call_id |
path | string (uuid) | yes |
Responses: 202 GenerationJobOut, 401, 403, 404, 409 Error, 422, 429, 503 Error.
GET /api/v1/generation-jobs/{job_id}
Get a generation job.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
job_id |
path | string (uuid) | yes |
Responses: 200 GenerationJobOut, 401, 403, 404, 422, 429.
GET /api/v1/scenarios
List scenarios.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
tag |
query | string | no | |
include_archived |
query | boolean | no | false |
q |
query | string | no |
Responses: 200 list of ScenarioSummary, 401, 403, 422, 429.
POST /api/v1/scenarios
Create a scenario.
Body: a scenario document. The full grammar is published as JSON Schema at GET /api/v1/scenarios/schema. The landing-page shorthand (phone, persona as text, must_include, must_not, max_latency_ms at top level) is accepted. Unknown fields are rejected unless they start with x_ (kept verbatim).
A target number that does not exist yet is registered as unverified.
Body (application/json): object.
Responses: 201 ScenarioOut, 401, 403 Error, 422 ScenarioInvalid, 429.
POST /api/v1/scenarios/generate
Generate scenarios from a description.
Proposes scenario documents (not saved). Review them, then POST the ones you keep to /scenarios. Invalid proposals are filtered out.
Generation takes 10 to 60 seconds, so it runs in the background: the answer is 202 with a job; poll GET /generation-jobs/{id} (its url) until status is done (then read result) or error.
Body (application/json): GenerateIn.
Responses: 202 GenerationJobOut, 401, 403, 422, 429, 503 Error.
POST /api/v1/scenarios/import
Import a scenario from YAML.
Send the YAML as the raw body (Content-Type: application/yaml). If a non-archived scenario with the same name exists it is updated (upsert), otherwise created.
Body (application/yaml): string.
Responses: 200 ScenarioOut, 201 ScenarioOut, 401, 403 Error, 422 ScenarioInvalid, 429.
GET /api/v1/scenarios/schema
Scenario JSON Schema.
JSON Schema of the scenario grammar (generated from callqa_spec).
Responses: 200 object, 401, 403, 422, 429.
POST /api/v1/scenarios/validate
Validate a scenario without saving.
Body: a scenario document. The full grammar is published as JSON Schema at GET /api/v1/scenarios/schema. The landing-page shorthand (phone, persona as text, must_include, must_not, max_latency_ms at top level) is accepted. Unknown fields are rejected unless they start with x_ (kept verbatim).
Body (application/json): object.
Responses: 200 object, 401, 403, 422 ScenarioInvalid, 429.
GET /api/v1/scenarios/{scenario_id}
Get a scenario.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes |
Responses: 200 ScenarioOut, 401, 403, 404, 422, 429.
PUT /api/v1/scenarios/{scenario_id}
Replace a scenario.
Body: a scenario document. The full grammar is published as JSON Schema at GET /api/v1/scenarios/schema. The landing-page shorthand (phone, persona as text, must_include, must_not, max_latency_ms at top level) is accepted. Unknown fields are rejected unless they start with x_ (kept verbatim).
Variants are matched by title: existing ones keep their id (and their history).
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes |
Body (application/json): object.
Responses: 200 ScenarioOut, 401, 403 Error, 404, 422 ScenarioInvalid, 429.
DELETE /api/v1/scenarios/{scenario_id}
Delete a scenario (admin).
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes |
Responses: 204, 401, 403, 404, 409 Error, 422, 429.
POST /api/v1/scenarios/{scenario_id}/archive
Archive a scenario.
Archived scenarios stop their schedule and are hidden from lists; their history is kept. Members may archive; only admins delete.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes |
Responses: 200 ScenarioSummary, 401, 403, 404, 422, 429.
GET /api/v1/scenarios/{scenario_id}/export
Export as YAML or JSON.
format=yaml (default) or format=json. Round-trips with /import.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes | |
format |
query | string | no | "yaml" |
Responses: 200, 401, 403, 404, 422, 429.
GET /api/v1/scenarios/{scenario_id}/variants
List variants.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes |
Responses: 200 list of VariantOut, 401, 403, 404, 422, 429.
POST /api/v1/scenarios/{scenario_id}/variants
Add a variant (test case).
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes |
Body (application/json): VariantIn.
Responses: 201 VariantOut, 401, 403 Error, 404, 422 ScenarioInvalid, 429.
POST /api/v1/scenarios/{scenario_id}/variants/derive
Auto-derive variants.
Adds AUTO test cases (edge cases, personas, accents, error paths) when the job completes. Requires the auto-derived coverage feature (Growth and up).
Generation takes 10 to 60 seconds, so it runs in the background: the answer is 202 with a job; poll GET /generation-jobs/{id} (its url) until status is done (then read result) or error.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes |
Body (application/json): DeriveIn.
Responses: 202 GenerationJobOut, 401, 403 Error, 404, 422, 429, 503 Error.
POST /api/v1/scenarios/{scenario_id}/variants/from-bundle
Turn a call into a regression test.
Imports a replay bundle (or a call of yours) as a MANUAL variant: the "Add to regression suite" button.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes |
Body (application/json): FromBundleIn.
Responses: 201 VariantOut, 400 Error, 401, 403, 404, 422 ScenarioInvalid, 429.
PATCH /api/v1/variants/{variant_id}
Update a variant.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
variant_id |
path | string (uuid) | yes |
Body (application/json): VariantPatch.
Responses: 200 VariantOut, 401, 403, 404, 422 ScenarioInvalid, 429.
DELETE /api/v1/variants/{variant_id}
Delete a variant (admin).
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
variant_id |
path | string (uuid) | yes |
Responses: 204, 401, 403, 404, 422, 429.
Runs
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/runs |
List runs | read |
admin, member, guest | 200 |
GET |
/api/v1/runs/{run_id} |
Get a run | read |
admin, member, guest | 200 |
DELETE |
/api/v1/runs/{run_id} |
Delete a run and its calls | admin |
admin | 204 |
POST |
/api/v1/runs/{run_id}/cancel |
Cancel the calls not dialed yet | run |
admin, member | 200 |
POST |
/api/v1/scenarios/{scenario_id}/runs |
Trigger a run (CI friendly) | run |
admin, member | 200, 201, 202 |
GET /api/v1/runs
List runs.
Filter by scenario_id, status, since, environment (slug, name or id), version_id (runs attributed to the version) and tag.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
query | string (uuid) | no | |
status |
query | string | no | |
since |
query | string (date-time) | no | |
environment |
query | string | no | |
version_id |
query | string (uuid) | no | |
tag |
query | string | no | |
limit |
query | integer | no | 50 |
offset |
query | integer | no | 0 |
Responses: 200 Page_RunOut_, 401, 403, 422, 429.
GET /api/v1/runs/{run_id}
Get a run.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
run_id |
path | string (uuid) | yes |
Responses: 200 RunOut, 401, 403, 404, 422, 429.
DELETE /api/v1/runs/{run_id}
Delete a run and its calls.
Admin scope. Deletes the run and all its calls (trace, transcript, result, bundles) for good. Refused (409 run_not_finished) while a call of the run is queued or in progress: cancel it first. Billing lines are kept without the calls' content. Audited.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
run_id |
path | string (uuid) | yes |
Responses: 204, 401, 403, 404, 409 Error, 422, 429.
POST /api/v1/runs/{run_id}/cancel
Cancel the calls not dialed yet.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
run_id |
path | string (uuid) | yes |
Responses: 200 RunOut, 401, 403, 404, 422, 429.
POST /api/v1/scenarios/{scenario_id}/runs
Trigger a run (CI friendly).
Queues one call per enabled variant (or the listed ones).
CI usage: ?wait=true&threshold=80 holds the request until the run finishes (timeout seconds, default 600, capped at 900) and answers:
* 200 finished and passed (score >= threshold, no failed, error or blocked call);
* 412 finished and failed;
* 202 still running at the timeout: poll GET /runs/{id}.
Without wait the answer is 201 with the queued run.
curl --fail alone is not a safe gate: it treats 202 as a success, so a slow run would pass. Check the status code:
code=$(curl -sS -o run.json -w '%{http_code}' -X POST --max-time 960 \
-H "Authorization: Bearer $CALLQA_API_KEY" \
"https://app.callqa.dev/api/v1/scenarios/$SCENARIO/runs?wait=true&timeout=900&threshold=80")
[ "$code" = "200" ] || exit 1
The wait is asynchronous on the server (no worker is held), but proxies in front of your runner may cut a request open for minutes: the CLI polls instead.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes | |
wait |
query | boolean | no | false |
timeout |
query | integer | no | 600 |
threshold |
query | integer | no |
Body (application/json): TriggerIn.
Responses: 200 RunOut, 201 RunOut, 202 RunOut, 400 Error, 401, 403 Error, 404, 412 RunOut, 422, 429.
Calls
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/calls |
List calls | read |
admin, member, guest | 200 |
GET |
/api/v1/calls/{call_id} |
Get a call | read |
admin, member, guest | 200 |
DELETE |
/api/v1/calls/{call_id} |
Delete a call | admin |
admin | 204 |
GET |
/api/v1/calls/{call_id}/bundle |
Export a replay bundle | read |
admin, member | 200 |
GET |
/api/v1/calls/{call_id}/events |
Call trace | read |
admin, member, guest | 200 |
GET |
/api/v1/calls/{call_id}/recording |
Download the call audio | read |
admin, member, guest | 200, 206 |
POST |
/api/v1/replays |
Replay a call | run |
admin, member | 201 |
GET /api/v1/calls
List calls.
Filter by status (e.g. error, blocked_insufficient_credit), verdict, run_id, since, environment.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
status |
query | string | no | |
verdict |
query | string | no | |
run_id |
query | string (uuid) | no | |
since |
query | string (date-time) | no | |
environment |
query | string | no | |
limit |
query | integer | no | 50 |
offset |
query | integer | no | 0 |
Responses: 200 Page_CallOut_, 401, 403, 422, 429.
GET /api/v1/calls/{call_id}
Get a call.
Includes the ResolvedRun, the result (verdicts with evidence, metrics, score) and the transcript.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
call_id |
path | string (uuid) | yes |
Responses: 200 CallDetailOut, 401, 403, 404, 422, 429.
DELETE /api/v1/calls/{call_id}
Delete a call.
Admin scope. Deletes the call, its trace, transcript, result and bundles for good. Refused with 409 while the call is not finished (call_not_finished) and for number verification calls, kept as evidence (verification_record). Billing lines are kept without the call's content. Audited.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
call_id |
path | string (uuid) | yes |
Responses: 204, 401, 403, 404, 409 Error, 422, 429.
GET /api/v1/calls/{call_id}/bundle
Export a replay bundle.
A self-contained JSON file (callqa_spec.ReplayBundle): resolved run, runtime (models, voices, settings), full trace and result. Replay it with POST /replays or import it as a regression test with POST /scenarios/{id}/variants/from-bundle.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
call_id |
path | string (uuid) | yes |
Responses: 200 object, 401, 403, 404, 422, 429.
GET /api/v1/calls/{call_id}/events
Call trace.
The append-only event trace (callqa_spec.CallEvent), by seq.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
call_id |
path | string (uuid) | yes | |
after_seq |
query | integer | no | -1 |
limit |
query | integer | no | 200 |
offset |
query | integer | no | 0 |
Responses: 200 Page_EventOut_, 401, 403, 404, 422, 429.
GET /api/v1/calls/{call_id}/recording
Download the call audio.
The stereo WAV of the call (16 bit PCM, 16 kHz): left track our test caller, right track your line. Sample 0 is the dialing event: a trace instant t_ms is at (t_ms - recording.start_ms) / 1000 seconds. Supports HTTP Range. 404 when the organization does not record audio, or once the recording expired (audio retention).
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
call_id |
path | string (uuid) | yes |
Responses: 200, 206, 401, 403, 404, 422, 429.
POST /api/v1/replays
Replay a call.
Creates a new call with exactly the same ResolvedRun (and by default the same runtime). The bundle's target must be a number of your organization; normal checks apply (verification, credit, concurrency).
Body (application/json): ReplayIn.
Responses: 201 CallOut, 400 Error, 401, 403, 404 Error, 422, 429.
Tags
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/tags |
List or search tags | read |
admin, member, guest | 200 |
POST |
/api/v1/tags |
Create a tag | write |
admin, member | 201 |
POST |
/api/v1/tags/bulk |
Tag or untag many objects | write |
admin, member | 200 |
GET |
/api/v1/tags/{tag_id} |
Get a tag | read |
admin, member, guest | 200 |
PATCH |
/api/v1/tags/{tag_id} |
Update a tag | write |
admin, member | 200 |
DELETE |
/api/v1/tags/{tag_id} |
Delete a tag (admin) | admin |
admin | 204 |
GET /api/v1/tags
List or search tags.
Tags are free keywords (a code name, a customer, a team) on scenarios, runs, versions and numbers; CallQA gives them no meaning of its own. With q, the autocomplete: names starting with q first, then containing it, most used first, at most limit (default 10, max 50).
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
q |
query | string | no | |
limit |
query | integer | no |
Responses: 200 list of TagSuggestion, 401, 403, 422, 429.
POST /api/v1/tags
Create a tag.
Body (application/json): TagIn.
Responses: 201 TagOut, 401, 403, 409 Error, 422, 429.
POST /api/v1/tags/bulk
Tag or untag many objects.
Adds add and removes remove on every listed scenario, run, version and number (by id). Tags to add are created when new. An id of another organization answers 404 unknown_object and nothing is changed.
Body (application/json): TagBulkIn.
Responses: 200 TagBulkOut, 400 Error, 401, 403, 404 Error, 422, 429.
GET /api/v1/tags/{tag_id}
Get a tag.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
tag_id |
path | integer | yes |
Responses: 200 TagOut, 401, 403, 404, 422, 429.
PATCH /api/v1/tags/{tag_id}
Update a tag.
Renaming a tag keeps it on its scenarios and runs.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
tag_id |
path | integer | yes |
Body (application/json): TagPatch.
Responses: 200 TagOut, 401, 403, 404, 409 Error, 422, 429.
DELETE /api/v1/tags/{tag_id}
Delete a tag (admin).
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
tag_id |
path | integer | yes |
Responses: 204, 401, 403, 404, 422, 429.
Versions
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/releases |
List releases (deprecated) | read |
admin, member, guest | 200 |
POST |
/api/v1/releases |
Tag a release (deprecated) | write |
admin, member | 201 |
GET |
/api/v1/releases/{release_id} |
Get a release (deprecated) | read |
admin, member, guest | 200 |
PATCH |
/api/v1/releases/{release_id} |
Update a release (deprecated) | write |
admin, member | 200 |
DELETE |
/api/v1/releases/{release_id} |
Delete a release (deprecated, admin) | admin |
admin | 204 |
POST |
/api/v1/releases/{release_id}/promote |
Promote a release (deprecated) | write |
admin, member | 201 |
GET |
/api/v1/versions |
List versions | read |
admin, member, guest | 200 |
POST |
/api/v1/versions |
Create a version | write |
admin, member | 201 |
GET |
/api/v1/versions/{version_id} |
Get a version | read |
admin, member, guest | 200 |
PATCH |
/api/v1/versions/{version_id} |
Update a version | write |
admin, member | 200 |
DELETE |
/api/v1/versions/{version_id} |
Delete a version (admin) | admin |
admin | 204 |
POST |
/api/v1/versions/{version_id}/promote |
Promote a version to another environment | write |
admin, member | 201 |
GET /api/v1/releases
List releases (deprecated).
Deprecated since 2026-09-27, removed on 2027-03-31.
Use GET /versions.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
environment |
query | string | no |
Responses: 200 list of ReleaseOut, 401, 403, 422, 429.
POST /api/v1/releases
Tag a release (deprecated).
Deprecated since 2026-09-27, removed on 2027-03-31.
Use POST /versions.
Body (application/json): ReleaseIn.
Responses: 201 ReleaseOut, 400 Error, 401, 403, 409 Error, 422, 429.
GET /api/v1/releases/{release_id}
Get a release (deprecated).
Deprecated since 2026-09-27, removed on 2027-03-31.
Use GET /versions/{id}.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
release_id |
path | string (uuid) | yes |
Responses: 200 ReleaseOut, 401, 403, 404, 422, 429.
PATCH /api/v1/releases/{release_id}
Update a release (deprecated).
Deprecated since 2026-09-27, removed on 2027-03-31.
Use PATCH /versions/{id}.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
release_id |
path | string (uuid) | yes |
Body (application/json): ReleasePatch.
Responses: 200 ReleaseOut, 400 Error, 401, 403, 404, 409 Error, 422, 429.
DELETE /api/v1/releases/{release_id}
Delete a release (deprecated, admin).
Deprecated since 2026-09-27, removed on 2027-03-31.
Use DELETE /versions/{id}.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
release_id |
path | string (uuid) | yes |
Responses: 204, 401, 403, 404, 422, 429.
POST /api/v1/releases/{release_id}/promote
Promote a release (deprecated).
Deprecated since 2026-09-27, removed on 2027-03-31.
Use POST /versions/{id}/promote.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
release_id |
path | string (uuid) | yes |
Body (application/json): PromoteIn.
Responses: 201 ReleaseOut, 400 Error, 401, 403, 404, 409 Error, 422, 429.
GET /api/v1/versions
List versions.
Newest first. environment (slug, name or id) keeps one environment, tag the versions carrying a tag, q a part of the label, git ref or sha.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
environment |
query | string | no | |
tag |
query | string | no | |
q |
query | string | no | |
limit |
query | integer | no | 50 |
offset |
query | integer | no | 0 |
Responses: 200 Page_VersionOut_, 401, 403, 422, 429.
POST /api/v1/versions
Create a version.
A version of your code in one environment, from deployed_at (default now). Runs of that environment are attributed to it until the next version. 409 already_exists when the label exists in the environment. CI can instead send version when it triggers a run.
Body (application/json): VersionIn.
Responses: 201 VersionOut, 400 Error, 401, 403, 409 Error, 422, 429.
GET /api/v1/versions/{version_id}
Get a version.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
version_id |
path | string (uuid) | yes |
Responses: 200 VersionOut, 401, 403, 404, 422, 429.
PATCH /api/v1/versions/{version_id}
Update a version.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
version_id |
path | string (uuid) | yes |
Body (application/json): VersionPatch.
Responses: 200 VersionOut, 400 Error, 401, 403, 404, 409 Error, 422, 429.
DELETE /api/v1/versions/{version_id}
Delete a version (admin).
Its runs are kept; they fall back to the previous version of the environment.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
version_id |
path | string (uuid) | yes |
Responses: 204, 401, 403, 404, 422, 429.
POST /api/v1/versions/{version_id}/promote
Promote a version to another environment.
The same code in the next environment (staging to production), from deployed_at (default now): label, git fields, notes, number and tags travel with it. 409 already_exists when the label is already there.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
version_id |
path | string (uuid) | yes |
Body (application/json): VersionPromoteIn.
Responses: 201 VersionOut, 400 Error, 401, 403, 404, 409 Error, 422, 429.
Environments
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/environments |
List environments | read |
admin, member, guest | 200 |
POST |
/api/v1/environments |
Create an environment | write |
admin, member | 201 |
GET |
/api/v1/environments/compare |
Compare environments (Growth and up) | read |
admin, member, guest | 200 |
GET |
/api/v1/environments/stability |
Stability per environment (Growth and up) | read |
admin, member, guest | 200 |
GET |
/api/v1/environments/{env_id} |
Get an environment | read |
admin, member, guest | 200 |
PATCH |
/api/v1/environments/{env_id} |
Update an environment | write |
admin, member | 200 |
DELETE |
/api/v1/environments/{env_id} |
Delete an environment (admin) | admin |
admin | 204 |
PUT |
/api/v1/environments/{env_id}/numbers |
Set the number dialed in an environment | write |
admin, member | 200 |
GET |
/api/v1/environments/{env_id}/scenarios |
Scenarios of an environment | read |
admin, member, guest | 200 |
POST |
/api/v1/environments/{env_id}/scenarios |
Bind, pause or unbind many scenarios | write |
admin, member | 200 |
GET |
/api/v1/matrix |
What runs where, and what passes | read |
admin, member, guest | 200 |
GET |
/api/v1/scenarios/{scenario_id}/environments |
Environments of a scenario | read |
admin, member, guest | 200 |
PUT |
/api/v1/scenarios/{scenario_id}/environments/{env_id} |
Activate or schedule a scenario in an environment | write |
admin, member | 200 |
DELETE |
/api/v1/scenarios/{scenario_id}/environments/{env_id} |
Remove a scenario from an environment | write |
admin, member | 204 |
GET /api/v1/environments
List environments.
In promotion order. Every organization has one default environment (is_default), used by runs and versions that name none.
Responses: 200 list of EnvironmentOut, 401, 403, 422, 429.
POST /api/v1/environments
Create an environment.
Body (application/json): EnvironmentIn.
Responses: 201 EnvironmentOut, 400 Error, 401, 403, 409 Error, 422, 429.
GET /api/v1/environments/compare
Compare environments (Growth and up).
Side a vs side b (for example a=staging&b=production): overall KPIs, per scenario, and the test cases whose last verdict differs. Narrow a side to one version with a_version / b_version (version id), and to one scenario with scenario_id. Without a version, a side covers the last days days. a_release / b_release are the deprecated names of a_version / b_version.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
a |
query | string | yes | |
b |
query | string | yes | |
a_version |
query | string (uuid) | no | |
b_version |
query | string (uuid) | no | |
scenario_id |
query | string (uuid) | no | |
days |
query | integer | no | 30 |
a_release |
query | string (uuid) | no | |
b_release |
query | string (uuid) | no |
Responses: 200 CompareOut, 400 Error, 401, 403 Error, 422, 429.
GET /api/v1/environments/stability
Stability per environment (Growth and up).
Over the last days (7, 30 or 90): tests, pass rate, average score, p95 latency, flaky test cases and last run of each environment.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
days |
query | integer | no | 30 |
Responses: 200 list of StabilityOut, 401, 403 Error, 422, 429.
GET /api/v1/environments/{env_id}
Get an environment.
By id or slug.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
env_id |
path | string | yes |
Responses: 200 EnvironmentOut, 401, 403, 404, 422, 429.
PATCH /api/v1/environments/{env_id}
Update an environment.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
env_id |
path | string | yes |
Body (application/json): EnvironmentPatch.
Responses: 200 EnvironmentOut, 400 Error, 401, 403, 404, 409 Error, 422, 429.
DELETE /api/v1/environments/{env_id}
Delete an environment (admin).
Refused (409 environment_in_use) while runs or versions belong to it, and for the default environment (409 default_required).
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
env_id |
path | string | yes |
Responses: 204, 401, 403, 404, 409 Error, 422, 429.
PUT /api/v1/environments/{env_id}/numbers
Set the number dialed in an environment.
Runs of this environment dial number_id instead of target_id. Both are numbers of your organization, each verified on its own.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
env_id |
path | string | yes |
Body (application/json): NumberIn.
Responses: 200 EnvironmentOut, 400 Error, 401, 403, 404, 422, 429.
GET /api/v1/environments/{env_id}/scenarios
Scenarios of an environment.
The scenarios activated in (or bound to) this environment.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
env_id |
path | string | yes |
Responses: 200 list of BindingOut, 401, 403, 404, 422, 429.
POST /api/v1/environments/{env_id}/scenarios
Bind, pause or unbind many scenarios.
One call for a multi-select or a drag and drop: enable, disable or remove the listed scenarios in this environment, optionally with the same schedule, timezone, call_window for all. An id of another organization answers 404 and nothing changes.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
env_id |
path | string | yes |
Body (application/json): BulkBindingIn.
Responses: 200 BulkBindingOut, 400 Error, 401, 403, 404, 422, 429.
GET /api/v1/matrix
What runs where, and what passes.
For every scenario x environment: activated or not, schedule, last completed run (verdict, score, version), trend of the last trend_runs runs; for the last versions versions of each environment: the scenarios that pass, warn, fail or were not tested. Filters: environment and tag (repeat them), scenario_id (repeat), q (part of a scenario name), bound_only.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
environment |
query | list of string | no | |
tag |
query | list of string | no | |
scenario_id |
query | list of string (uuid) | no | |
q |
query | string | no | "" |
bound_only |
query | boolean | no | false |
trend_runs |
query | integer | no | 5 |
versions |
query | integer | no | 5 |
Responses: 200 MatrixOut, 401, 403, 422, 429.
GET /api/v1/scenarios/{scenario_id}/environments
Environments of a scenario.
Where the scenario runs, with the schedule, the call window and the number dialed in each environment.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes |
Responses: 200 list of BindingOut, 401, 403, 404, 422, 429.
PUT /api/v1/scenarios/{scenario_id}/environments/{env_id}
Activate or schedule a scenario in an environment.
Creates the binding when needed. Fields left out keep their value. Each environment has its own schedule and window.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes | |
env_id |
path | string | yes |
Body (application/json): BindingIn.
Responses: 200 BindingOut, 400 Error, 401, 403, 404, 422, 429.
DELETE /api/v1/scenarios/{scenario_id}/environments/{env_id}
Remove a scenario from an environment.
Deletes the binding and its schedule. Past runs are kept.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes | |
env_id |
path | string | yes |
Responses: 204, 401, 403, 404, 422, 429.
Webhooks
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/webhooks |
List webhook endpoints | read |
admin, member | 200 |
POST |
/api/v1/webhooks |
Create a webhook endpoint | admin |
admin | 201 |
GET |
/api/v1/webhooks/{webhook_id} |
Get a webhook endpoint | read |
admin, member | 200 |
PATCH |
/api/v1/webhooks/{webhook_id} |
Update a webhook endpoint | admin |
admin | 200 |
DELETE |
/api/v1/webhooks/{webhook_id} |
Delete a webhook endpoint | admin |
admin | 204 |
GET |
/api/v1/webhooks/{webhook_id}/deliveries |
Delivery log of an endpoint | read |
admin, member | 200 |
POST |
/api/v1/webhooks/{webhook_id}/rotate-secret |
Rotate the signing secret | admin |
admin | 200 |
POST |
/api/v1/webhooks/{webhook_id}/test |
Send a test event now | admin |
admin | 200 |
GET /api/v1/webhooks
List webhook endpoints.
Payloads are JSON {id, type, created, data} signed with HMAC-SHA256:
CallQA-Signature: t=<unix ts>,v1=<hex hmac_sha256(secret, "<ts>.<raw body>")>
During a secret rotation the header carries one v1 per valid secret: accept the delivery if any v1 matches. Verify in constant time and reject timestamps older than 5 minutes. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 6 h.
Events: call.completed, call.failed, call.blocked, call.regressed, call.recovered, suite_run.started, suite_run.completed, suite_run.failed, score.dropped, version.created, version.promoted, release.created, release.promoted, wallet.low_balance
Responses: 200 list of WebhookOut, 401, 403, 422, 429.
POST /api/v1/webhooks
Create a webhook endpoint.
Payloads are JSON {id, type, created, data} signed with HMAC-SHA256:
CallQA-Signature: t=<unix ts>,v1=<hex hmac_sha256(secret, "<ts>.<raw body>")>
During a secret rotation the header carries one v1 per valid secret: accept the delivery if any v1 matches. Verify in constant time and reject timestamps older than 5 minutes. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 6 h.
Events: call.completed, call.failed, call.blocked, call.regressed, call.recovered, suite_run.started, suite_run.completed, suite_run.failed, score.dropped, version.created, version.promoted, release.created, release.promoted, wallet.low_balance
Body (application/json): WebhookIn.
Responses: 201 WebhookCreated, 400 Error, 401, 403, 422, 429.
GET /api/v1/webhooks/{webhook_id}
Get a webhook endpoint.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
webhook_id |
path | string (uuid) | yes |
Responses: 200 WebhookOut, 401, 403, 404, 422, 429.
PATCH /api/v1/webhooks/{webhook_id}
Update a webhook endpoint.
Change the URL, the events, the description, or pause it (enabled: false). The signing secret does not change: use rotate-secret.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
webhook_id |
path | string (uuid) | yes |
Body (application/json): WebhookPatch.
Responses: 200 WebhookOut, 400 Error, 401, 403, 404, 422, 429.
DELETE /api/v1/webhooks/{webhook_id}
Delete a webhook endpoint.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
webhook_id |
path | string (uuid) | yes |
Responses: 204, 401, 403, 404, 422, 429.
GET /api/v1/webhooks/{webhook_id}/deliveries
Delivery log of an endpoint.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
webhook_id |
path | string (uuid) | yes | |
limit |
query | integer | no | 50 |
offset |
query | integer | no | 0 |
Responses: 200 Page_DeliveryOut_, 401, 403, 404, 422, 429.
POST /api/v1/webhooks/{webhook_id}/rotate-secret
Rotate the signing secret.
Returns the new secret (shown once). During grace_period_hours (default 24, at most 168) every delivery is signed with both secrets (two v1 in CallQA-Signature), so you can deploy the new one without losing events.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
webhook_id |
path | string (uuid) | yes |
Body (application/json): SecretRotateIn.
Responses: 200 WebhookCreated, 400 Error, 401, 403, 404, 422, 429.
POST /api/v1/webhooks/{webhook_id}/test
Send a test event now.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
webhook_id |
path | string (uuid) | yes |
Responses: 200 DeliveryOut, 401, 403, 404, 422, 429.
Alerts
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/alert-channels |
List alert channels | admin |
admin | 200 |
POST |
/api/v1/alert-channels |
Create an alert channel | admin |
admin | 201 |
GET |
/api/v1/alert-channels/{channel_id} |
Get an alert channel | admin |
admin | 200 |
PATCH |
/api/v1/alert-channels/{channel_id} |
Update an alert channel | admin |
admin | 200 |
DELETE |
/api/v1/alert-channels/{channel_id} |
Delete an alert channel | admin |
admin | 204 |
POST |
/api/v1/alert-channels/{channel_id}/test |
Send a test alert | admin |
admin | 200 |
GET /api/v1/alert-channels
List alert channels.
Alert channels warn people in Slack (incoming webhook) or by email when a run run completes and one of the channel's rules matches: the run failed (on_failure), its score is under on_score_below, or a call's p95 latency is above on_latency_p95_above_ms. One message per channel and run at most, sent within seconds by the dispatcher and retried after 1, 5 and 30 minutes.
The Slack webhook URL is a secret: it must be an https://hooks.slack.com/ URL, and it is never returned in full. For machine integrations, use webhooks.
Responses: 200 list of AlertChannelOut, 401, 403, 422, 429.
POST /api/v1/alert-channels
Create an alert channel.
Alert channels warn people in Slack (incoming webhook) or by email when a run run completes and one of the channel's rules matches: the run failed (on_failure), its score is under on_score_below, or a call's p95 latency is above on_latency_p95_above_ms. One message per channel and run at most, sent within seconds by the dispatcher and retried after 1, 5 and 30 minutes.
The Slack webhook URL is a secret: it must be an https://hooks.slack.com/ URL, and it is never returned in full. For machine integrations, use webhooks.
Body (application/json): AlertChannelIn.
Responses: 201 AlertChannelOut, 400 Error, 401, 403, 422, 429.
GET /api/v1/alert-channels/{channel_id}
Get an alert channel.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
channel_id |
path | integer | yes |
Responses: 200 AlertChannelOut, 401, 403, 404, 422, 429.
PATCH /api/v1/alert-channels/{channel_id}
Update an alert channel.
Only the fields you send change. null on a threshold removes that rule. The kind cannot change.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
channel_id |
path | integer | yes |
Body (application/json): AlertChannelPatch.
Responses: 200 AlertChannelOut, 400 Error, 401, 403, 404, 422, 429.
DELETE /api/v1/alert-channels/{channel_id}
Delete an alert channel.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
channel_id |
path | integer | yes |
Responses: 204, 401, 403, 404, 422, 429.
POST /api/v1/alert-channels/{channel_id}/test
Send a test alert.
Sends a test message now and tells whether it went through. Limited to 5 per minute per channel.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
channel_id |
path | integer | yes |
Responses: 200 AlertTestOut, 401, 403, 404, 422, 429.
API keys & logs
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/api-keys |
List API keys | admin |
admin | 200 |
POST |
/api/v1/api-keys |
Create an API key | admin |
admin | 201 |
GET |
/api/v1/api-keys/{key_id} |
Get an API key | admin |
admin | 200 |
PATCH |
/api/v1/api-keys/{key_id} |
Rename or restrict an API key | admin |
admin | 200 |
DELETE |
/api/v1/api-keys/{key_id} |
Revoke an API key | admin |
admin | 204 |
POST |
/api/v1/api-keys/{key_id}/rotate |
Rotate an API key | admin |
admin | 201 |
GET |
/api/v1/api-logs |
API request journal | read |
admin, member | 200 |
GET /api/v1/api-keys
List API keys.
Responses: 200 list of ApiKeyOut, 401, 403, 422, 429.
POST /api/v1/api-keys
Create an API key.
The full key is returned once; only a hash is stored. allowed_cidrs restricts where it can be used from.
Body (application/json): ApiKeyIn.
Responses: 201 ApiKeyCreated, 400 Error, 401, 403, 422, 429.
GET /api/v1/api-keys/{key_id}
Get an API key.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
key_id |
path | integer | yes |
Responses: 200 ApiKeyOut, 401, 403, 404, 422, 429.
PATCH /api/v1/api-keys/{key_id}
Rename or restrict an API key.
Scopes cannot change: create a new key instead.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
key_id |
path | integer | yes |
Body (application/json): ApiKeyPatch.
Responses: 200 ApiKeyOut, 400 Error, 401, 403, 404, 422, 429.
DELETE /api/v1/api-keys/{key_id}
Revoke an API key.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
key_id |
path | integer | yes |
Responses: 204, 401, 403, 404, 422, 429.
POST /api/v1/api-keys/{key_id}/rotate
Rotate an API key.
Creates a replacement with the same name, scopes, networks and expiry, returned once. The old key keeps working for grace_period_hours (default 24, at most 168, 0 revokes it now), then expires: deploy the new one in between.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
key_id |
path | integer | yes |
Body (application/json): KeyRotateIn.
Responses: 201 ApiKeyCreated, 400 Error, 401, 403, 404, 422, 429.
GET /api/v1/api-logs
API request journal.
Every authenticated API request of your organization (method, path, status, duration, key, IP). Bodies are never stored.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
since |
query | string (date-time) | no | |
status_code |
query | integer | no | |
key_prefix |
query | string | no | |
limit |
query | integer | no | 100 |
offset |
query | integer | no | 0 |
Responses: 200 Page_ApiLogOut_, 401, 403, 422, 429.
Exports for LLMs
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/calls/{call_id}/llm-export |
Export a call for an LLM | read |
admin, member, guest | 200 |
GET |
/api/v1/runs/{run_id}/llm-export |
Export a run for an LLM | read |
admin, member, guest | 200 |
GET |
/api/v1/scenarios/{scenario_id}/llm-export |
Export a scenario for an LLM | read |
admin, member, guest | 200 |
GET /api/v1/calls/{call_id}/llm-export
Export a call for an LLM.
A report made to be pasted into an LLM (a chat assistant or a coding agent) that will fix your voice agent: the scenario, the results, every failed check with the judge's quote, a readable transcript (AGENT = your agent, CALLER = our test caller), metrics, the judge's recommendations and a primer asking for a fix.
format=markdown (default) answers text/markdown; format=json answers the same document as compact JSON (format: "callqa.llm_report", version, level).
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
call_id |
path | string (uuid) | yes | |
format |
query | string | no | "markdown" |
Responses: 200 object, 401, 403, 404, 422, 429.
GET /api/v1/runs/{run_id}/llm-export
Export a run for an LLM.
A report made to be pasted into an LLM (a chat assistant or a coding agent) that will fix your voice agent: the scenario, the results, every failed check with the judge's quote, a readable transcript (AGENT = your agent, CALLER = our test caller), metrics, the judge's recommendations and a primer asking for a fix.
format=markdown (default) answers text/markdown; format=json answers the same document as compact JSON (format: "callqa.llm_report", version, level). The failed calls (up to 5) are detailed.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
run_id |
path | string (uuid) | yes | |
format |
query | string | no | "markdown" |
Responses: 200 object, 401, 403, 404, 422, 429.
GET /api/v1/scenarios/{scenario_id}/llm-export
Export a scenario for an LLM.
A report made to be pasted into an LLM (a chat assistant or a coding agent) that will fix your voice agent: the scenario, the results, every failed check with the judge's quote, a readable transcript (AGENT = your agent, CALLER = our test caller), metrics, the judge's recommendations and a primer asking for a fix.
format=markdown (default) answers text/markdown; format=json answers the same document as compact JSON (format: "callqa.llm_report", version, level). Covers the scenario YAML, its last 5 runs, the checks that fail most and the last 3 failed calls.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
scenario_id |
path | string (uuid) | yes | |
format |
query | string | no | "markdown" |
Responses: 200 object, 401, 403, 404, 422, 429.
Billing
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/billing/forecast |
Forecast of the month | read |
admin, member, guest | 200 |
POST |
/api/v1/billing/portal |
Open the billing portal | admin |
admin | 200 |
GET |
/api/v1/billing/referral |
Your referral link | read |
admin, member, guest | 200 |
GET |
/api/v1/billing/settings |
Overage, spend cap and alerts | admin |
admin | 200 |
PATCH |
/api/v1/billing/settings |
Change overage, the spend cap or the low balance threshold (admin) | admin |
admin | 200 |
GET |
/api/v1/billing/subscription |
Current subscription | admin |
admin | 200 |
POST |
/api/v1/billing/subscription/checkout |
Subscribe to a plan | admin |
admin | 200 |
GET |
/api/v1/plans |
Public plans | read |
admin, member, guest | 200 |
GET |
/api/v1/usage |
Quota usage this month | read |
admin, member, guest | 200 |
GET |
/api/v1/wallet |
Wallet balance | admin |
admin | 200 |
POST |
/api/v1/wallet/checkout |
Top up the wallet | admin |
admin | 200 |
GET |
/api/v1/wallet/transactions |
Wallet ledger | admin |
admin | 200 |
GET /api/v1/billing/forecast
Forecast of the month.
What the enabled schedules will consume until the quota resets (1st of next month, UTC), per schedule and environment: runs left x test cases x destination weight x average 10 minute slices observed over 30 days (maximum duration when a scenario never ran). Compared with the quota, bonus calls, overage settings and cap. Manual, API and CI runs are not forecast.
Responses: 200 ForecastOut, 401, 403, 422, 429.
POST /api/v1/billing/portal
Open the billing portal.
Returns a Stripe billing portal URL: change or cancel the plan, update the payment method, download invoices.
Body (application/json): PortalIn.
Responses: 200 CheckoutOut, 400 Error, 401, 403, 422, 429, 503 Error.
GET /api/v1/billing/referral
Your referral link.
Both organizations get the bonus calls when the referred one starts its first paid subscription. Referring yourself (a member of your organization, the same Stripe customer) or an existing customer gives nothing.
Responses: 200 ReferralOut, 401, 403, 422, 429.
GET /api/v1/billing/settings
Overage, spend cap and alerts.
Paid overage on/off, its monthly cap (wallet spend), what happens at the limit, and the wallet.low_balance threshold.
Responses: 200 BillingSettingsOut, 401, 403, 422, 429.
PATCH /api/v1/billing/settings
Change overage, the spend cap or the low balance threshold (admin).
Only the fields you send change; null removes the cap or turns wallet.low_balance off. When the cap is reached, new calls are not placed (blocked_insufficient_credit, block reason spend_cap_reached) until the next month or a higher cap. allow_paid_overage: false stops calls once the quota and bonus calls are used (overage_disabled); on_limit: pause_schedules also pauses the schedules until the 1st of next month.
Body (application/json): BillingSettingsPatch.
Responses: 200 BillingSettingsOut, 401, 403, 422, 429.
GET /api/v1/billing/subscription
Current subscription.
Responses: 200 SubscriptionOut, 401, 403, 422, 429.
POST /api/v1/billing/subscription/checkout
Subscribe to a plan.
Returns a Stripe Checkout URL for a first subscription. If your organization already has an active Stripe subscription the answer is 409 (subscription_exists): change plan, interval or payment method in the billing portal (POST /billing/portal).
Body (application/json): SubscriptionCheckoutIn.
Responses: 200 CheckoutOut, 400 Error, 401, 403, 409 Error, 422, 429, 503 Error.
GET /api/v1/plans
Public plans.
Plans you can subscribe to with POST /billing/subscription/checkout.
Responses: 200 list of PlanPublicOut, 401, 403, 422, 429.
GET /api/v1/usage
Quota usage this month.
Quota periods are calendar months (UTC). held = units reserved by calls in progress.
Responses: 200 UsageOut, 401, 403, 422, 429.
GET /api/v1/wallet
Wallet balance.
Calls beyond the monthly quota are paid from a prepaid wallet, at your plan's overage price times the destination weight. Credit is reserved before a call is dialed and the unused part is released after. When the wallet cannot cover a call, the call is not placed and ends with status blocked_insufficient_credit: top up with POST /wallet/checkout. Top-ups are between $20 and $5,000 (amount_cents 2000 to 500000).
Responses: 200 WalletOut, 401, 403, 422, 429.
POST /api/v1/wallet/checkout
Top up the wallet.
Calls beyond the monthly quota are paid from a prepaid wallet, at your plan's overage price times the destination weight. Credit is reserved before a call is dialed and the unused part is released after. When the wallet cannot cover a call, the call is not placed and ends with status blocked_insufficient_credit: top up with POST /wallet/checkout. Top-ups are between $20 and $5,000 (amount_cents 2000 to 500000).
Returns a Stripe Checkout URL. The wallet is credited when Stripe confirms the payment (webhook), not before.
Body (application/json): CheckoutIn.
Responses: 200 CheckoutOut, 400 Error, 401, 403, 422, 429, 503 Error.
GET /api/v1/wallet/transactions
Wallet ledger.
Append-only ledger: every balance change has a line.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
limit |
query | integer | no | 50 |
offset |
query | integer | no | 0 |
Responses: 200 Page_WalletTxOut_, 401, 403, 422, 429.
Support
| Method | Path | Summary | Scope | Roles | Success |
|---|---|---|---|---|---|
GET |
/api/v1/support/tickets |
List support tickets | read |
admin, member, guest | 200 |
POST |
/api/v1/support/tickets |
Open a support ticket | read |
admin, member | 201 |
GET |
/api/v1/support/tickets/{ticket_id} |
Get a support ticket | read |
admin, member, guest | 200 |
POST |
/api/v1/support/tickets/{ticket_id}/close |
Close a support ticket | read |
admin, member | 200 |
POST |
/api/v1/support/tickets/{ticket_id}/messages |
Reply to a support ticket | read |
admin, member | 201 |
GET /api/v1/support/tickets
List support tickets.
Ask the CallQA team: a bug, a question, feedback, a billing matter or an abuse report. A person of the team answers, usually within a few hours on business days; the answer is emailed to the author (and to the organization's admins when an API key opened the ticket) and readable here. Statuses: open (the team has to answer), waiting_customer (the team waits for you), answered, closed. A reply reopens a ticket.
Never paste a secret (API key, password, token): anything that looks like one is replaced by [redacted secret] before it is stored.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
status |
query | string | no | |
limit |
query | integer | no | 20 |
offset |
query | integer | no | 0 |
Responses: 200 Page_TicketOut_, 401, 403, 422, 429.
POST /api/v1/support/tickets
Open a support ticket.
Ask the CallQA team: a bug, a question, feedback, a billing matter or an abuse report. A person of the team answers, usually within a few hours on business days; the answer is emailed to the author (and to the organization's admins when an API key opened the ticket) and readable here. Statuses: open (the team has to answer), waiting_customer (the team waits for you), answered, closed. A reply reopens a ticket.
Never paste a secret (API key, password, token): anything that looks like one is replaced by [redacted secret] before it is stored.
At most 20 new tickets a day per organization (429 rate_limited beyond).
Body (application/json): TicketIn.
Responses: 201 TicketDetail, 400 Error, 401, 403, 422 Error, 429.
GET /api/v1/support/tickets/{ticket_id}
Get a support ticket.
The ticket and every message, oldest first: the answers of the CallQA team have author staff.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
ticket_id |
path | string (uuid) | yes |
Responses: 200 TicketDetail, 401, 403, 404, 422, 429.
POST /api/v1/support/tickets/{ticket_id}/close
Close a support ticket.
When the matter is solved. A later reply reopens it.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
ticket_id |
path | string (uuid) | yes |
Responses: 200 TicketDetail, 401, 403, 404, 422, 429.
POST /api/v1/support/tickets/{ticket_id}/messages
Reply to a support ticket.
Adds a message and reopens the ticket (status open): the team is alerted. Never paste a secret.
| Parameter | In | Type | Required | Default |
|---|---|---|---|---|
ticket_id |
path | string (uuid) | yes |
Body (application/json): MessageIn.
Responses: 201 TicketDetail, 400 Error, 401, 403, 404, 422, 429.
Schemas
Request and response objects, as named above. Full definitions in api/openapi.json under components.schemas.
ActiveCode
| Field | Type | Required | Description |
|---|---|---|---|
attempt_id |
string (uuid) | yes | |
method |
string | yes | |
status |
pending | calling |
yes | |
expires_at |
string (date-time) | yes | |
call_id |
string (uuid) | yes | |
call_status |
string | yes | |
dtmf_gate |
string | no | Keys pressed after the answer. |
AlertChannelIn
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | |
kind |
slack | email |
yes | |
webhook_url |
string | no | Slack only: incoming webhook URL (https://hooks.slack.com/services/...). |
to |
list of string | no | Email only: up to 10 recipients. |
on_failure |
boolean | no | Alert when a run fails. |
on_score_below |
integer | no | Alert when the run score is under this. |
on_latency_p95_above_ms |
integer | no | Alert when a call's p95 latency is above this. |
enabled |
boolean | no |
AlertChannelOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer | yes | |
name |
string | yes | |
kind |
slack | email |
yes | |
config |
object | yes | Slack: {webhook_url} masked (it is a secret). Email: {to}. |
on_failure |
boolean | yes | |
on_score_below |
integer | yes | |
on_latency_p95_above_ms |
integer | yes | |
enabled |
boolean | yes | |
created_at |
string (date-time) | yes |
AlertChannelPatch
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | |
webhook_url |
string | no | |
to |
list of string | no | |
on_failure |
boolean | no | |
on_score_below |
integer | no | null removes the rule. |
on_latency_p95_above_ms |
integer | no | null removes the rule. |
enabled |
boolean | no |
AlertTestOut
| Field | Type | Required | Description |
|---|---|---|---|
sent |
boolean | yes | |
error |
string | yes |
ApiKeyCreated
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer | yes | |
name |
string | yes | |
prefix |
string | yes | |
scopes |
list of string | yes | |
allowed_cidrs |
list of string | yes | |
created_at |
string (date-time) | yes | |
expires_at |
string (date-time) | yes | |
last_used_at |
string (date-time) | yes | |
last_used_ip |
string | yes | |
revoked_at |
string (date-time) | yes | |
rotated_from_id |
integer | yes | |
key |
string | yes | The API key. Shown only once: store it in your secrets. |
ApiKeyIn
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | |
scopes |
list of string | no | |
expires_in_days |
integer | no | |
allowed_cidrs |
list of string | no | Only accept requests from these networks (IPv4 or IPv6, CIDR or single address, at most 20). Empty: any address. |
ApiKeyOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer | yes | |
name |
string | yes | |
prefix |
string | yes | |
scopes |
list of string | yes | |
allowed_cidrs |
list of string | yes | |
created_at |
string (date-time) | yes | |
expires_at |
string (date-time) | yes | |
last_used_at |
string (date-time) | yes | |
last_used_ip |
string | yes | |
revoked_at |
string (date-time) | yes | |
rotated_from_id |
integer | yes |
ApiKeyPatch
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | |
allowed_cidrs |
list of string | no | Replaces the list. [] removes the restriction. |
ApiLogOut
| Field | Type | Required | Description |
|---|---|---|---|
method |
string | yes | |
path |
string | yes | |
status_code |
integer | yes | |
duration_ms |
integer | yes | |
api_key_prefix |
string | yes | |
ip |
string | yes | |
request_id |
string | yes | |
created_at |
string (date-time) | yes |
AttestIn
| Field | Type | Required | Description |
|---|---|---|---|
accept |
boolean | yes | Must be true: you accept the attestation text. |
signer_name |
string | yes | Full name and title of the person attesting. |
AuditOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer | yes | |
action |
string | yes | |
actor_email |
string | yes | The person, for actions done in the dashboard. |
actor_api_key_prefix |
string | yes | The API key, for API actions. |
target_type |
string | yes | |
target_id |
string | yes | |
data |
object | yes | |
ip |
string | yes | |
created_at |
string (date-time) | yes |
BillingSettingsOut
| Field | Type | Required | Description |
|---|---|---|---|
monthly_spend_cap_cents |
integer | yes | Most the wallet may spend per calendar month (UTC). null: no cap. |
spent_this_month_cents |
integer | yes | Wallet spend this month, reservations of calls in progress included. |
low_balance_threshold_cents |
integer | yes | wallet.low_balance is sent when the balance falls under this. null: never. |
allow_paid_overage |
boolean | yes | Calls beyond the monthly quota and bonus calls are paid from the wallet. false: they are not placed (block reason overage_disabled). |
on_limit |
block_calls | pause_schedules |
yes | When the limit is reached (overage off, cap reached, no overage on the plan): block each call, or also pause the schedules until the 1st of next month (UTC). |
schedules_paused |
boolean | yes | The schedules are paused by this rule now. |
BillingSettingsPatch
| Field | Type | Required | Description |
|---|---|---|---|
monthly_spend_cap_cents |
integer | no | |
low_balance_threshold_cents |
integer | no | |
allow_paid_overage |
boolean | no | Admins only (billing.overage). |
on_limit |
string | no |
BindingIn
| Field | Type | Required | Description |
|---|---|---|---|
enabled |
boolean | no | Run the scenario in this environment. A new binding is enabled unless false. |
schedule |
string | no | Cron expression or every day at 09:00. "" removes the schedule; absent keeps it. |
timezone |
string | no | IANA zone of the schedule. Absent keeps it (or the one written in schedule). |
call_window |
string | no | HH:MM-HH:MM Area/City; scheduled calls wait for it. Without a zone, the schedule's zone. "" removes it. |
schedule_enabled |
boolean | no | Pause (false) or resume the schedule without losing it. |
BindingOut
| Field | Type | Required | Description |
|---|---|---|---|
scenario_id |
string (uuid) | yes | |
scenario_name |
string | yes | |
environment |
string | yes | Environment slug. |
enabled |
boolean | yes | |
schedule |
string | yes | |
cron |
string | yes | The schedule as a cron expression, in timezone. |
timezone |
string | yes | |
call_window |
string | yes | |
schedule_enabled |
boolean | yes | |
next_run_at |
string (date-time) | yes | |
last_run_at |
string (date-time) | yes | |
number_phone |
string | yes | The number dialed in this environment. |
BulkBindingIn
| Field | Type | Required | Description |
|---|---|---|---|
enabled |
boolean | no | Run the scenario in this environment. A new binding is enabled unless false. |
schedule |
string | no | Cron expression or every day at 09:00. "" removes the schedule; absent keeps it. |
timezone |
string | no | IANA zone of the schedule. Absent keeps it (or the one written in schedule). |
call_window |
string | no | HH:MM-HH:MM Area/City; scheduled calls wait for it. Without a zone, the schedule's zone. "" removes it. |
schedule_enabled |
boolean | no | Pause (false) or resume the schedule without losing it. |
scenario_ids |
list of string (uuid) | yes | |
action |
enable | disable | remove |
yes | enable: activate (created if needed); disable: keep the binding and its schedule but do not run; remove: delete the binding. |
BulkBindingOut
| Field | Type | Required | Description |
|---|---|---|---|
action |
string | yes | |
environment |
string | yes | |
scenarios |
integer | yes | |
bindings |
list of BindingOut | yes |
CallDetailOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
run_id |
string (uuid) | yes | |
variant_id |
string (uuid) | yes | |
title |
string | yes | |
target_phone |
string | yes | |
status |
string | yes | |
block_reason |
string | yes | |
error |
string | yes | |
verdict |
string | yes | |
score |
integer | yes | |
duration_ms |
integer | yes | |
latency_p50_ms |
integer | yes | |
latency_p95_ms |
integer | yes | |
latency_max_ms |
integer | yes | |
cost_cents |
integer | yes | |
quota_units |
integer | yes | |
replay_of |
string (uuid) | yes | |
created_at |
string (date-time) | yes | |
dispatched_at |
string (date-time) | yes | |
completed_at |
string (date-time) | yes | |
content_purged_at |
string (date-time) | no | When the call's content (trace, transcript, detailed evaluation) was deleted at the end of the plan's retention period. Score, verdict, latencies and cost remain. |
run |
object | yes | The ResolvedRun that was dialed. |
result |
object | yes | CallResult: verdicts, metrics, score. |
transcript |
list of TurnOut | yes | |
recording |
object | yes | |
usage |
object | yes | |
cost_breakdown |
object | no | Our measured cost of the call in USD (telephony, STT, TTS, LLM, judge). Not what you are charged: see cost_cents. |
CallOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
run_id |
string (uuid) | yes | |
variant_id |
string (uuid) | yes | |
title |
string | yes | |
target_phone |
string | yes | |
status |
string | yes | |
block_reason |
string | yes | |
error |
string | yes | |
verdict |
string | yes | |
score |
integer | yes | |
duration_ms |
integer | yes | |
latency_p50_ms |
integer | yes | |
latency_p95_ms |
integer | yes | |
latency_max_ms |
integer | yes | |
cost_cents |
integer | yes | |
quota_units |
integer | yes | |
replay_of |
string (uuid) | yes | |
created_at |
string (date-time) | yes | |
dispatched_at |
string (date-time) | yes | |
completed_at |
string (date-time) | yes | |
content_purged_at |
string (date-time) | no | When the call's content (trace, transcript, detailed evaluation) was deleted at the end of the plan's retention period. Score, verdict, latencies and cost remain. |
CheckoutIn
| Field | Type | Required | Description |
|---|---|---|---|
amount_cents |
integer | yes | Top-up amount in USD cents (2000 to 500000). |
success_url |
string | no | |
cancel_url |
string | no |
CheckoutOut
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | yes | Redirect the user to this Stripe Checkout page. |
CiOut
| Field | Type | Required | Description |
|---|---|---|---|
threshold |
integer | yes | |
passed |
boolean | yes | null while the run is not finished. |
CompareOut
| Field | Type | Required | Description |
|---|---|---|---|
a |
string | yes | |
b |
string | yes | |
kpis_a |
KpisOut | yes | |
kpis_b |
KpisOut | yes | |
scenarios |
list of ScenarioCompareOut | yes | |
verdict_changes |
list of VerdictDiffOut | yes |
ComplianceOut
| Field | Type | Required | Description |
|---|---|---|---|
status |
pending | allow | needs_review | block |
yes | Compliance review of the current content: only allow can be run. |
label |
string | yes | |
categories |
list of string | yes | |
reasons |
list of ComplianceReasonOut | yes | |
reviewed_at |
string | yes | |
decided_by_staff |
boolean | yes | |
message |
string | yes | What it means and what to do next. |
enforced |
boolean | yes |
ComplianceReasonOut
| Field | Type | Required | Description |
|---|---|---|---|
category |
string | yes | |
quote |
string | yes | Words of the scenario that caused the decision. |
explanation |
string | yes |
DeliveryOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
event_type |
string | yes | |
status |
string | yes | |
attempts |
integer | yes | |
last_status_code |
integer | yes | |
last_error |
string | yes | |
created_at |
string (date-time) | yes | |
delivered_at |
string (date-time) | yes |
DeriveIn
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer | no |
EnvironmentForecastOut
| Field | Type | Required | Description |
|---|---|---|---|
environment |
string | yes | |
units |
integer | yes | |
runs_left |
integer | yes | |
calls |
integer | yes |
EnvironmentIn
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | |
order |
integer | no | Promotion order, smallest first (development 10, staging 20, production 30). Default: after the last one. |
color |
string | no | |
is_default |
boolean | no | Runs that name no environment go here. |
EnvironmentNumberOut
| Field | Type | Required | Description |
|---|---|---|---|
target_id |
string (uuid) | yes | |
target_phone |
string | yes | |
number_id |
string (uuid) | yes | |
number_phone |
string | yes |
EnvironmentOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
name |
string | yes | |
slug |
string | yes | |
order |
integer | yes | |
color |
string | yes | |
is_default |
boolean | yes | |
numbers |
list of EnvironmentNumberOut | yes | |
created_at |
string (date-time) | yes |
EnvironmentPatch
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | |
order |
integer | no | |
color |
string | no | |
is_default |
boolean | no | Only true is accepted: make another environment the default to change it. |
Error
| Field | Type | Required | Description |
|---|---|---|---|
detail |
string | yes | Human readable explanation. Can change: do not parse it. |
code |
string | yes | Stable machine-readable code, e.g. missing_scope, feature_not_in_plan, not_found. See the errors guide. |
feature |
string | no | feature_not_in_plan: the plan feature that is missing, e.g. release_compare. |
required_plan |
string | no | Plan errors: the cheapest plan that includes the feature or lifts the limit. |
upgrade_url |
string | no | Plan errors: page where an admin upgrades in one click (Stripe, prorated), then comes back. |
EventOut
| Field | Type | Required | Description |
|---|---|---|---|
seq |
integer | yes | |
t_ms |
integer | yes | |
at |
string (date-time) | yes | |
type |
string | yes | |
data |
object | yes |
ExportOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
status |
queued | running | done | error | expired |
yes | |
created_at |
string (date-time) | yes | |
finished_at |
string (date-time) | yes | |
expires_at |
string (date-time) | yes | The archive is deleted at this date. |
size_bytes |
integer | yes | |
sha256 |
string | yes | SHA-256 of the zip archive. |
counts |
object | yes | Number of objects of each kind. |
error |
string | yes | |
download_url |
string | yes | Signed link valid 24 hours, no API key needed. Set when done. |
ForecastOut
| Field | Type | Required | Description |
|---|---|---|---|
period_start |
string | yes | |
period_end |
string | yes | First day of the next period (exclusive). |
included |
integer | yes | |
bonus_remaining |
integer | yes | |
used_units |
integer | yes | Used or reserved this month (quota, bonus, wallet). |
scheduled_units |
integer | yes | What the enabled schedules will use until the end of the month. |
projected_units |
integer | yes | |
will_exceed |
boolean | yes | The schedules will use more than the quota and bonus calls left. |
overage_units |
integer | yes | |
estimated_overage_cents |
integer | yes | Wallet cost of overage_units at your plan's price, uncapped. null: the plan has no paid overage. |
month_overage_cents |
integer | yes | Spent so far + estimated. |
blocked_units |
integer | yes | Units expected to be blocked: overage off, no overage on the plan, or the monthly cap. |
overage_allowed |
boolean | yes | |
spend_cap_cents |
integer | yes | |
spent_cents |
integer | yes | |
overage_price_cents |
integer | yes | |
level |
ok | warn | danger |
yes | |
schedules_paused |
boolean | yes | |
schedules |
list of ScheduleForecastOut | yes | |
environments |
list of EnvironmentForecastOut | yes |
FromBundleIn
| Field | Type | Required | Description |
|---|---|---|---|
call_id |
string (uuid) | no | Build the bundle from this call... |
bundle |
object | no | ...or pass a ReplayBundle. |
title |
string | no |
GenerateIn
| Field | Type | Required | Description |
|---|---|---|---|
description |
string | yes | What the voice service does, in plain words. |
phone |
string | yes | |
language |
string | no | |
count |
integer | no |
GenerationJobOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
kind |
propose | derive | edge_case |
yes | |
status |
queued | running | done | error |
yes | |
scenario_id |
string (uuid) | yes | |
call_id |
string (uuid) | yes | |
result |
object | yes | When done. propose: {scenarios: [...], rejected}; derive: {variants: [...], rejected}; edge_case: {variant: {...}}. |
error |
string | yes | |
usage |
object | yes | |
created_at |
string (date-time) | yes | |
finished_at |
string (date-time) | yes | |
url |
string | yes | Poll this URL until status is done or error. |
InvitationIn
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | yes | |
role |
admin | member | guest |
no |
KeyRotateIn
| Field | Type | Required | Description |
|---|---|---|---|
grace_period_hours |
integer | no | How long the old key keeps working. 0 revokes it now. |
KpisOut
| Field | Type | Required | Description |
|---|---|---|---|
tests |
integer | yes | |
passed |
integer | yes | |
failed |
integer | yes | |
warned |
integer | yes | |
pass_rate |
number | yes | |
avg_score |
number | yes | |
avg_latency_ms |
integer | yes | |
p95_latency_ms |
integer | yes | |
interruptions_per_call |
number | yes | |
cost_cents |
integer | yes |
LastAttempt
| Field | Type | Required | Description |
|---|---|---|---|
attempt_id |
string (uuid) | yes | |
method |
string | yes | |
status |
succeeded | failed | error | expired | calling |
yes | |
outcome |
string | yes | matched, not_heard, not_answered, wrong_code, call_not_placed... |
heard_digits |
string | yes | Digits we understood (the code is masked with * when it matched). |
sip_status |
integer | no | When the line never answered: the SIP final answer of the destination (480 unavailable, 408 no answer, 486 busy, 603 declined, 404 unknown number...). |
sip_reason |
string | no | The carrier's short reason for sip_status. |
failure_hint |
string | no | What to fix before the next attempt, in plain words (null when the attempt did not fail). Show it to the user as is. |
call_id |
string (uuid) | yes | |
finished_at |
string (date-time) | yes | |
dtmf_gate |
string | no | Keys pressed after the answer. |
MatrixCell
| Field | Type | Required | Description |
|---|---|---|---|
environment |
string | yes | |
bound |
boolean | yes | |
enabled |
boolean | yes | |
schedule |
string | yes | |
timezone |
string | yes | |
call_window |
string | yes | |
schedule_enabled |
boolean | yes | |
next_run_at |
string (date-time) | yes | |
running |
boolean | yes | A run is queued or in progress. |
last_run |
MatrixRun | yes | Last completed run. |
trend |
MatrixTrend | yes |
MatrixEntry
| Field | Type | Required | Description |
|---|---|---|---|
scenario_id |
string (uuid) | yes | |
name |
string | yes | |
run_id |
string (uuid) | yes | |
score |
integer | yes |
MatrixEnvironment
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
slug |
string | yes | |
name |
string | yes | |
color |
string | yes | |
order |
integer | yes | |
is_default |
boolean | yes |
MatrixOut
| Field | Type | Required | Description |
|---|---|---|---|
environments |
list of MatrixEnvironment | yes | |
scenarios |
list of MatrixScenario | yes | |
versions |
list of MatrixVersionRow | yes | The last versions of each environment, newest first per environment. |
totals |
object | yes | Per environment slug, over the enabled scenarios: last verdicts. |
MatrixRun
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
number |
integer | yes | |
created_at |
string (date-time) | yes | |
verdict |
pass | warn | fail |
yes | |
score |
integer | yes | |
total |
integer | yes | |
passed |
integer | yes | |
version |
MatrixVersion | yes |
MatrixScenario
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
name |
string | yes | |
target_phone |
string | yes | |
tags |
list of string | yes | |
cells |
list of MatrixCell | yes | One per environment, in environments order. |
MatrixTotals
| Field | Type | Required | Description |
|---|---|---|---|
passed |
integer | yes | |
warned |
integer | yes | |
failed |
integer | yes | |
untested |
integer | yes | |
running |
integer | yes |
MatrixTrend
| Field | Type | Required | Description |
|---|---|---|---|
scores |
list of integer | yes | Scores of the last completed runs, oldest first. |
direction |
string | yes |
MatrixVersion
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
label |
string | yes | |
git_sha |
string | yes | |
deployed_at |
string (date-time) | yes |
MatrixVersionRow
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
label |
string | yes | |
environment |
string | yes | |
deployed_at |
string (date-time) | yes | |
git_ref |
string | yes | |
git_sha |
string | yes | |
source_url |
string | yes | |
tags |
list of string | yes | |
passed |
list of MatrixEntry | yes | |
warned |
list of MatrixEntry | yes | |
failed |
list of MatrixEntry | yes | |
untested |
list of MatrixEntry | yes | Enabled in the environment, no run on this version. |
MeOut
| Field | Type | Required | Description |
|---|---|---|---|
org_id |
string (uuid) | yes | |
org_name |
string | yes | |
org_slug |
string | yes | |
api_key_prefix |
string | yes | |
scopes |
list of string | yes | |
plan |
PlanOut | yes | |
subscription_status |
string | yes | |
allowed_countries |
list of string | yes |
MemberOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer | yes | |
email |
string | yes | |
role |
admin | member | guest |
yes | |
joined_at |
string (date-time) | yes |
MessageIn
| Field | Type | Required | Description |
|---|---|---|---|
body |
string | yes | Markdown. |
MessageOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer | yes | |
author |
customer_human | customer_agent | staff |
yes | staff is the CallQA team. |
author_name |
string | yes | |
body |
string | yes | |
created_at |
string (date-time) | yes | |
read_by_customer |
boolean | yes | For a message of the team: a person of the organization opened it in the app. |
NumberIn
| Field | Type | Required | Description |
|---|---|---|---|
target_id |
string (uuid) | yes | The number the scenarios point to. |
number_id |
string (uuid) | yes | The number to dial instead in this environment (registered and verified); null or equal to target_id removes the mapping. |
Page_ApiLogOut_
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer | yes | Total number of matching items. |
limit |
integer | yes | |
offset |
integer | yes | |
items |
list of ApiLogOut | yes |
Page_AuditOut_
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer | yes | Total number of matching items. |
limit |
integer | yes | |
offset |
integer | yes | |
items |
list of AuditOut | yes |
Page_CallOut_
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer | yes | Total number of matching items. |
limit |
integer | yes | |
offset |
integer | yes | |
items |
list of CallOut | yes |
Page_DeliveryOut_
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer | yes | Total number of matching items. |
limit |
integer | yes | |
offset |
integer | yes | |
items |
list of DeliveryOut | yes |
Page_EventOut_
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer | yes | Total number of matching items. |
limit |
integer | yes | |
offset |
integer | yes | |
items |
list of EventOut | yes |
Page_ExportOut_
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer | yes | Total number of matching items. |
limit |
integer | yes | |
offset |
integer | yes | |
items |
list of ExportOut | yes |
Page_RunOut_
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer | yes | Total number of matching items. |
limit |
integer | yes | |
offset |
integer | yes | |
items |
list of RunOut | yes |
Page_TicketOut_
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer | yes | Total number of matching items. |
limit |
integer | yes | |
offset |
integer | yes | |
items |
list of TicketOut | yes |
Page_VersionOut_
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer | yes | Total number of matching items. |
limit |
integer | yes | |
offset |
integer | yes | |
items |
list of VersionOut | yes |
Page_WalletTxOut_
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer | yes | Total number of matching items. |
limit |
integer | yes | |
offset |
integer | yes | |
items |
list of WalletTxOut | yes |
PlanOut
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | yes | |
name |
string | yes | |
included_calls_per_month |
integer | yes | |
overage_price_cents |
integer | yes | Wallet price of one extra call unit. |
max_seats |
integer | yes | |
max_countries |
integer | yes | |
max_concurrent_calls |
integer | yes | |
features |
object | yes |
PlanPublicOut
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | yes | |
name |
string | yes | |
included_calls_per_month |
integer | yes | |
overage_price_cents |
integer | yes | Wallet price of one extra call unit. |
max_seats |
integer | yes | |
max_countries |
integer | yes | |
max_concurrent_calls |
integer | yes | |
features |
object | yes | |
monthly_price_cents |
integer | yes | |
annual_monthly_price_cents |
integer | yes | Per month, billed yearly. |
PortalIn
| Field | Type | Required | Description |
|---|---|---|---|
return_url |
string | no |
PromoteIn
| Field | Type | Required | Description |
|---|---|---|---|
environment |
string | yes | Environment to promote to, by slug, name or id. |
released_at |
string (date-time) | no | Default: now. |
ReferralOut
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | yes | |
url |
string | yes | Share it: a new organization created through it gets, like yours, bonus_calls_each bonus calls at its first paid subscription. |
bonus_calls_each |
integer | yes | |
pending |
integer | yes | Referred organizations that have not paid yet. |
granted |
integer | yes | |
calls_earned |
integer | yes |
ReleaseIn
| Field | Type | Required | Description |
|---|---|---|---|
version |
string | yes | |
notes |
string | no | |
released_at |
string (date-time) | no | |
target_id |
string (uuid) | no | |
environment |
string | no | Environment, by slug, name or id (staging). Default: the organization's default environment. |
ReleaseOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
version |
string | yes | |
notes |
string | yes | |
released_at |
string (date-time) | yes | |
target_id |
string (uuid) | yes | |
environment |
string | yes | Slug of the release's environment. |
ReleasePatch
| Field | Type | Required | Description |
|---|---|---|---|
version |
string | no | |
notes |
string | no | |
released_at |
string (date-time) | no | |
target_id |
string (uuid) | no | Send null to detach the release from its target. |
ReplayIn
| Field | Type | Required | Description |
|---|---|---|---|
call_id |
string (uuid) | no | Replay this call of your organization... |
bundle |
object | no | ...or an exported ReplayBundle. |
same_runtime |
boolean | no | Reuse the bundle's models, voices and settings (exact reproduction). |
RoleIn
| Field | Type | Required | Description |
|---|---|---|---|
role |
admin | member | guest |
yes |
RunOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
number |
integer | yes | |
scenario_id |
string (uuid) | yes | |
scenario_name |
string | yes | |
trigger |
string | yes | |
status |
queued | running | completed | canceled |
yes | |
verdict |
string | yes | |
score |
integer | yes | |
total |
integer | yes | |
passed |
integer | yes | |
failed |
integer | yes | |
warned |
integer | yes | |
errored |
integer | yes | |
blocked |
integer | yes | |
cost_cents |
integer | yes | |
version |
string | yes | Label of the run's version: the one the trigger named, else the one live in its environment when it ran. |
version_id |
string (uuid) | yes | |
release |
string | yes | Deprecated: same value as version. |
tags |
list of string | no | |
environment |
string | yes | Slug of the run's environment. |
created_at |
string (date-time) | yes | |
started_at |
string (date-time) | yes | |
finished_at |
string (date-time) | yes | |
ci |
CiOut | no | |
url |
string | yes |
Sandbox
| Field | Type | Required | Description |
|---|---|---|---|
calls_left_24h |
integer | yes | |
max_concurrent_calls |
integer | yes | |
max_duration_s |
integer | yes | |
schedules_and_ci |
boolean | yes |
ScenarioCompareOut
| Field | Type | Required | Description |
|---|---|---|---|
scenario_id |
string (uuid) | yes | |
name |
string | yes | |
a |
SideScore | yes | |
b |
SideScore | yes | |
score_delta |
number | yes |
ScenarioInvalid
| Field | Type | Required | Description |
|---|---|---|---|
detail |
string | no | |
code |
string | no | |
errors |
list of ValidationIssue | yes |
ScenarioOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
name |
string | yes | |
target_id |
string (uuid) | yes | |
target_phone |
string | yes | |
source |
string | yes | |
tags |
list of string | yes | |
variants |
integer | yes | |
archived |
boolean | yes | |
schedule |
string | yes | |
updated_at |
string (date-time) | yes | |
document |
object | yes | Full scenario document, variants included. |
variant_list |
list of VariantOut | yes | |
compliance |
ComplianceOut | no | Compliance review (acceptable use policy) of this content. |
ScenarioSummary
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
name |
string | yes | |
target_id |
string (uuid) | yes | |
target_phone |
string | yes | |
source |
string | yes | |
tags |
list of string | yes | |
variants |
integer | yes | |
archived |
boolean | yes | |
schedule |
string | yes | |
updated_at |
string (date-time) | yes |
ScheduleForecastOut
| Field | Type | Required | Description |
|---|---|---|---|
scenario_id |
string (uuid) | yes | |
scenario_name |
string | yes | |
environment |
string | yes | Environment of this schedule (a scenario has one schedule per environment): decides the number dialed. |
schedule |
string | yes | Cron expression, local time of timezone. |
timezone |
string | yes | |
call_window |
string | yes | |
runs_left |
integer | yes | Runs before the 1st of next month, 00:00 UTC. |
test_cases |
integer | yes | Enabled variants (1 without variants). |
weight |
integer | yes | Units per 10 minute slice for the destination; 0 when the destination is blocked. |
avg_duration_s |
number | yes | Average duration of the scenario's calls of the last 30 days. |
observed |
boolean | yes | false: never ran recently, counted at its maximum duration. |
slices_per_call |
number | yes | |
units |
integer | yes |
SecretRotateIn
| Field | Type | Required | Description |
|---|---|---|---|
grace_period_hours |
integer | no | How long the previous secret keeps signing deliveries (second v1). 0 stops it at once. |
SideScore
| Field | Type | Required | Description |
|---|---|---|---|
tests |
integer | yes | |
pass_rate |
number | yes | |
score |
number | yes | |
latency_ms |
integer | yes |
StabilityOut
| Field | Type | Required | Description |
|---|---|---|---|
environment |
string | yes | |
tests |
integer | yes | |
pass_rate |
number | yes | |
avg_score |
number | yes | |
p95_latency_ms |
integer | yes | |
flaky |
integer | yes | Test cases that both passed and failed in the period. |
last_run_id |
string (uuid) | yes | |
last_run_verdict |
string | yes |
SubscriptionCheckoutIn
| Field | Type | Required | Description |
|---|---|---|---|
plan |
string | yes | A public plan code (GET /plans). |
interval |
monthly | annual |
no | |
success_url |
string | no | |
cancel_url |
string | no |
SubscriptionOut
| Field | Type | Required | Description |
|---|---|---|---|
plan |
PlanOut | yes | |
status |
string | yes | |
interval |
string | yes | |
current_period_start |
string (date-time) | yes | |
current_period_end |
string (date-time) | yes | |
cancel_at_period_end |
boolean | yes | |
managed_by_stripe |
boolean | yes | false for plans invoiced manually. |
payment_retry_until |
string (date-time) | no | status past_due: the renewal payment failed and Stripe retries it. The plan keeps working until this date; update the card in the billing portal before it or the subscription is canceled. |
TagBulkIn
| Field | Type | Required | Description |
|---|---|---|---|
add |
list of string | no | Tags to add, created if new. |
remove |
list of string | no | Tags to remove. |
objects |
TagObjects | yes |
TagBulkOut
| Field | Type | Required | Description |
|---|---|---|---|
added |
list of string | yes | |
removed |
list of string | yes | |
objects |
object | yes | Objects touched, per kind. |
TagIn
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | |
color |
string | no |
TagObjects
| Field | Type | Required | Description |
|---|---|---|---|
scenarios |
list of string (uuid) | no | |
runs |
list of string (uuid) | no | |
versions |
list of string (uuid) | no | |
numbers |
list of string (uuid) | no | Phone numbers (targets). |
TagOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer | yes | |
name |
string | yes | |
color |
string | yes |
TagPatch
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | |
color |
string | no |
TagSuggestion
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer | yes | |
name |
string | yes | |
color |
string | yes | |
uses |
integer | yes | Objects carrying the tag (scenarios, runs, versions, numbers). |
TargetIn
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | |
phone |
string | yes | E.164 number, e.g. +14155550100. |
kind |
ai_agent | ivr | iva | human | unknown |
no | |
platform |
string | no | |
language |
string | no | |
description |
string | no |
TargetOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
name |
string | yes | |
phone |
string | yes | |
country |
string | yes | |
line_type |
string | yes | |
kind |
string | yes | |
platform |
string | yes | |
language |
string | yes | |
description |
string | yes | |
verification_status |
unverified | pending | verified | expired | locked | suspended |
yes | |
verification_method |
string | yes | |
verified_at |
string (date-time) | yes | |
verification_expires_at |
string (date-time) | yes | |
attested |
boolean | yes | The legal attestation is signed (sandbox calls). |
created_at |
string (date-time) | yes |
TargetPatch
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | |
kind |
string | no | |
platform |
string | no | |
language |
string | no | |
description |
string | no |
TicketDetail
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
ref |
string | yes | Short reference, e.g. CQA-42. |
kind |
bug | question | feedback | billing | abuse_report |
yes | |
status |
open | waiting_customer | answered | closed |
yes | |
channel |
mcp | api | app | email |
yes | |
subject |
string | yes | |
created_by |
string | yes | |
call_id |
string (uuid) | yes | |
run_id |
string (uuid) | yes | |
scenario_id |
string (uuid) | yes | |
context |
object | yes | |
created_at |
string (date-time) | yes | |
updated_at |
string (date-time) | yes | |
last_staff_at |
string (date-time) | yes | Last answer of the team. |
unread |
integer | yes | Answers of the team nobody of the organization has opened in the app yet. |
message_count |
integer | yes | |
url |
string | yes | The ticket in the app. |
messages |
list of MessageOut | yes | |
secrets_redacted |
integer | no | On create and reply: how many things that looked like secrets were removed. |
TicketIn
| Field | Type | Required | Description |
|---|---|---|---|
subject |
string | yes | |
body |
string | yes | Markdown. What you tried, what happened (error code and detail), what you expected. |
kind |
bug | question | feedback | billing | abuse_report |
no | |
call_id |
string (uuid) | no | A call this is about. |
run_id |
string (uuid) | no | A run this is about. |
scenario_id |
string (uuid) | no | A scenario this is about. |
context |
object | no | Free JSON (8 KB at most): client name and version, OS, the request that failed... |
TicketOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
ref |
string | yes | Short reference, e.g. CQA-42. |
kind |
bug | question | feedback | billing | abuse_report |
yes | |
status |
open | waiting_customer | answered | closed |
yes | |
channel |
mcp | api | app | email |
yes | |
subject |
string | yes | |
created_by |
string | yes | |
call_id |
string (uuid) | yes | |
run_id |
string (uuid) | yes | |
scenario_id |
string (uuid) | yes | |
context |
object | yes | |
created_at |
string (date-time) | yes | |
updated_at |
string (date-time) | yes | |
last_staff_at |
string (date-time) | yes | Last answer of the team. |
unread |
integer | yes | Answers of the team nobody of the organization has opened in the app yet. |
message_count |
integer | yes | |
url |
string | yes | The ticket in the app. |
TriggerIn
| Field | Type | Required | Description |
|---|---|---|---|
variants |
list of string (uuid) | no | Subset of variant ids; default all enabled variants. |
version |
string | no | Version of your code under test (v2.3.0, a short sha), in the run's environment; created if new. Without it the run belongs to the version live in the environment. |
git_ref |
string | no | Branch or tag of the version, stored when the version is created. |
git_sha |
string | no | |
source_url |
string | no | Commit or pipeline page. |
release |
string | no | Deprecated: the former name of version, read when version is absent. |
tags |
list of string | no | Free keywords added to the run. |
ci |
boolean | no | Mark the run as triggered by CI. |
environment |
string | no | Environment, by slug, name or id (staging). Default: the organization's default environment. The version, when given, is looked up (or created) in this environment. |
TurnOut
| Field | Type | Required | Description |
|---|---|---|---|
t_ms |
integer | yes | |
speaker |
string | yes | |
text |
string | yes | |
start_ms |
integer | yes | |
end_ms |
integer | yes |
UsageOut
| Field | Type | Required | Description |
|---|---|---|---|
period_start |
string | yes | |
included |
integer | yes | |
used |
integer | yes | |
held |
integer | yes | |
remaining |
integer | yes | |
bonus_remaining |
integer | yes | |
overage_price_cents |
integer | yes | |
wallet_available_cents |
integer | yes | |
plan |
string | yes |
ValidationIssue
| Field | Type | Required | Description |
|---|---|---|---|
loc |
string | yes | Dotted path of the offending field. |
msg |
string | yes |
VariantIn
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | yes | |
source |
auto | manual |
no | |
persona |
object | no | |
goal |
string | no | |
instructions |
string | no | |
test_data |
object | no | |
assertions |
list of object | no | |
enabled |
boolean | no |
VariantOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
index |
integer | yes | |
title |
string | yes | |
source |
auto | manual |
yes | |
enabled |
boolean | yes | |
spec |
object | yes | callqa_spec Variant document. |
VariantPatch
| Field | Type | Required | Description |
|---|---|---|---|
enabled |
boolean | no | |
spec |
object | no | Replace the variant document. |
VerdictDiffOut
| Field | Type | Required | Description |
|---|---|---|---|
scenario |
string | yes | |
index |
integer | yes | |
title |
string | yes | |
a |
string | yes | |
b |
string | yes | |
a_call_id |
string (uuid) | yes | |
b_call_id |
string (uuid) | yes | |
regressed |
boolean | yes | True when the test case passes on side A only. |
VerificationCallIn
| Field | Type | Required | Description |
|---|---|---|---|
dtmf_gate |
string | no | Replace the keys to press of the active code (see start). {code} only works when it was already given to start or retry. |
VerificationCallOut
| Field | Type | Required | Description |
|---|---|---|---|
call_id |
string (uuid) | yes | |
verification |
VerificationOut | yes |
VerificationCodeOut
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | yes | Shown ONCE, never stored in clear: copy it now. |
phrase |
string | yes | Add this sentence to the greeting or the agent prompt. |
code_expires_at |
string (date-time) | yes | |
method |
string | yes | |
verification |
VerificationOut | yes |
VerificationOut
| Field | Type | Required | Description |
|---|---|---|---|
status |
unverified | pending | verified | expired | locked | suspended |
yes | |
method |
string | yes | |
verified_at |
string (date-time) | yes | |
expires_at |
string (date-time) | yes | Re-verification needed after this date. |
locked_until |
string (date-time) | yes | |
suspended_reason |
string | yes | |
attested |
boolean | yes | |
attested_at |
string (date-time) | yes | |
attestation_version |
string | no | Version of the signed text. |
attestation_current_version |
string | yes | Version to sign today. |
attestation_outdated |
boolean | no | Signed under a text that is no longer accepted: sign again, calls to this number are blocked until then. |
attempts_left |
integer | yes | Failed attempts allowed before a 24 h lock. A call that is not answered counts too. |
attempts_max |
integer | no | Failed attempts allowed per number in 24 h. |
attempts_reset_at |
string (date-time) | no | When the oldest failed attempt stops counting (24 h after it ended) and one attempt comes back; null when none counts. |
dtmf_gate |
string | no | Keys remembered for the next verification call (IVR menu). |
active_code |
ActiveCode | yes | |
last_attempt |
LastAttempt | yes | |
sandbox |
Sandbox | yes | Limits while the number is not verified (attestation signed). |
inbound_number |
string | yes | Number to call for inbound_dtmf. |
VerificationStartIn
| Field | Type | Required | Description |
|---|---|---|---|
method |
spoken_code | inbound_dtmf |
no | spoken_code (default): put the code in the line's greeting or agent prompt, then POST .../verification/call. inbound_dtmf (when enabled): call our verification number from the line and type the code. |
attestation |
AttestIn | no | Sign the attestation in the same request (required once). |
dtmf_gate |
string | no | Keys the verification call presses after the line answers, before listening for the code, when the code is behind an IVR menu: 0-9, *, #, w for a 0.5 s pause (as in Twilio's sendDigits), at most 40 characters. Example: 1w2. {code} types the code itself (reserved to CallQA test lines). Omitted: the keys remembered on the number; empty string: no keys. |
renew |
boolean | no | Verify a verified number again (renews the 180 days). The number stays verified during the attempt and if it fails. |
VerifyIn
| Field | Type | Required | Description |
|---|---|---|---|
method |
string | no | Legacy endpoint: signs the attestation only. |
accept |
boolean | yes | Must be true: you accept the attestation text. |
signer_name |
string | yes | Full name of the person attesting. |
VersionIn
| Field | Type | Required | Description |
|---|---|---|---|
label |
string | yes | |
environment |
string | no | Environment, by slug, name or id (staging). Default: the organization's default environment. |
deployed_at |
string (date-time) | no | When it went live. Default: now. |
git_ref |
string | no | |
git_sha |
string | no | |
source_url |
string | no | |
notes |
string | no | |
target_id |
string (uuid) | no | Only for this phone number (default: every number). |
tags |
list of string | no |
VersionOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
label |
string | yes | |
environment |
string | yes | Slug of the version's environment. |
deployed_at |
string (date-time) | yes | |
git_ref |
string | yes | |
git_sha |
string | yes | |
source_url |
string | yes | |
notes |
string | yes | |
target_id |
string (uuid) | yes | |
tags |
list of string | yes | |
created_at |
string (date-time) | yes |
VersionPatch
| Field | Type | Required | Description |
|---|---|---|---|
label |
string | no | |
deployed_at |
string (date-time) | no | |
git_ref |
string | no | |
git_sha |
string | no | |
source_url |
string | no | |
notes |
string | no | |
target_id |
string (uuid) | no | Send null to apply the version to every number. |
tags |
list of string | no | Replaces the tags. |
VersionPromoteIn
| Field | Type | Required | Description |
|---|---|---|---|
environment |
string | yes | Environment to promote to, by slug, name or id. |
deployed_at |
string (date-time) | no | Default: now. |
WalletOut
| Field | Type | Required | Description |
|---|---|---|---|
balance_cents |
integer | yes | |
held_cents |
integer | yes | |
available_cents |
integer | yes | |
currency |
string | yes |
WalletTxOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
integer | yes | |
kind |
string | yes | |
amount_cents |
integer | yes | |
held_delta_cents |
integer | yes | |
balance_after_cents |
integer | yes | |
call_id |
string (uuid) | yes | |
description |
string | yes | |
created_at |
string (date-time) | yes |
WebhookCreated
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
url |
string | yes | |
events |
list of string | yes | |
description |
string | yes | |
enabled |
boolean | yes | |
created_at |
string (date-time) | yes | |
filters |
WebhookFilters | yes | |
previous_secret_expires_at |
string (date-time) | no | While set and in the future, deliveries carry a second v1 signature made with the previous secret. |
secret |
string | yes | Signing secret. Shown only once. |
WebhookFilters
| Field | Type | Required | Description |
|---|---|---|---|
scenario_ids |
list of string (uuid) | no | Only events of these scenarios. |
tags |
list of string | no | Only events of scenarios or runs carrying one of these tags. |
environments |
list of string | no | Only events of these environments (slug, name or id). |
WebhookIn
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | yes | |
events |
list of string | yes | Event types, or ['*'] for all. |
description |
string | no | |
enabled |
boolean | no | |
filters |
WebhookFilters | no | Optional scope. Each non-empty list must match (any value of a list, all lists). Call, run and version events are filtered; organization events (wallet.low_balance) always go through. Empty or absent: every event. |
WebhookOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | |
url |
string | yes | |
events |
list of string | yes | |
description |
string | yes | |
enabled |
boolean | yes | |
created_at |
string (date-time) | yes | |
filters |
WebhookFilters | yes | |
previous_secret_expires_at |
string (date-time) | no | While set and in the future, deliveries carry a second v1 signature made with the previous secret. |
WebhookPatch
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | no | |
events |
list of string | no | |
description |
string | no | |
enabled |
boolean | no | |
filters |
WebhookFilters | no | Optional scope. Each non-empty list must match (any value of a list, all lists). Call, run and version events are filtered; organization events (wallet.low_balance) always go through. Empty or absent: every event. Send {} or null to remove every filter. |