Error response format
When a request fails, NDI returns a typed error:code— Machine-readable identifier. Treat it as an open enum so new codes remain compatible.message— Human-readable explanation.detail— Operation-specific context (e.g., validation field errors).nullwhen the code carries no additional payload.retryable— Whether retrying the identical request could succeed.request_id— Per-request correlation id; matches theX-Request-Idresponse header.
Error codes
invalid_request
Missing required field, wrong type, value out of range, or extra field on a
strict model. detail.errors lists each refused field. POST /v1/systemone
also returns detail.issues: one {path, message} per refused field, by dotted
path (questions.risk.criteria).
POST /v1/workspaces/{workspace_id}/knowledge-graph/builds also returns this
code when the workspace has no ingested files. That refusal happens before a
job or workflow is created. Ingest files first, then build.
HTTP: 422
unsupported_file_type
Source format not supported by the upload boundary or requested operation, or
a conclusive byte signature conflicts with the filename extension. Single-shot
and staged uploads fail synchronously before a workspace file or operation job
is created. A resumable upload reports the same error on its completion job,
without creating a workspace file or ingestion job. Change the input format or
select an operation that supports it; the same request is not retryable.
Multipart Content-Type is not authoritative.
HTTP: 422
file_too_large
Uploaded file exceeds the size limit for POST /v1/uploads.
HTTP: 413
path_conflict
A workspace file already exists at that path and on_conflict is reject.
HTTP: 409
file_not_ingested
A synchronous workspace tool call (such as read-file, qa-file, or
query-tables) ran against a file that has not been ingested. Search only
sees ingested files, so it skips un-ingested ones instead of returning this error.
HTTP: 409
upload_expired
The granular upload handle expired, or a resumable upload session expired,
already completed, or was aborted.
HTTP: 409
job_not_cancellable
The job is already terminal.
HTTP: 409
job_cancelled
The job, or the job it ran for, was cancelled. A cancelled job serves no
results and is not charged, except for results already delivered before the cancel, so reads of its result rows, groundings, presentations or result pages are refused.
GET /v1/jobs/{id} still returns the job, with result_state: "cancelled". It is also the
error.code on a job you cancelled.
HTTP: 409
result_expired
The job row exists but its payload was deleted by retention.
HTTP: 410
range_not_satisfiable
A byte-range request (artifact or crop) is outside the object.
HTTP: 416
unauthorized
API key missing, invalid, or revoked.
HTTP: 401
access_denied
Operation not allowed for this key (e.g., session belongs to a different key).
HTTP: 403
workspace_not_found
Workspace does not exist (or the key cannot see it).
HTTP: 404
file_not_found
File or output does not exist.
For a tool that takes a path, the path must be the file’s full path relative
to the workspace root, exactly as uploaded: not a bare file name, a folder, a
file id, or a path that starts with the workspace id. The error is final for
that path; do not retry it or guess variants. Find the real path with
folder metadata (name_contains) or take
source_path from a search result. See File paths.
HTTP: 404
job_not_found
Job does not exist for this key.
HTTP: 404
session_not_found
Intelligent-search session ID does not exist in this workspace, or it is the ID of a follow-up job rather than the thread root.
HTTP: 404
kg_not_built
Knowledge-graph tool called on a workspace with no built graph.
HTTP: 409
kg_build_in_progress
A knowledge-graph build is already running for this workspace.
HTTP: 409
ledger_understanding_not_found
The workspace has never produced a ledger-understanding report. Start one
with POST /v1/workspaces/{id}/ledger-understanding, or ingest a ledger
and let the workspace policy chain it.
HTTP: 404
ledger_understanding_in_progress
A ledger-understanding run is already in flight for this workspace. Wait
for that job, or read the current report with GET.
HTTP: 409
workspace_deleting
Operation attempted on a workspace that is currently being deleted.
HTTP: 409
session_busy
A QA (or deprecated deep-search) conversation is still answering a previous turn.
HTTP: 409
rate_limited
Per-key requests-per-minute limit hit.
HTTP: 429
concurrency_limit_reached
Per-key concurrent-jobs cap hit.
HTTP: 429
quota_exceeded
Your organization has used its included credit allowance and paid usage is not enabled.
New jobs that use credits are refused and not charged. Jobs you already started, and cancelled or failed ones, count against the allowance by the units they used, so the line holds even when a burst of jobs is accepted just under it. Uploading, replacing and deleting
files, and deleting workspaces, use no credits and still work. Retrying does not help:
contact support to enable paid usage.
HTTP: 402
corrupt_file
File could not be processed because it is corrupt or not the format its extension claims.
HTTP: Used in job failure error.code, not as an HTTP status. The job status is "failed".
not_implemented
This deployment does not offer the operation; for example, POST /v1/systemone
on a deployment without a decision model.
HTTP: 501
upstream_unavailable
A service NDI depends on, such as a model, failed or did not answer in time.
The response marks this code as retryable.
HTTP: 503
internal_error
Unexpected server error. The response marks this code as retryable.
HTTP: 500
HTTP status codes
Rate limits
Per API key:- Concurrent jobs: Configurable per key; when the cap is hit, new job-creating requests return
429 concurrency_limit_reached. - RPM (requests per minute): Enforced per key via Redis (no-ops when Redis is unavailable in local dev). When the cap is hit, requests return
429 rate_limited.
Retry strategy
For429 or 5xx errors:
- Include
Idempotency-Keyto ensure replay is safe. - Wait with exponential backoff:
wait = min(2^attempt, 60)seconds. - Retry up to 5 times before giving up.
wait_seconds timeout
When a job does not complete within ?wait_seconds= (max 300), the endpoint
returns 202 (not 408). The job continues running; poll it.