curl --request POST \
--url https://ndi-api.nace.ai/v1/workspaces/{workspace_id}/tools/folder-metadata \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"category": "<string>",
"directory": "/",
"include_tables": false,
"name_contains": "<string>",
"page_size": 100,
"recursive": false,
"start_after": "<string>"
}
'{
"categories": [
{
"count": 123,
"name": "<string>"
}
],
"coverage": {
"labels_required": [
"<string>"
],
"request_access_hint": "<string>",
"restricted_candidates": 0
},
"directory": "<string>",
"file_entries": [
{
"ingestion_status": "discovered",
"path": "<string>",
"size_bytes": 123,
"category": "<string>",
"kind": "<string>",
"uploaded_at": "2023-11-07T05:31:56Z"
}
],
"files_truncated": true,
"ingestion_summary": {},
"overall_files": 123,
"subdirectories": [
{
"name": "<string>",
"overall_files": 123,
"failed_files": 123,
"top_categories": [
{
"count": 123,
"name": "<string>"
}
]
}
],
"hint": "<string>",
"shared_columns": [
{
"appears_in": [
{
"component": "<string>",
"data_type": "<string>",
"path": "<string>",
"distinct_count": 123,
"max_value": "<string>",
"min_value": "<string>",
"row_count": 123
}
],
"name": "<string>",
"table_count": 123
}
],
"shared_columns_truncated": true,
"tables": [
{
"component": "<string>",
"path": "<string>",
"size_bytes": 123,
"column_count": 123,
"row_count": 123,
"table_name": "<string>"
}
],
"tables_truncated": true
}{
"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
}
}folder_metadata
Orient in a workspace directory: counts, category mix, and one page of files.
Cost class: fast.
Progressive by design — explore the corpus in bounded steps instead of one dump:
- Call with the defaults (non-recursive) on
/: the directory’s own files plus a summary per subdirectory (subtree file count, top categories, failed-ingestion count) — enough to pick a descent target. - Descend by calling again with
directory=<subdirectory>, or narrow withcategory=<name>(an exact category from the response),ingestion_status=<status>(e.g.failedto list exactly the files the failure counts advertised),kind=<lane>(tablefor every spreadsheet,document,media), orname_contains=(case-insensitive filename/path substring) — counts and listing both narrow, and the filters compose. Because the narrowed names come from subtree censuses, any narrow impliesrecursiveunlessrecursiveis sent explicitly. file_metadataon candidate files, thenread_file/qa_fileto act on them.
name_contains finds files by what they are called; the search tools
find them by what they contain — and only a listing can find a file
whose ingestion failed, since it was never indexed.
include_tables=true adds a table census: one row per queryable tab
in scope (path, component, rows x columns) plus shared_columns — column
names appearing in several tables with each side’s profiled stats,
unverified join candidates to check with one query-tables call. The one
orientation call that replaces a file-metadata call per file; slower than a
bare call (it reads ingestion profiles), so use it once and then work
per-file. Implies recursive the way the narrows do.
A directory that names a file is refused with invalid_request
pointing at file_metadata; an unfiltered call that matches nothing
answers with a hint naming the nearest existing ancestor and its
subdirectories, so a typo is never a dead end.
file_entries is the listing — each entry carries ingestion status,
size, category, and kind (ingestion lane), so a folder can be triaged
without per-file calls. It comes one page_size page at a time
(files_truncated reports a cut); resume with start_after=<last path>, or better, narrow the scope. recursive=true still returns
whole-subtree counts and listing when a full census is genuinely needed.
Everything is filtered by both the caller’s access predicate and the
requested directory — the two compose as an intersection.
curl --request POST \
--url https://ndi-api.nace.ai/v1/workspaces/{workspace_id}/tools/folder-metadata \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"category": "<string>",
"directory": "/",
"include_tables": false,
"name_contains": "<string>",
"page_size": 100,
"recursive": false,
"start_after": "<string>"
}
'{
"categories": [
{
"count": 123,
"name": "<string>"
}
],
"coverage": {
"labels_required": [
"<string>"
],
"request_access_hint": "<string>",
"restricted_candidates": 0
},
"directory": "<string>",
"file_entries": [
{
"ingestion_status": "discovered",
"path": "<string>",
"size_bytes": 123,
"category": "<string>",
"kind": "<string>",
"uploaded_at": "2023-11-07T05:31:56Z"
}
],
"files_truncated": true,
"ingestion_summary": {},
"overall_files": 123,
"subdirectories": [
{
"name": "<string>",
"overall_files": 123,
"failed_files": 123,
"top_categories": [
{
"count": 123,
"name": "<string>"
}
]
}
],
"hint": "<string>",
"shared_columns": [
{
"appears_in": [
{
"component": "<string>",
"data_type": "<string>",
"path": "<string>",
"distinct_count": 123,
"max_value": "<string>",
"min_value": "<string>",
"row_count": 123
}
],
"name": "<string>",
"table_count": 123
}
],
"shared_columns_truncated": true,
"tables": [
{
"component": "<string>",
"path": "<string>",
"size_bytes": 123,
"column_count": 123,
"row_count": 123,
"table_name": "<string>"
}
],
"tables_truncated": true
}{
"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
Path Parameters
Workspace identifier.
Body
Request body for folder-metadata.
Non-recursive by default: the first call on a directory orients (its own files plus per-subdirectory summaries), and the caller descends or narrows from there instead of receiving the whole subtree at once.
A category or ingestion_status narrow implies recursion unless
recursive is sent explicitly: the censuses those names come from
(root categories, subdirectory top_categories / failed_files,
ingestion_summary) count subtrees, so the natural follow-up call must
scope the same subtree — otherwise narrowing by a subtree fact from the
non-recursive root would answer an inexplicable zero. An explicit
recursive=false alongside a narrow is honored as sent.
1024The only thing that says whether a file is searchable.
expired cannot be derived from the hash pair: retention clears
ingested_hash, so an aged-out file and a never-ingested file look
identical. They call for different customer actions, so the status carries
the distinction the hashes cannot.
discovered, queued, ingesting, ingested, stale, expired, failed, not_required The processing lane a file routes through — exactly one per file.
DOCUMENT covers everything the categorize pipeline can split into
logical documents (PDF, Office, images, text, markdown, HTML, email);
TABLE covers spreadsheets and CSV; MEDIA covers audio and video,
which become one speaker-attributed timeline plus sampled frames;
JSON preserves arbitrary nested structure and may add a table
projection for a top-level record array. The diagram lane remains an
explicit non-goal: its extensions map to no
lane, which surfaces as a typed per-file unsupported_file_type
outcome.
document, table, media, json 1 - 2561 <= x <= 5001024Response
Successful Response
Response body for folder-metadata.
Counts are filtered by both the caller's access predicate and the requested
directory — the two compose as an intersection so the view an agent sees
is neither wider than its access gate nor wider than its declared scope.
file_entries lists the visible source files the counts cover (same
scope and recursion as the counts), path-sorted, one page at a time
(page_size); files_truncated reports a cut page — resume with
start_after=<last path>, or narrow with category= / a subdirectory.
Source paths, never ingestion derivatives: derivative paths are reported by
GET /v1/workspaces/{workspace_id}/files/{file_id}/metadata.
Show child attributes
Show child attributes
What the access gate withheld, in counts and label names only.
Never a name, path, or fragment of withheld content. Naming the labels leaks nothing the caller cannot already read from the workspace config, and it turns "some results were withheld" into an actionable request.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
40Show child attributes
Show child attributes
500Show child attributes
Show child attributes