Skip to main content

How errors are reported

The API signals failures with standard HTTP status codes. Most errors carry a JSON body. Simple errors use the conventional {"detail": "..."} shape; two cases — frozen Tasks and request validation — return a structured, typed body documented below. Always branch on the status code first, then read the body for detail.

Status codes at a glance

400 — Bad request

A 400 means the request was understood but is not acceptable as sent. The specific causes differ by endpoint (see the table above). The body is the conventional detail shape:
Unknown task_type on POST /task/create:
Neither/both identifiers on GET /task/get:
The uuid query parameter is what this message calls task_id — pass exactly one of uuid or external_id.Requesting a link for a non-file Data Item on get_link:
task_type values are provisioned for your organization during onboarding — there is no discovery endpoint. If you get “Task type not in list of allowed tasks,” confirm the exact value with your Synthpop contact.

403 — Forbidden

Returned by POST /auth/get_token when the api_secret is well-formed but invalid or obsolete — for example after it has been rotated or revoked. Mint a fresh secret from the Synthpop UI and exchange it again.
Distinguish 400 from 403 on token exchange: 400 means the secret is the wrong shape (fix the string you send); 403 means the shape is right but the secret is not valid (rotate it). Retrying the same call will not fix either.

404 — Not found

The identifier is well-formed but nothing matches it. On GET /task/get this means no Task with that uuid/external_id exists for your org; on get_link it means the Task has no Data Item with that ID.

409 — Conflict (frozen Task, or Task not accepting uploads)

POST /task/{uuid}/upload returns 409 in two distinct cases, each with a different body shape:
  • The Task has been frozen by a newer, patient-matched Task. The body is a TaskFrozenError naming the Task to use instead.
  • The Task is not accepting uploads (it is not in waiting), or every file in the upload request is a duplicate of an existing Data Item. The body is the conventional {"detail": "..."} shape — for example "All uploaded files are duplicates of existing data items."not a TaskFrozenError.
If the body is a TaskFrozenError, do not retry the same Task — redirect your upload to current_task_uuid. If the body is a plain {"detail": ...} (the Task is not accepting uploads, or every file was a duplicate), retrying the same bytes will not help; fix the request instead. See Responding to waiting Tasks.

422 — Validation error

FastAPI returns 422 when a request fails schema validation — a missing required field, a wrong type, or an out-of-range value (for example length=500 on GET /task/list, which caps at 200). The body is an HTTPValidationError: a detail array where each entry pinpoints one problem.

429 — Too many requests

Two distinct endpoints return 429:
  • POST /task/create returns 429 when too much deferrable work is already parked server-side awaiting capacity (it reports as pending, not as a separate observable status). Back off and retry the create later; in-flight Tasks are unaffected. See load backpressure.
  • POST /auth/get_token returns 429 when there have been too many token-exchange attempts. This response carries a Retry-After header telling you how many seconds to wait before trying again. A token is valid for 365 days, so exchange once and reuse it rather than minting a fresh token per request.
The 429 on POST /task/create is part of the API contract but is not yet present in the committed OpenAPI snapshot that powers the API reference. Handle it anyway; the committed reference will include it once the schema-regeneration job lands.

Responding to waiting Tasks

Recover from a 409 and resume a paused Task.

Working with async results

Polling, callbacks, and load backpressure (the 429 queue cap).