Skip to content

Error catalog

Every request to the API that fails returns the same JSON error shape, with a stable, machine-readable code identifying what went wrong. This page lists every code the API can return to a partner today, what it means, and what to do about it. It does not cover a failed analysis job: that is a different kind of failure, described at the bottom of this page.

The error envelope

A failed request always responds with a JSON body of this shape:

{
  "error": {
    "code": "malformed_request",
    "message": "`upload_handle` is required and must be a non-empty string."
  }
}

code is the stable, machine-readable string to branch your handling on; it will not change once published. message is a human-readable sentence for logs and debugging; treat its exact wording as informational, not something to parse or match against.

Retryable vs terminal

Each code below is marked either Retryable or Terminal.

A Retryable failure is a transient, server-side condition. The request itself was fine; retrying it later, after the wait or corrective step named in its entry, can succeed with no change to what you sent.

A Terminal failure means something about the request or its credentials needs to change before trying again. Retrying it unchanged will fail the same way every time.

Quick reference

Code HTTP status Retryable / Terminal
authentication_error 401 Terminal
malformed_request 400 Terminal
unknown_exercise 404 Terminal
upload_not_found 404 Terminal
upload_spot_expired 410 Terminal
upload_incomplete 409 Terminal
upload_already_claimed 409 Terminal
spots_per_window_exceeded 429 Retryable
unclaimed_bytes_exceeded 429 Retryable
store_unavailable 503 Retryable
storage_stat_unavailable 503 Retryable
catalog_error 500 Retryable
internal_error 500 Terminal
job_not_found 404 Terminal

Codes

authentication_error · 401 · Terminal

Meaning: The request's credentials were rejected. A missing or malformed Authorization header, an invalid key, a suspended key, and a revoked key all produce this exact same code and the same message: "Invalid, suspended, or revoked API key." The response never tells you which of these applies.

Action: Confirm the header is present and formed as Authorization: Bearer <key>. If the key looks correct and is still rejected, it may have been suspended or revoked; ask the operator to check its status.

malformed_request · 400 · Terminal

Meaning: The request body was not valid JSON, was not a JSON object, or was missing a required field.

Action: Fix the request body and resubmit. The message names the specific problem, for example a missing upload_handle or exercise_id.

unknown_exercise · 404 · Terminal

Meaning: The exercise id in the request does not exist in the exercise catalog.

Action: Look up a valid id from the exercise catalog endpoint described in the endpoint reference, then resubmit with a valid id.

upload_not_found · 404 · Terminal

Meaning: No upload spot exists for the handle given. This covers two different situations on purpose: a handle nobody ever minted, and a handle that exists but belongs to a different key. Both return this exact same response, so a caller cannot use the response to tell "this handle was never yours" apart from "this handle does not exist."

Action: Check the handle for a copy or transcription error, or mint a fresh upload spot and start over.

upload_spot_expired · 410 · Terminal

Meaning: The upload spot existed but its lifetime window has already passed.

Action: Mint a new upload spot and complete the upload within its window this time.

upload_incomplete · 409 · Terminal

Meaning: The upload spot exists and is still within its window, but no video bytes have landed on it yet.

Action: Complete the upload to the spot before submitting a job against it.

upload_already_claimed · 409 · Terminal

Meaning: This has two distinct outcomes depending on what you send. If the handle was already claimed by a job for the same exercise id you are submitting again, that is not an error at all: the request succeeds with a 201 and returns the original job id unchanged. This code and status are returned only when the handle was already claimed by a job for a different exercise id than the one in the new request.

Action: If you meant to submit the same exercise again, resend the exact same exercise id and expect the original job id back. If you meant a different exercise, mint a new upload spot for it; a handle can only be claimed once.

spots_per_window_exceeded · 429 · Retryable

Meaning: This key has already minted the maximum number of upload spots allowed within the current rolling window.

Action: Wait for the window to roll forward and free a spot, then retry minting.

unclaimed_bytes_exceeded · 429 · Retryable

Meaning: This key's total unclaimed upload bytes have gone over the cap. See limits and quotas for the current figures.

Action: Submit jobs for outstanding uploads to claim their bytes, or wait for unclaimed spots to expire, then retry minting.

store_unavailable · 503 · Retryable

Meaning: A durable backing store the API depends on, the API key store, the upload-spot store, or the job store, is temporarily unreachable. This is distinct from storage_stat_unavailable below, which covers only the video storage layer.

Action: Wait a moment and retry.

storage_stat_unavailable · 503 · Retryable

Meaning: A query against the video storage layer failed or timed out. This is distinct from store_unavailable above, which covers the key, upload-spot, and job stores rather than video storage.

Action: Wait a moment and retry.

catalog_error · 500 · Retryable

Meaning: The exercise catalog could not be read or resolved.

Action: Wait a moment and retry. If it keeps happening, contact the operator at support@fitizens.io.

internal_error · 500 · Terminal

Meaning: The server retained the uploaded video but could not confirm that the job record itself was written. This is a rare server-side failure, not a problem with the request you sent.

Action: Do not immediately resubmit with the same handle; it may already be marked claimed. Contact the operator at support@fitizens.io with the approximate time of the request so the job can be reconciled.

job_not_found · 404 · Terminal

Meaning: No job exists for the given id, or it exists but belongs to a different key. As with upload_not_found, both cases return this exact same response so a foreign job's existence cannot be probed.

Action: Check the job id for a copy or transcription error.

A failed job is not an envelope error

A job that reaches its failed state is not reported through this envelope. Polling or retrieving that job still returns a normal 200 response; the failure shows up as a reason recorded on the job's own status payload, not as one of the codes above. See the endpoint reference for where that field lives on the job status response and how to read it.