Skip to main content

Error response format

When a request fails, NDI returns a typed error:
Fields:
  • 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). null when the code carries no additional payload.
  • retryable — Whether retrying the identical request could succeed.
  • request_id — Per-request correlation id; matches the X-Request-Id response 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

For 429 or 5xx errors:
  1. Include Idempotency-Key to ensure replay is safe.
  2. Wait with exponential backoff: wait = min(2^attempt, 60) seconds.
  3. 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.