The envelope
Every refusal on this surface has the same two keys, whatever raised it. There is no second error shape to handle.
Any 4xx or 5xx
{
"error": "insufficient_scope",
"message": "This key does not carry the scope this endpoint needs.",
"required": "transcriptions:write"
}erroris the stable, machine-readable code. Branch on this.messageis written for a person reading a log. Do not parse it — the wording may change at any time.- Some refusals add a field:
errorson a validation failure,requiredon a scope refusal,docson the gating 403s.
The catalogue
Every code the API can answer with. This list is generated from the same OpenAPI document the API is tested against, so it cannot drift from the running code.
| HTTP | error | Meaning |
|---|---|---|
| 401 | missing_api_key | No Authorization header. Send `Authorization: Bearer h2t_live_…`. |
| 401 | invalid_api_key | Unreadable, unknown or revoked key — or a credential from another surface. Not retryable. |
| 403 | subscription_required | The account has no active subscription. The key is kept and will work again once it does. |
| 403 | insufficient_scope | The key does not carry the scope this route needs. The body names it. Create a key with the scope. |
| 403 | account_restricted | The account has been restricted. Contact support. |
| 403 | guest_account_not_supported | A guest account cannot use the API. |
| 403 | workspace_scope_mismatch | This key is pinned to one workspace and the recording lives in another. |
| 403 | forbidden | Allowed to ask, not allowed to do this to this recording. |
| 404 | not_found | No such recording, or not one this key can see. The two are deliberately indistinguishable. |
| 409 | concurrency_limit | Too many recordings running at once for this plan. Wait for one to finish and retry. |
| 409 | idempotency_key_reused | This Idempotency-Key was used for a different request body. Use a new key. |
| 409 | idempotency_request_in_progress | The first request with this key has not answered yet. Retry in a few seconds. |
| 409 | transcription_still_processing | A recording that has not finished — still queued or processing — cannot be deleted. Wait for it to complete or fail. |
| 409 | transcription_is_not_ready | Only a completed recording can be exported. |
| 413 | file_too_large | The request body is over what the server accepts at all. Compress the recording, or split it. A file that arrives but is over the per-file ceiling is a `validation_failed` naming `file` instead. |
| 400 | bad_request | The request could not be read as HTTP we understand. Not retryable as sent. |
| 405 | method_not_allowed | That path exists, but not for this HTTP method. Check the reference. |
| 422 | validation_failed | The request could not be accepted as sent. `errors` names each field. |
| 422 | uploaded_file_cannot_be_processed | The upload did not reach storage intact. Retry with a fresh Idempotency-Key. |
| 422 | playlist_not_acceptable | That link is a playlist. Send one recording at a time. |
| 429 | rate_limited | Too many requests, or today’s create allowance is spent. Wait Retry-After seconds. |
| 500 | internal_error | Our fault. Retry with backoff; if it persists, quote the X-Request-Id. |
| 503 | service_unavailable | The API is briefly down, usually for a deploy. Retry after Retry-After seconds, or shortly. |
Validation errors
A 422 carries an errors map: the field, and every message about it.
422 Unprocessable Entity
{
"error": "validation_failed",
"message": "The request could not be accepted as sent.",
"errors": {
"url": ["Send either a `file` or a `url`."],
"language": ["Unknown language code. GET /languages lists the ones we accept."]
}
}Request ids
Every response — including every error — carries X-Request-Id. Send your own and we echo it; send nothing and we generate one. It is in our logs beside the request, so quoting it in a support conversation saves everybody a day.
What to retry
| Status | Retry? |
|---|---|
401, 403, 404, 413, 422 | No. Nothing about waiting changes the answer — fix the request or the key. |
409 | Yes, after a pause. A concurrency ceiling clears as recordings finish; an in-flight idempotency key resolves in seconds. |
429 | Yes, after Retry-After seconds. Not sooner. |
5xx | Yes, with exponential backoff — and with the same Idempotency-Key if it was a create, so you do not pay twice for one recording. |
