Перейти к содержимому

Errors

Это содержимое пока не доступно на вашем языке.

The API uses conventional HTTP status codes. Broadly: 2xx succeeded, 4xx failed on something in your request or your account, 5xx is us.

Status Meaning
200 201 Worked.
400 Validation failed, or a typed detail code. Fix the request; retrying unchanged never helps.
401 Missing, invalid, or no-longer-entitled credential.
402 Understood and well-formed, but your plan will not allow it.
403 Authenticated, but the role or scheme gate refused — e.g. a human-only endpoint.
404 Not found — or hidden. Another team’s data returns 404, never 403, so the API never confirms that it exists.
409 State collision — a re-analysis already running, an email already in use.
410 Was valid, now gone. An expired share token.
429 Your API key went over its rate limit on upload-intent or session create — see Limits and capacity. Wait the seconds in Retry-After. (POST /api/me/verify-email/ also returns one after five wrong codes.)
503 A dependency was unavailable. Safe to retry with backoff.
Status detail What happened What to do
401 Invalid API key Unknown, revoked or mistyped. Stop. Retrying never helps — the credential is wrong.
401 API keys require the Organization plan or higher The key is fine; the plan no longer carries API access. Every call fails from this moment. Stop and alert the customer. This is billing, not code. Keys are not deleted — they work again when the plan is restored.
403 This endpoint is not available to API-key callers. A human-only endpoint. Do not retry with a key. See what a key can reach.
402 quota_exhausted Out of analyses for the period. Queue and retry after reset_at, or have the customer upgrade. Nothing was created — no session, no charge.
402 subscription_suspended Payment failed and no live tier remains. Raised before quota is touched. Stop uploading. The customer must fix payment.
402 client_limit_reached Creating an athlete would exceed the plan’s ceiling. Reuse an existing athlete, or upgrade.
402 exercise_not_in_plan The (activity, exercise) exists but is not in the contract’s movement list — also for an upload with no exercise unless the whole activity is covered, and for a re-point (POST /api/sessions/<id>/reanalyze/) onto such a pair. Raised before quota is touched. Pick a movement from GET /api/exercises/ — it is already filtered to the contract — or have the customer extend it. Never retried as-is.
400 {"exercise": ["'…' is not a supported exercise for activity '…'"]} Exact-match miss. Fix the string against the catalogue. Never retry as-is.
400 code: "unsupported_camera_view" That pair has no analyser for that plane. Carries supported_views. Send a supported view, or re-film.
400 clip_too_long / clip_too_large The uploaded clip — or its trim window — is over 180 s (30 s when the session renders with original_quality), or the file is over 500 MB. Carries max_duration_s / max_bytes. Raised before quota is touched. Trim or compress, PUT it again to the same URL, and re-create. See Limits and capacity.
400 clip_not_found Nothing was uploaded to that key — a create (or re-run) sent before the PUT finished, or a clip since deleted (an upload no session claims within three days is). Raised before quota is touched. Finish the PUT, then create again; for an old upload, ask for a new intent and PUT again.
400 {"id": ["session_id_taken"]} That upload-intent id was already consumed — usually a retry after a dropped 201. Check whether the session exists before re-creating it.
400 video_url must be an http(s) URL — … A placeholder, a bare path, or another team’s media. Send the public_url from the upload intent, unmodified.
409 source_deleted A re-run of a face-blurred session whose team deletes unblurred originals: the upload is gone (source_deleted: true, video_url: ""); the blurred videos and every metric stay. Raised before anything changes. Never retried as-is. To analyse the movement again, upload the clip as a new session.
503 enqueue_failed Quota was debited but the queue was unreachable. Safe to retry.
429 rate_limited Your key went over its per-key limit on upload-intent or session create. Nothing was created or debited. Wait Retry-After seconds, then retry. Not a 402 — the plan is fine, the loop is too tight.

Every 402 returns the same shape, so one handler covers all of them:

{
"detail": "quota_exhausted",
"reset_at": "2026-10-01T00:00:00Z",
"current_plan": "organization",
"current_role": "organization"
}
flowchart TB
    E["402 Payment Required"] --> D{"read detail"}
    D -->|quota_exhausted| Q["account healthy,<br/>allowance used"]
    D -->|subscription_suspended| S["account in dunning"]
    D -->|exercise_not_in_plan| X["movement not in<br/>the contract"]
    Q --> QA["queue the work,<br/>retry after reset_at"]
    S --> SA["stop and escalate —<br/>a person must fix payment"]
    X --> XA["pick from the catalogue —<br/>waiting changes nothing"]

quota_exhausted and subscription_suspended are both 402 and both read as “payment required” from outside. They are not the same:

  • quota_exhausted — the account is healthy and has simply used its allowance. Waiting fixes it. Queue the work.
  • subscription_suspended — the account is in dunning. Waiting fixes nothing; a person has to update a payment method. Stop and escalate.

Treating the second like the first means a silently stalled integration and a customer who finds out from you rather than from us.

exercise_not_in_plan is the third kind: nothing ran out and nothing is broken — the movement is a line item the contract does not carry. Waiting changes nothing and neither does a plan upgrade; the movement list is agreed with the account manager. The catalogue is already filtered to the contract, so an integration that offers only what GET /api/exercises/ returns never sees this code.