> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ndi.nace.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Access labels and coverage

> Controlling file visibility and understanding filtered results

Access labels are your organization's vocabulary for controlling file visibility
within workspaces. They are metadata tags — not roles or permissions — that gate
which files a query or tool can see.

***

## Organizing labels

Create labels in your organization's catalog:

```bash theme={"dark"}
curl -X POST "$NDI_BASE_URL/v1/access-labels" \
  -H "X-API-Key: $NDI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "financial",
    "description": "Financial documents and extracts"
  }'
```

The name is immutable after creation. The `PATCH` endpoint updates only
`description`. Labels in the catalog are reusable: multiple workspaces can
include the same label.

***

## Workspace-level labels

When you create a workspace, you freeze a subset of your organization's label
catalog as the workspace's **allowed set**:

```bash theme={"dark"}
curl -X POST "$NDI_BASE_URL/v1/workspaces" \
  -H "X-API-Key: $NDI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "q4-audit",
    "domain_slug": "generic",
    "access": {
      "labels": ["financial", "audit-only"],
      "default_label": "financial"
    }
  }'
```

A file's labels must be a subset of the workspace's allowed labels. This set
is immutable: changing it requires a new workspace.

`default_label` is applied to files uploaded without an explicit label.

***

## File-level labels

When you upload a file into a workspace, assign labels from the allowed set via
the `metadata` JSON form field:

```bash theme={"dark"}
curl -X POST "$NDI_BASE_URL/v1/workspaces/$WS/files" \
  -H "X-API-Key: $NDI_API_KEY" \
  -F "file=@document.pdf" \
  -F 'metadata={"path":"document.pdf","access_labels":["financial"]}'
```

***

## Coverage reporting

Every tool and query response includes `coverage` — how many candidates were
withheld by the access predicate and which labels would unlock them:

```json theme={"dark"}
{
  "coverage": {
    "restricted_candidates": 12,
    "labels_required": ["audit-only"]
  },
  "hits": [...]
}
```

* **`restricted_candidates`** — Files visible in scope but withheld by your key's access predicate.
* **`labels_required`** — Labels your key would need to see the withheld files.

This transparency prevents silent data loss: you know when restricted files exist even if you cannot read them.

***

## Your key's scope

An API key's access predicate is set when the key is created. When you call a
tool in a workspace, the API filters results to files visible to your key and
returns `coverage` describing what was withheld.

Labels use `AND` logic: a file is visible only if all of the key's required
visibility labels are present on the file.

***

## Label catalog management

List labels:

```bash theme={"dark"}
curl "$NDI_BASE_URL/v1/access-labels" \
  -H "X-API-Key: $NDI_API_KEY"
```

Update description:

```bash theme={"dark"}
curl -X PATCH "$NDI_BASE_URL/v1/access-labels/$LABEL_ID" \
  -H "X-API-Key: $NDI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"description": "Updated description"}'
```

Delete (only if not referenced by any workspace):

```bash theme={"dark"}
curl -X DELETE "$NDI_BASE_URL/v1/access-labels/$LABEL_ID" \
  -H "X-API-Key: $NDI_API_KEY"
```

***

## Automatic classification at ingestion

When a workspace declares labels, NDI automatically classifies each logical
document produced during ingestion using the label descriptions as prompts.

**How it works:**

1. After a document file is parsed and split into logical documents, a
   classification step reads each document's markdown content.
2. An LLM call compares the content against your label descriptions and assigns
   the best matching label — or marks the document as intentionally unlabelled
   (`null` label, visible to everyone) when no label fits.
3. If classification fails for any reason, the document falls back to
   `default_label` so ingestion always completes.

**Mixed-label sources:** A single uploaded file can split into logical documents
with different labels (e.g. a board packet whose first section is public and
whose appendix is privileged). Reading the source file directly requires holding
every label present on any of its logical documents.

**Label descriptions matter:** The description you write for each label is the
classifier's prompt. Clear, specific descriptions produce better assignments.

**Skipped for unfiltered workspaces:** If a workspace declares no labels,
no classification pass runs and every file is visible to all callers.
