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 theX-Request-Idheader. Always present. Quote it when contacting support.
- Name
error_id- Type
- string
- Description
5xxonly. Anerr_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
5xxand most4xx. Optional; see the note below.
- Name
errors- Type
- object
- Description
Per-field details on validation failures (for example, property id → messages).
Example error
{
"error": {
"type": "invalid_request_error",
"code": "resource_missing",
"message": "Passport not found.",
"param": "passport",
"request_id": "req_9f2c1ab34d5e6f70",
"sentry_id": "6fb2138a0f364dffa0729f63abba46ac"
}
}
sentry_id is best-effort and not a severity signal. It is omitted for
failures rejected before they reach the API — unmatched routes, unsupported
methods, request validation, rate limits, and requests sent with no
credentials — and the same code can arrive with or without one depending on
where it was raised. Never branch on its presence; branch on type and
code. error_id is the reliable marker of a server-side failure.
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 revoked team membership.
- Name
permission_error- Type
- 403
- Description
The key lacks the required ability, ontology scope, or team role.
- Name
rate_limit_error- Type
- 429
- Description
Too many requests — honor the
Retry-Afterheader.
- 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.
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. Six Lab authoring endpoints used to answer with bare framework bodies — {"valid": false, "errors": [...]} or {"message": "..."} — so a client could not write one error handler for the API. Every non-2xx body from those endpoints is now the envelope above, with the editor-oriented detail rows under error.errors, so the positioned parse feedback an editor underlines with is still there, in the same place it is everywhere else.
Success bodies are untouched: query/validate still answers {valid: true, mode} and dsl/parse still answers {valid: true, ast, kind}.
The status is 422 rather than 400. Handle it the same way — fix the input and resubmit; retrying it unchanged will fail again.
Five codes come with it:
- Name
query_empty- Type
- code
- Description
No query text was supplied.
- Name
parse_error- Type
- code
- Description
The PassQL failed to parse.
error.errorscarries positioned rows —message,offset,length,expected.
- Name
unknown_ontology- Type
- code
- Description
A
fromclause named an ontology that does not exist in this team.
- Name
ambiguous_ontology- Type
- code
- Description
An ontology name matched more than one ontology; address it by id.
- Name
unprintable_ast- Type
- code
- Description
dsl/printwas handed an AST it cannot render back to PassQL.
error.type is safe to branch on across the whole API. Two Lab values used to
be derived from the status band alone and were wrong — 403 forbidden_ontology reported invalid_request_error instead of
permission_error, and 408 query_timeout reported it instead of
api_error. Both are correct now.
Common codes
- Name
resource_missing- Type
- code
- Description
The id doesn't exist in your team — resources in other teams read as missing.
- Name
parameter_invalid- Type
- code
- Description
A request field failed validation;
paramanderrorssay which.
- Name
value_invalid- Type
- code
- Description
A passport property value was rejected;
errorsmaps property id → messages.
- Name
invalid_date- Type
- code
- Description
A
datevalue or cell is not a calendar day — impossible, out of range, or relative. See passports.
- Name
ambiguous_date- Type
- code
- Description
A numeric
datelike03/04/2026reads both ways; sendYYYY-MM-DD. See passports.
- Name
template_not_draft- Type
- code
- Description
Completed templates are immutable; only drafts can be edited or deleted.
- Name
template_is_draft- Type
- code
- Description
Drafts can't be forked or instantiated — complete the template first.
- Name
passport_exists_at_level- Type
- code
- Description
A singular level already has a passport under that parent.
- Name
friendly_id_taken- Type
- code
- Description
The friendly id is already used within the parent.
- Name
composition_missing / composition_exists- Type
- code
- Description
No composition exists yet · a composition already exists.
- Name
composition_too_large- Type
- code
- Description
413 · the composition layout exceeds the 400 KB storage ceiling. Trim it and retry.
- Name
file_already_exists- Type
- code
- Description
409 · a concurrent upload took the filename; retry to get the next free name.
- Name
ability_not_scoped_to_resource- Type
- code
- Description
The key's ontology scope doesn't cover this resource.
- Name
lab_not_enabled- Type
- code
- Description
The team hasn't enabled Synex Lab.
- Name
preview_mismatch- Type
- code
- Description
A transform preview expired or no longer matches the submitted AST.
- Name
not_rollback_eligible- Type
- code
- Description
Only the latest completed, non-rolled-back transform can be rolled back.
- Name
invalid_cursor- Type
- code
- Description
The pagination cursor is malformed, tampered with, or replayed under a different set of filters.
- Name
metadata_invalid- Type
- code
- Description
The merged metadata bag broke its limits — 50 keys, 40-character keys, 500-character values.
paramnames the whole field or the offending entry.
Ontology drafts and versions
- Name
draft_missing- Type
- code
- Description
404 · no draft is open on this ontology.
- Name
draft_exists- Type
- code
- Description
409 · a draft is already open.
GETit, orDELETEit first.
- Name
draft_conflict- Type
- code
- Description
409 ·
base_updated_atis stale. Re-read the draft and reapply your changes.
- Name
version_conflict- Type
- code
- Description
409 ·
expected_latest_versionis stale. Re-readlatest_versionand retry.
- Name
structure_invalid- Type
- code
- Description
400 · the draft breaks the additive-change rules. One message per violation in
error.errors.structure.
- Name
ontology_not_migrated- Type
- code
- Description
409 · the ontology predates versioning and has not been backfilled, so its draft cannot be published.
API keys
- Name
ability_not_grantable- Type
- code
- Description
400 ·
abilitiesnames something the calling key does not hold. See monotonic minting.
- Name
scope_not_grantable- Type
- code
- Description
400 · the requested ontology scope is wider than the caller's — including omitting it, or unscoping.
- Name
key_not_rollable- Type
- code
- Description
403 · the target key reaches further than the caller, so rolling it would hand over abilities.
Webhooks, suppliers and data requests
- Name
webhook_url_invalid- Type
- code
- Description
400 · the endpoint URL is not
https, carries userinfo, is too long, or resolves into a blocked range.
- Name
enabled_events_invalid- Type
- code
- Description
400 · an unknown event type, or
"*"mixed with concrete types.
- Name
webhook_endpoint_limit_reached- Type
- code
- Description
400 · the team already holds 16 endpoints.
- Name
supplier_already_linked- Type
- code
- Description
409 · your team is already linked to this supplier.
- Name
contact_not_found- Type
- code
- Description
400 ·
contact_emailis not a contact of that supplier. Nothing is created.
- Name
supplier_request_not_fulfilled- Type
- code
- Description
409 · the supplier has not answered yet, so there is nothing to map.
- Name
mapping_invalid- Type
- code
- Description
400 · an unknown field id, two fields mapped onto one property, or a rejected value.
error.errorsis keyed by field id.
- Name
ability_missing- Type
- code
- Description
403 · mapping without
passports:writescoped to the target passport's ontology.
Uploads, folders and Lab
- Name
upload_completed- Type
- code
- Description
409 · a second
complete, or aDELETEafter completing, on a multipart upload.
- Name
folder_already_exists- Type
- code
- Description
409 · a folder of that name already exists in the directory.
- Name
file_not_previewable- Type
- code
- Description
404 · the file exists but is not a spreadsheet the preview can parse — distinct from
resource_missing.
- Name
lab_role_required- Type
- code
- Description
403 · enabling or disabling Lab needs an admin/owner key owner.
- Name
audit_role_required- Type
- code
- Description
403 · the audit log and its settings need an admin/owner key owner. Distinct from
insufficient_permissions.
- Name
not_exportable- Type
- code
- Description
409 · only query runs can be exported.
- Name
export_not_ready- Type
- code
- Description
409 · the export is still being written. Poll the run rather than retrying the download.
Handling guidance
Branch on type first, then code. Never parse message — it is meant for
humans and its wording can change without a version bump.
- 4xx — fix the request; retrying it unchanged will fail again. The two exceptions are a
409in-flight idempotency conflict and a429. - 429 — wait the number of seconds in
Retry-After, then retry. See Rate limits. - 5xx — retry with exponential backoff, reusing the same
Idempotency-Keyso a write can't be duplicated.
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.