Errors

The Synex API uses conventional HTTP status codes and always returns the same error envelope, so you can branch on machine-readable fields rather than parsing prose. A 2xx means success; 4xx means the request needs fixing; 5xx means something failed on our side.

The error envelope

Every non-2xx response has a single top-level error object.

  • Name
    type
    Type
    string
    Description

    Broad category — branch on this first. One of the types listed below.

  • Name
    code
    Type
    string
    Description

    Specific, machine-readable failure — branch on this second.

  • Name
    message
    Type
    string
    Description

    Human-readable explanation. Do not parse it; wording may change.

  • Name
    param
    Type
    string
    Description

    The offending request parameter, when applicable. Nullable.

  • Name
    request_id
    Type
    string
    Description

    A req_ identifier for the whole request, also returned as the X-Request-Id header. Always present. Quote it when contacting support.

  • Name
    error_id
    Type
    string
    Description

    5xx only. An err_ identifier unique to this one failure. Quote it when reporting a server-side problem — it is the most direct way for support to find the exact incident.

  • Name
    sentry_id
    Type
    string
    Description

    Present when the failure was recorded in our error tracker — every 5xx and most 4xx. Optional; see the note below.

  • Name
    errors
    Type
    object
    Description

    Per-field details on validation failures (for example, property id → failure details). Omitted on failures that are not per-field.

Example error

{
  "error": {
    "type": "invalid_request_error",
    "code": "resource_missing",
    "message": "Passport not found.",
    "param": "passport",
    "request_id": "req_9f2c1ab34d5e6f70",
    "sentry_id": "6fb2138a0f364dffa0729f63abba46ac"
  }
}

Replayed responses

A response replayed from an Idempotency-Key — marked by the Idempotent-Replayed: true header — is the stored body of the original request, so its error.request_id is the original request's id, not the id of the call you just made. The X-Request-Id header always reflects the current request. Quote the header's id when asking about the replay itself and the body's id when asking about the original.

Error types

  • Name
    invalid_request_error
    Type
    400/404/405/409/413/422
    Description

    Bad parameters, missing resources, or state conflicts. Synex Lab's PassQL tooling answers with 422 — see below.

  • Name
    authentication_error
    Type
    401
    Description

    Missing or invalid key, or the key's owner has left its team.

  • Name
    permission_error
    Type
    403
    Description

    The key lacks the required ability — never granted, or no longer held by the member who minted it — or its ontology scope does not cover the resource; or the key's team is suspended or cannot issue passports.

  • Name
    rate_limit_error
    Type
    429
    Description

    Too many requests — honor the Retry-After header.

  • Name
    idempotency_error
    Type
    409
    Description

    An idempotency key was reused with a different payload, or is still in flight.

  • Name
    api_error
    Type
    5xx
    Description

    Something failed on our side. Retry with backoff. Also Lab's 408 query_timeout.

Request validation failures come back as 400 parameter_invalid, with the offending field in param and per-field detail in errors.

Synex Lab: PassQL tooling

Synex Lab returns the standard error envelope, and only that. Every non-2xx body from the Lab authoring endpoints is the envelope above, with the editor-oriented detail rows under error.errors — the positioned parse feedback an editor underlines with sits in the same place it does everywhere else.

Success bodies are a different shape: query/validate answers {valid: true, mode} and dsl/parse answers {valid: true, ast, kind} on a 200.

The status is 422 rather than 400. Handle it the same way — fix the input and resubmit; retrying it unchanged will fail again. 422 is not the only status these endpoints use, though: query/validate reports at whatever status the underlying failure carries — 403, for instance, when the query names another team's ontology — so branch on error.code rather than the status.

Error codes

Every error.code the API returns, grouped by the surface that raises it.

Requests

  • Name
    parameter_invalid
    Type
    400
    Description

    A request field failed validation; param and errors say which.

  • Name
    resource_missing
    Type
    404
    Description

    The id doesn't exist in your team — resources in other teams read as missing. Also returned for unmatched routes.

  • Name
    method_not_allowed
    Type
    405
    Description

    The HTTP method is not supported on this endpoint.

  • Name
    invalid_cursor
    Type
    400
    Description

    The pagination cursor is malformed, tampered with, or replayed under a different set of filters. See pagination.

  • Name
    metadata_invalid
    Type
    400
    Description

    The merged metadata bag broke its limits — 50 keys, 40-character keys, 500-character values, string values only. param names the whole field or the offending entry.

  • Name
    metadata_too_large
    Type
    413
    Description

    The bag fits its own limits but would push the object it is stored on past the 400 KB storage ceiling. Send fewer or shorter keys.

  • Name
    idempotency_key_too_long
    Type
    400
    Description

    Idempotency-Key exceeded 255 characters.

  • Name
    idempotency_key_reused
    Type
    409
    Description

    Typed idempotency_error. The idempotency key was already used with a different request payload.

  • Name
    idempotency_key_in_flight
    Type
    409
    Description

    Typed idempotency_error. The original request with this key is still running. Carries Retry-After; wait and retry.

  • Name
    rate_limit_exceeded
    Type
    429
    Description

    Typed rate_limit_error. See rate limits.

  • Name
    internal_error
    Type
    500
    Description

    Typed api_error. Something failed on our side; retry with backoff and the same Idempotency-Key.

Authentication and permissions

  • Name
    invalid_api_key
    Type
    401
    Description

    No key was sent, the key is unknown, revoked or expired, or it is a credential the public API does not accept.

  • Name
    api_key_team_revoked
    Type
    401
    Description

    The key's owner is no longer a member of the team the key is pinned to.

  • Name
    insufficient_permissions
    Type
    403
    Description

    The key does not hold the ability the endpoint requires — either it was never granted, or the member who minted the key no longer holds it. The message names the ability. See authentication.

  • Name
    ability_not_scoped_to_resource
    Type
    403
    Description

    The key's ontology scope doesn't cover this resource — including an ontology outside a scoped ontologies:read, a team-wide list under a scoped ability, and POST /v1/ontologies under a scoped ontologies:write. See ontology scopes.

  • Name
    ability_missing
    Type
    403
    Description

    The operation stands in for a passport write and the key holds no passports:write at all — opening or applying a data request or a bulk data request. param is passport, or selection on a bulk request. Refused before anything is looked up.

  • Name
    team_suspended
    Type
    403
    Description

    The key's team is suspended, so the key is refused whatever it was granted — on *:write endpoints at the write tier, on creating endpoints at the create tier, and on every authenticated endpoint at the full tier. See suspended teams.

  • Name
    company_required
    Type
    403
    Description

    The key's team has no company — it is a supplier team — so it cannot issue passports: POST /v1/ontologies, POST /v1/ontologies/:ontology/passports and POST /v1/imports refuse. Everything else the key was granted stays available.

API keys

  • Name
    ability_not_grantable
    Type
    400
    Description

    abilities names something the calling key does not hold. See monotonic minting.

  • Name
    scope_not_grantable
    Type
    400
    Description

    The requested ontology scope is wider than the caller's — including omitting it, or unscoping. param is scopes.

  • Name
    grant_inconsistent
    Type
    400
    Description

    The key's own grants contradict each other: an ability held without the one it requires, or a scoped dependent reaching ontologies its ontologies:read does not. param is the dependent ability. Reported only once the subset checks pass. See ontologies are the root.

  • Name
    api_key_limit_reached
    Type
    400
    Description

    The team already holds its maximum of 32 live keys. Revoke one first; rolling is exempt.

  • Name
    key_not_rollable
    Type
    403
    Description

    The target key reaches further than the caller, so rolling it would hand over abilities. param is api_key.

  • Name
    key_not_patchable
    Type
    403
    Description

    A PATCH touching abilities or scopes on a key that reaches further than the caller. param is api_key.

Templates and ontologies

  • Name
    template_not_draft
    Type
    409
    Description

    Completed templates are immutable; only drafts can be edited or deleted.

  • Name
    template_is_draft
    Type
    409
    Description

    Drafts can't be forked or instantiated — complete the template first.

  • Name
    template_too_large
    Type
    413
    Description

    The whole template — structure, names and descriptions — exceeds the 400 KB storage ceiling. Nothing was written.

  • Name
    draft_missing
    Type
    404
    Description

    No draft is open on this ontology.

  • Name
    draft_exists
    Type
    409
    Description

    A draft is already open. GET it, or DELETE it first.

  • Name
    draft_conflict
    Type
    409
    Description

    base_updated_at is stale. Re-read the draft and reapply your changes.

  • Name
    version_conflict
    Type
    409
    Description

    expected_latest_version is stale. Re-read latest_version and retry.

  • Name
    structure_invalid
    Type
    400
    Description

    The draft breaks the additive-change rules, or names a child level the structure doesn't define. One message per violation in error.errors.structure.

  • Name
    ontology_not_migrated
    Type
    409
    Description

    The ontology predates versioning and has not been backfilled, so its draft cannot be published. Contact support.

Passports and releases

  • Name
    value_invalid
    Type
    400
    Description

    A passport property value was rejected; errors maps property id → failure details, each with its own code — see codes inside errors.

  • Name
    value_too_deep
    Type
    400
    Description

    A range or file value nests objects and arrays past the storage ceiling.

  • Name
    passport_too_large
    Type
    413
    Description

    The passport would exceed the 400 KB storage ceiling — values and change history. Nothing in the patch was written.

  • Name
    level_missing
    Type
    404
    Description

    The level named — or the passport's own level — is not in the ontology's schema version. param names what was looked up.

  • Name
    passport_exists_at_level
    Type
    409
    Description

    A singular level already has a passport under that parent.

  • Name
    friendly_id_taken
    Type
    409
    Description

    The friendly id is already used within the parent.

  • Name
    passport_released
    Type
    409
    Description

    The passport is released: its values and friendly_id are frozen, it cannot be archived directly, and it cannot be deleted. Set publish_status to draft first. Also returned when applying a data request onto a released passport.

  • Name
    ancestor_not_released
    Type
    409
    Description

    A passport cannot be released while an ancestor is not; the message names the nearest one. Release from the top down.

  • Name
    descendants_released
    Type
    409
    Description

    A released passport cannot be drafted while anything beneath it is still released. Draft the subtree first, deepest first.

  • Name
    release_in_progress
    Type
    409
    Description

    A release run rooted at this passport or one of its ancestors is still running. Poll GET /v1/release_runs/:id until it is terminal, then retry.

  • Name
    release_conflict
    Type
    409
    Description

    The release moved under the request — cancelled, or released again — between the read and the write. Re-read the passport and decide again.

  • Name
    release_reported
    Type
    409
    Description

    The release has already been reported for billing and can no longer be taken back. Contact support if it was reported in error.

  • Name
    ontology_locked
    Type
    409
    Description

    A passport transfer is running on the passport's ontology, which freezes every write to its passports — values, friendly_id, publish_status, metadata, creating one beneath and deleting one. Nothing was written; retry once the transfer finishes.

Compositions

  • Name
    composition_missing / composition_exists
    Type
    404 / 409
    Description

    No composition exists yet · a composition already exists.

  • Name
    composition_too_large
    Type
    413
    Description

    The composition layout — or one language's strings — exceeds the 400 KB storage ceiling. Nothing was written.

  • Name
    language_invalid
    Type
    400
    Description

    lang is not one of the 24 official EU language codes. param is lang.

  • Name
    language_is_default
    Type
    400
    Description

    lang is the ontology's default language, whose strings are the composition document itself. param is lang.

  • Name
    translation_invalid
    Type
    400
    Description

    One or more translation strings broke the contract — a malformed key, a non-string, or a string over its cap. param is strings, and error.errors is keyed by string key with one message each.

Suppliers and data requests

  • Name
    supplier_already_linked
    Type
    409
    Description

    The address already holds a pending or linked supplier link with your team, or a resend was tried on a link that has been accepted. A revoked link releases the address.

  • Name
    link_revoked
    Type
    409
    Description

    A rename or resend on a revoked supplier link, or a data request opened against one. Invite the supplier afresh.

  • Name
    data_request_not_open
    Type
    409
    Description

    A draft or submit on a data request that is not open or changes_requested, or a cancel on one already terminal.

  • Name
    data_request_not_submitted
    Type
    409
    Description

    A review, apply or return on a data request that is not submitted.

  • Name
    review_incomplete
    Type
    409
    Description

    An apply with no value accepted, or a return with no value rejected.

  • Name
    selection_invalid
    Type
    400
    Description

    An id in a bulk data request's selection cannot be taken — not your passport, another level or ontology, an ambiguous friendly id, another pinned version. param is selection, and error.errors is keyed by the offending id. Nothing is opened.

  • Name
    selection_too_large
    Type
    400
    Description

    The selection names more than 500 passports.

  • Name
    data_request_set_creating
    Type
    409
    Description

    An answers upload on a bulk request whose children are not made yet — poll its creation job first.

  • Name
    data_request_set_closed
    Type
    409
    Description

    A cancel, submit_all, apply_all, upload or fill on a bulk request that is already closed.

  • Name
    nothing_to_submit
    Type
    409
    Description

    submit_all on a bulk request with no child left to answer.

  • Name
    nothing_to_apply
    Type
    409
    Description

    apply_all on a bulk request with no submission waiting.

  • Name
    identifier_missing
    Type
    409
    Description

    fulfil/match on a bulk request the customer marked no identifier on — pair the rows by hand with fulfil/fill.

Webhooks

  • Name
    webhook_url_invalid
    Type
    400
    Description

    The endpoint URL is not https, carries userinfo, is too long, or resolves into a blocked range.

  • Name
    enabled_events_invalid
    Type
    400
    Description

    An unknown event type, or "*" mixed with concrete types.

  • Name
    webhook_endpoint_limit_reached
    Type
    400
    Description

    The team already holds 16 endpoints.

Files and uploads

  • Name
    file_already_exists
    Type
    409
    Description

    A concurrent upload took the filename; retry to get the next free name.

  • Name
    folder_already_exists
    Type
    409
    Description

    A folder of that name already exists in the directory.

  • Name
    file_not_previewable
    Type
    404
    Description

    The file exists but is not a spreadsheet the preview can parse — distinct from resource_missing.

  • Name
    upload_incomplete
    Type
    400
    Description

    complete listed fewer parts than the multipart upload was planned with; the message names the missing part numbers.

  • Name
    upload_completed
    Type
    409
    Description

    A second complete, or a DELETE after completing, on a multipart upload.

Synex Lab

  • Name
    lab_not_enabled
    Type
    409
    Description

    The team hasn't enabled Synex Lab.

  • Name
    transforms_not_enabled
    Type
    403
    Description

    The deployment's transform allowlist does not include this team.

  • Name
    preview_mismatch
    Type
    409
    Description

    A transform preview expired or no longer matches the submitted AST.

  • Name
    not_rollback_eligible
    Type
    409
    Description

    Only the latest finished, non-rolled-back transform can be rolled back; check each run's rollback_eligible.

  • Name
    not_exportable
    Type
    409
    Description

    Only query runs can be exported.

  • Name
    export_not_ready
    Type
    409
    Description

    The export is still being written. Poll the run rather than retrying the download.

The query pipeline — POST /v1/lab/query, and the compile stage of transform preview and execute — carries {path, message, …} rows in error.errors pointing at the offending stage of the AST:

  • Name
    invalid_ast
    Type
    422
    Description

    The AST failed validation — a malformed or unknown node. Also a request body that is not an AST object.

  • Name
    compile_error
    Type
    422
    Description

    The AST is well-formed but could not be compiled to a query.

  • Name
    ambiguous_property
    Type
    422
    Description

    A property name resolves to conflicting types across the selected ontologies. Reference it by id; errors[].candidates lists the conflicting definitions.

  • Name
    too_many_groups
    Type
    422
    Description

    A grouped query produced more groups than the cap. Group by a lower-cardinality property, or add filters.

  • Name
    bad_cursor
    Type
    422
    Description

    The ast.cursor keyset token is malformed or stale — restart from the first page.

  • Name
    query_timeout
    Type
    408
    Description

    Typed api_error. The query exceeded the statement timeout — narrow the filter, reduce the page size, or use an export.

  • Name
    forbidden_ontology
    Type
    403
    Description

    Typed permission_error. A source ontology is not accessible. An id that does not exist and one belonging to another team are deliberately indistinguishable.

The DSL authoring tools answer with five codes of their own, all 422:

  • Name
    query_empty
    Type
    422
    Description

    dsl_text is blank on POST /v1/lab/dsl/parse.

  • Name
    parse_error
    Type
    422
    Description

    The PassQL failed to parse. error.errors carries positioned rows — message, offset, length, expected.

  • Name
    unknown_ontology
    Type
    422
    Description

    A from clause named an ontology that does not exist in this team. offset and length are null.

  • Name
    ambiguous_ontology
    Type
    422
    Description

    An ontology name matched more than one ontology; errors[].expected lists the candidate ids. Address it by id.

  • Name
    unprintable_ast
    Type
    422
    Description

    dsl/print was handed an AST it cannot render back to PassQL.

Codes inside errors

A rejected passport value — on a passport PATCH, a data request submit, or an import row — is reported per property. The envelope's code is value_invalid; look for these in errors[<property id>][].code, each beside property_id, property_name, message and raw. A failure in one translation of a translated value also carries language:

  • Name
    invalid_text / invalid_number / invalid_boolean
    Type
    nested
    Description

    The value is not of the property's type.

  • Name
    invalid_measurement / invalid_range / invalid_file
    Type
    nested
    Description

    The value is not a numeric measurement, a range with a numeric start, or a file reference.

  • Name
    invalid_date
    Type
    nested
    Description

    A date value or cell is not a calendar day — impossible, out of range, or relative. See passports.

  • Name
    ambiguous_date
    Type
    nested
    Description

    A numeric date like 03/04/2026 reads both ways; send YYYY-MM-DD. See passports.

  • Name
    number_out_of_range
    Type
    nested
    Description

    A number is outside the storable magnitude 1E-130–9.9999…E+125.

  • Name
    value_too_deep
    Type
    nested
    Description

    The value nests past the storage ceiling.

  • Name
    value_too_long
    Type
    nested
    Description

    A translation is over its type's cap — 255 characters for short_text, 7,000 for long_text, 50 tags of at most 64 characters. Carries language.

  • Name
    value_too_large
    Type
    nested
    Description

    The value is too large to store. On a passport PATCH this is reported as 413 passport_too_large instead.

  • Name
    unsupported_language
    Type
    nested
    Description

    A translated value is keyed by something other than one of the 24 official EU language codes, in lowercase.

  • Name
    not_importable
    Type
    nested
    Description

    The property carries no writable value — a category divider or a computed composite.

Type-check each element before reading .code. Naming a property id the passport's ontology does not define yields plain message strings rather than detail objects, and one request can return both element types in the same map. A data request submit mixes them the same way: a missing answer, a file that is not your team's and a property the request never asked for arrive as strings, while a value the definition refuses is a detail object. Treat a string element as a message and an object as a detail.

Over-deep template structures and composition layouts are caught by request validation before they reach storage, so they arrive as 400 parameter_invalid with the depth explained in errors — not as value_too_deep.

Handling guidance

  • 4xx — fix the request; retrying it unchanged will fail again. The two exceptions are a 409 in-flight idempotency conflict and a 429.
  • 429 — wait the number of seconds in Retry-After, then retry. See Rate limits.
  • 5xx — retry with exponential backoff, reusing the same Idempotency-Key so a write can't be duplicated. One exception: a 5xx from POST /v1/data_requests with a recipient means the request and its pending supplier link already exist and only the invitation failed to send, so a retry is 409 supplier_already_linked — find the request and resend the invitation on its link instead.

When something looks wrong on our side, log request_id alongside your own correlation id, and include error_id if the response carried one. Those two values let support identify the exact request and failure without asking you to reproduce it.

Was this page helpful?