curl --request POST \
--url https://ndi-api.nace.ai/v1/workspaces/{workspace_id}/filtered-search \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"query": "<string>"
}
'{
"created_at": "2023-11-07T05:31:56Z",
"job_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"kind": "parse",
"status": "queued",
"deleted_at": "2023-11-07T05:31:56Z",
"effort": "<string>",
"error": {
"code": "invalid_request",
"message": "<string>",
"request_id": "<string>",
"retryable": true,
"detail": {}
},
"finished_at": "2023-11-07T05:31:56Z",
"force": true,
"idempotency_key": "<string>",
"name": "<string>",
"payload_expires_at": "2023-11-07T05:31:56Z",
"progress": {
"items_done": 0,
"items_failed": 0,
"items_total": 123,
"stage": "<string>",
"units_done": 0,
"units_total": 123
},
"project_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"query": "<string>",
"query_scope": {
"type": "workspace"
},
"result": {
"document": {
"page_count": 123,
"blocks": [
{
"content": "<string>",
"id": "<string>",
"type": "<string>",
"confidence": 123,
"image_url": "<string>",
"location": {
"page": 2,
"kind": "page_region",
"polygons": [
[
[
123,
123
]
]
]
}
}
],
"chunks": [
{
"content": "<string>",
"id": "<string>",
"location": {
"page": 2,
"kind": "page_region",
"polygons": [
[
[
123,
123
]
]
]
}
}
],
"markdown": "<string>",
"ocr": {
"lines": [
{
"bbox": {
"height": 123,
"left": 123,
"original_page": 123,
"page": 123,
"top": 123,
"width": 123
},
"confidence": 123,
"text": "<string>",
"chunk_index": 123,
"rotation": 0
}
],
"words": [
{
"bbox": {
"height": 123,
"left": 123,
"original_page": 123,
"page": 123,
"top": 123,
"width": 123
},
"confidence": 123,
"text": "<string>",
"chunk_index": 123,
"rotation": 0
}
]
},
"spreadsheet": {
"sheets": [
{
"cell_map_url": "<string>",
"sheet_name": "<string>"
}
]
},
"text": "<string>"
},
"file_name": "<string>",
"lane": "<string>",
"ocr_applied": true,
"units": 123,
"converted_pdf_url": "<string>",
"result_type": "parse"
},
"result_state": "available",
"source": {
"upload_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "upload"
},
"started_at": "2023-11-07T05:31:56Z",
"units": 0,
"workspace_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}{
"error": {
"code": "invalid_request",
"detail": null,
"message": "Request body has extra fields that this operation does not accept.",
"request_id": "req-01j9k2n3p4q5r6s7t8v9",
"retryable": false
}
}{
"error": {
"code": "invalid_request",
"detail": null,
"message": "Request body has extra fields that this operation does not accept.",
"request_id": "req-01j9k2n3p4q5r6s7t8v9",
"retryable": false
}
}filtered_search
Run a structured filter/rank/count query over the workspace’s metadata catalog. Cost class: job.
Send {type: "query", query, context?, path_prefix?, allow_clarification?}
to start a search: an agent translates the request into a validated catalog
query, and the result is a table — typed columns, matching components as
rows (each named by its uploaded file), an exact total_matches, and
evidence citations per extracted value. Count-style questions return the
total with no rows; “top N” questions return exactly N rows with the
pre-cutoff total. The result’s interpretation states how the agent read
the request. With allow_clarification set, a genuinely ambiguous request
may come back as a rejection instead: the succeeded result carries one
question in clarification (no rows, no applied_query, exhaustive
false) — filtered search keeps no session, so answer it by sending a new
initial query.
Send {type: "page", cursor} with an earlier result’s next_cursor
to fetch the next page. Pages re-execute the original query against the
current catalog under your current access — no agent runs, and
concurrent ingestion or deletion may shift later pages (the result’s
catalog_drifted says the underlying data changed). Only the API key
that ran the original query may page it; an expired or purged parent job
returns invalid_request.
Read exhaustive before treating a result as a census: when false, the
matches shown are valid but completeness is not guaranteed (uncataloged
files in scope, degraded extractions, or a residual content condition).
Read field_readability alongside it: a non-empty entry means the
predicate could not be evaluated on part of the population, so an empty or
thin result may reflect unreadable data rather than absence.
Monetary values always carry their ISO currency and are never converted;
comparing amounts across currencies is unsupported in this version.
curl --request POST \
--url https://ndi-api.nace.ai/v1/workspaces/{workspace_id}/filtered-search \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"query": "<string>"
}
'{
"created_at": "2023-11-07T05:31:56Z",
"job_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"kind": "parse",
"status": "queued",
"deleted_at": "2023-11-07T05:31:56Z",
"effort": "<string>",
"error": {
"code": "invalid_request",
"message": "<string>",
"request_id": "<string>",
"retryable": true,
"detail": {}
},
"finished_at": "2023-11-07T05:31:56Z",
"force": true,
"idempotency_key": "<string>",
"name": "<string>",
"payload_expires_at": "2023-11-07T05:31:56Z",
"progress": {
"items_done": 0,
"items_failed": 0,
"items_total": 123,
"stage": "<string>",
"units_done": 0,
"units_total": 123
},
"project_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"query": "<string>",
"query_scope": {
"type": "workspace"
},
"result": {
"document": {
"page_count": 123,
"blocks": [
{
"content": "<string>",
"id": "<string>",
"type": "<string>",
"confidence": 123,
"image_url": "<string>",
"location": {
"page": 2,
"kind": "page_region",
"polygons": [
[
[
123,
123
]
]
]
}
}
],
"chunks": [
{
"content": "<string>",
"id": "<string>",
"location": {
"page": 2,
"kind": "page_region",
"polygons": [
[
[
123,
123
]
]
]
}
}
],
"markdown": "<string>",
"ocr": {
"lines": [
{
"bbox": {
"height": 123,
"left": 123,
"original_page": 123,
"page": 123,
"top": 123,
"width": 123
},
"confidence": 123,
"text": "<string>",
"chunk_index": 123,
"rotation": 0
}
],
"words": [
{
"bbox": {
"height": 123,
"left": 123,
"original_page": 123,
"page": 123,
"top": 123,
"width": 123
},
"confidence": 123,
"text": "<string>",
"chunk_index": 123,
"rotation": 0
}
]
},
"spreadsheet": {
"sheets": [
{
"cell_map_url": "<string>",
"sheet_name": "<string>"
}
]
},
"text": "<string>"
},
"file_name": "<string>",
"lane": "<string>",
"ocr_applied": true,
"units": 123,
"converted_pdf_url": "<string>",
"result_type": "parse"
},
"result_state": "available",
"source": {
"upload_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"type": "upload"
},
"started_at": "2023-11-07T05:31:56Z",
"units": 0,
"workspace_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}{
"error": {
"code": "invalid_request",
"detail": null,
"message": "Request body has extra fields that this operation does not accept.",
"request_id": "req-01j9k2n3p4q5r6s7t8v9",
"retryable": false
}
}{
"error": {
"code": "invalid_request",
"detail": null,
"message": "Request body has extra fields that this operation does not accept.",
"request_id": "req-01j9k2n3p4q5r6s7t8v9",
"retryable": false
}
}Authorizations
Headers
Replaying a request with the same key returns the original job with 200, never a second job.
200Path Parameters
Workspace identifier.
Query Parameters
Bounded inline wait. 0 (the default) returns 202 immediately. Above 0 the route returns 200 with a terminal job if the work finished in time, and 202 with the still-running job if it did not. Either way, read status.
0 <= x <= 300Body
- InitialFilteredSearch
- FilteredSearchPage
Start a filtered search: a natural-language filter/rank/count request.
1 - 4096Let the run finish with ONE clarifying question instead of a result, when the request is genuinely ambiguous and the ambiguity materially changes the result. The rejected result carries the question in clarification (no rows, no applied_query, exhaustive false); filtered search keeps no session, so answer it by issuing a new initial query. Off — the default — means the run always executes, stating its reading in interpretation.
20000Narrow the run to a folder subtree. It intersects with the access filter, never overrides it.
1024"query"Response
Successful Response
Every slow method returns one of these (spec 12 §1.4).
There is no /parse + /parse_async pair: every job-creating route
returns 202 with a queued job and accepts ?wait_seconds=, returning
200 with a terminal job if the work finished inside the window. The
caller's code path is the same either way — read status.
What a job is doing. Open enum — a new member is additive.
parse, split, classify, categorize, extract, ground, ingestion, reconciliation, kg_build, file_upload, file_replace, upload_session_complete, file_delete, workspace_delete, intelligent_search, qa_file, qa_tables, filtered_search, je_testing, deep_search_v2 cancelled is a first-class terminal status, not a failed variant.
Conflating them makes error-rate metrics lie and forces every client to string-match an error code.
queued, running, succeeded, failed, cancelled When the caller hid this run from listings, via delete_job. A soft delete: the job still reads by id and still counts toward GET /v1/usage — what it stops doing is appearing in GET /v1/jobs.
Which implementation ran this job, for the one kind whose request carries an effort (intelligent_search): 'fast' is fact-search, 'deep' (or the legacy 'balanced') is deep-search and the console's QA chat — a deep-search run under another surface, not a fourth implementation, so this field cannot tell those two apart. Null for every other kind. Read from the stored request, so it rides the same retention path as query and result: null once result_state leaves available.
Present only on failed.
Show child attributes
Show child attributes
The request's own force flag, echoed regardless of outcome. Present for kinds whose request carries one (e.g. kg_build, where a true value cleared the durable store before the job ran) so a caller can tell that apart from a build that failed leaving the prior store untouched — result alone cannot, since it is absent on failure either way.
Caller-chosen display name (the console's 'project'). Mutable via PATCH /v1/jobs/{job_id}.
Absent until running.
Show child attributes
Show child attributes
Caller-minted grouping id shared by jobs submitted together. Filterable on GET /v1/jobs.
The question this job was asked, for the search kinds that carry one (intelligent_search, qa_file, qa_tables, filtered_search); null for every other kind. Read from the stored request, so it rides the same retention path as result: null once result_state leaves available. Narrow by design — the frozen access context never reaches the wire. File scope is summarised separately as query_scope.
The question ran against the whole workspace.
- WorkspaceQueryScope
- FileQueryScope
- PathPrefixQueryScope
- FilesQueryScope
Show child attributes
Show child attributes
Present only on succeeded, and only while retained.
- ParseResult
- SplitResult
- ClassifyResult
- CategorizeResult
- ExtractResult
- GroundResult
- IngestionResult
- ReconciliationResult
- KnowledgeGraphBuildResult
- UploadFileResult
- ReplaceFileResult
- DeleteFileResult
- WorkspaceDeleteResult
- IntelligentSearchResult
- QaFileResult
- QaTablesResult
- FilteredSearchResult
- JetResult
- DeepSearchV2Result
Show child attributes
Show child attributes
Why Job.result is null.
available with a null result means the job has not succeeded yet;
expired means retention dropped it; not_retained means the client is
provisioned zero_retention and already had its one chance to read it.
A late poll returns the job with a state, never a 404 — "this expired" and
"this never existed" are a scheduling problem and a bug respectively, and a
404 makes them identical.
available, expired, not_retained Bytes staged through POST /v1/uploads. Single-use, TTL-bound.
- UploadSource
- UrlSource
- WorkspaceFileSource
- ParseResultSource
Show child attributes
Show child attributes
The workspace this job reads. Null for a document operation whose source is an upload, a URL, or a prior parse result.