Errors

Every response that is not 2xx uses the same JSON envelope, on the public API and the dashboard API alike:

Example (JSON)
{
  "error": {
    "type": "unprocessable_audio",
    "code": "insufficient_speech",
    "message": "At least 3 seconds of speech are required (detected 1.8 s).",
    "param": "file"
  },
  "request_id": "req_6d2e9b0a14f798e4eef1697e"
}
Field Description
error.type A broad category. Use it for general handling.
error.code A specific snake_case code. Use it for program logic. New codes may be added; treat unknown codes by their type.
error.message A human-readable explanation. Do not parse it; the wording can change.
error.param The request field the error relates to, such as file or speaker_id, or null.
request_id Identifier of the request. Also sent in the X-Request-Id header on every response, successful or not. Include it when you contact support.

Failed requests are never charged. See Credits and billing.

Error types and HTTP status codes

error.type HTTP Meaning
invalid_request 400 A field is missing, malformed or out of range.
authentication 401 The API key is missing, invalid or revoked.
insufficient_credits 402 Not enough available credits.
permission 403 The key lacks a scope, or the account cannot use this feature yet.
not_found 404 The resource does not exist in your account.
conflict 409 The request conflicts with the current state, such as an operation still running.
payload_too_large 413 The upload is larger than the limit.
unsupported_media 415 The file format is not supported.
unprocessable_audio 422 The audio was decoded but cannot be used (length, silence, speech).
rate_limited 429 Too many requests. Wait for Retry-After seconds.
internal 500, 502, 504 A server-side failure, including model failure or timeout.
overloaded 503 The model service is at capacity. Wait for Retry-After seconds.

Error codes

error.code error.type HTTP Meaning Retry
missing_api_key authentication 401 No Authorization: Bearer header was sent. No
invalid_api_key authentication 401 The key or the Authorization header is malformed, or the key does not exist. No
api_key_in_url invalid_request 400 An api_key query parameter was sent. Keys go only in the Authorization header. Rotate a key that was sent in a URL. No
revoked_api_key authentication 401 The key was revoked or rotated out. No
insufficient_scope permission 403 The key does not have a scope this request needs. The message names the missing scopes. No
email_not_verified permission 403 The account's email address is not verified. No
account_suspended permission 403 The account is suspended. No
account_deleted permission 403 The account was deleted. No
session_required permission 403 The endpoint is only available to signed-in dashboard sessions, not API keys. No
invalid_speaker_id invalid_request 400 speaker_id does not match ^[A-Za-z0-9_.:-]{1,64}$. No
invalid_limit invalid_request 400 limit is not an integer in the allowed range. No
invalid_cursor invalid_request 400 The pagination cursor is not one the API returned. No
invalid_display_name invalid_request 400 display_name is longer than 128 characters. No
missing_file invalid_request 400 The multipart body has no file field. No
invalid_multipart invalid_request 400 The multipart body could not be parsed. No
unknown_parameter, duplicate_parameter, invalid_parameter invalid_request 400 A form field is not accepted by this endpoint, appears twice, or has an invalid value (for example detect_deepfake that is not true or false). No
unsupported_content_type unsupported_media 415 The request is not multipart/form-data. No
request_too_large payload_too_large 413 A request body other than an upload is too large. No
invalid_idempotency_key invalid_request 400 Idempotency-Key is not 1 to 128 printable ASCII characters. No
invalid_operation, invalid_status invalid_request 400 A GET /v1/usage filter has an unknown value. No
speaker_not_found not_found 404 The speaker_id is not enrolled in your account. No
operation_not_found not_found 404 The operation_id does not exist in your account. No
operation_in_progress conflict 409 A request with the same Idempotency-Key is still running. The body includes its operation_id; poll it. Later
operation_superseded conflict 409 A newer attempt with the same Idempotency-Key took over this operation. Use the newer attempt's result. No
idempotency_result_unavailable conflict 409 The stored result for this Idempotency-Key is no longer available. Use a new key. No
too_many_samples conflict 409 The speaker already has the maximum of 5 active samples. No
speaker_reenrollment_required conflict 409 The speaker has no samples for the current speaker model version. Enroll again. No
idempotency_key_reuse invalid_request 422 The Idempotency-Key was already used for a different request. No
insufficient_credits insufficient_credits 402 Available credits are below the cost of the request. No
file_too_large payload_too_large 413 The upload is over 20 MB. No
unsupported_format unsupported_media 415 The file is not an audio format the server can decode. No
corrupt_audio unprocessable_audio 422 The file is empty, truncated or could not be decoded. No
audio_decode_timeout unprocessable_audio 422 Decoding took longer than 20 seconds. Send a shorter or simpler file. No
audio_too_short unprocessable_audio 422 The decoded audio is shorter than the minimum for this operation. No
audio_too_long unprocessable_audio 422 The decoded audio is longer than the maximum for this operation. No
silent_audio unprocessable_audio 422 The recording contains no signal. No
insufficient_speech unprocessable_audio 422 Too little speech was detected (3 s to enroll, 2 s to verify). No
rate_limited rate_limited 429 Too many requests for this key, account or IP address. After Retry-After
concurrency_limited rate_limited 429 Too many model operations running at once for this account. After Retry-After
inference_overloaded overloaded 503 The model queue is full. After Retry-After
inference_failed internal 502 The model service returned an error. Yes, with backoff
inference_timeout internal 504 The model did not finish in time. Yes, with backoff
operation_interrupted internal 500 The operation was interrupted, for example by a server restart. Nothing was charged. Yes, with backoff
internal_error internal 500 An unexpected server error. Nothing was charged. Yes, with backoff

Other malformed requests return 400 with type invalid_request, a specific error.code, and error.param naming the field.

Some errors add fields next to error. 402 insufficient_credits includes the amounts:

Example (JSON)
{
  "error": {
    "type": "insufficient_credits",
    "code": "insufficient_credits",
    "message": "This operation needs 100 credits but only 50 are available.",
    "param": null
  },
  "request_id": "req_a81c0f3e5b2734048a57188e",
  "credits": { "available": 50, "required": 100 }
}

Handling errors

Example (cURL)
# -s hides progress, -w prints the status code after the body
curl -s -w "\nHTTP %{http_code}\n" https://platform.detectif.ai/v1/speakers/unknown-id \
  -H "Authorization: Bearer $DETECTIF_API_KEY"

See Idempotency and retries for which errors are safe to retry and how.