# Context pack: Filter and refine search

Source: https://nordvec.com/docs/guides/how-to/filter-search
Pack: https://nordvec.com/docs/packs/how-to/filter-search

This pack bundles one Nordvec guide with the guides it builds on and the guides it links to, in reading order, so an assistant reading it meets no reference it cannot follow.

## Contents

1. [Documents & search](https://nordvec.com/docs/guides/concepts/documents) (builds on)
2. [Filter and refine search](https://nordvec.com/docs/guides/how-to/filter-search) (this guide)
3. [List and retrieve documents](https://nordvec.com/docs/guides/how-to/list-documents) (linked from this guide)

---

# Documents & search
Source: https://nordvec.com/docs/guides/concepts/documents

Documents are the searchable unit in Nordvec. Ingest them, then find them with full-text search over the API.

A **document** is the unit Nordvec searches over: a file, a page, a message, an
email, whatever you ingest. Once a document is processed it becomes searchable
and can be cited in an answer.

## Ingesting documents [#ingesting-documents]

Documents enter your workspace three ways:

- **Connectors** sync from a source you own (the connector owner). New and
  changed documents are picked up automatically. See the
  [connector catalogue](/docs/guides/connectors).
- **Uploads** of files you add in the app.
- **Push** sends a document to Nordvec directly from your own system. See
  [Push documents](/docs/guides/how-to/push-documents).

Whichever way it arrives, Nordvec processes the document, splits it into passages, and indexes
it for retrieval. Your documents are stored and searched in the EU.

A document has a `status`: `processing` while it is being indexed, `indexed`
once every processing step has finished, and `failed` when it could not be processed.

## Who sees a document [#who-sees-a-document]

A document from a connector is private to the person who connected it until
that person shares it. An API key belongs to the workspace, so it sees what the
workspace shares and never a person's unshared documents.

## Searching [#searching]

`/documents/search` is **full-text** search over the title and the whole text
of every document you can see, however long the document is. Words match in the
form you write them, in any language, and accents are ignored on both sides, so
`cafe` finds `café`. There is no stemming: `invoice` does not match `invoices`.

Search runs standalone, without conversation context. It returns ranked
documents, each with a snippet from its best-matching passage and a relevance
score from 0 up to, but never reaching, 1.

```bash
curl https://nordvec.com/api/v1/documents/search \
  -H "Authorization: Bearer $NORDVEC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "renewal terms", "limit": 10 }'
```

You can also list documents (`/documents/list`) or fetch one by id
(`/documents/{id}`). See the [API reference](/docs/api) for the full set of
parameters and response shapes.

### Try it [#try-it]

Run a query against a small demo corpus. This searches six sample documents in
your browser, ranked and sourced, the same shape a real search returns.

<LiveSearchDemo />

Semantic retrieval, which matches passages by meaning rather than exact words,
is what powers a [conversation's](/docs/guides/concepts/conversations) cited
answers. The search endpoint itself matches words.

<Callout>
Search only ever returns documents the caller is allowed to see. Access is
enforced in the database, not in application code.
</Callout>

## Content types [#content-types]

A document can carry a knowledge type in `content_type` (for example
`decision`, `runbook`, `policy` or `faq`). A pushed document sets it with the
push API's `type`; a document with no type has `content_type: null`. Search and
list both filter on it with `contentType`.

## Where documents are cited [#where-documents-are-cited]

Search gives you passages to work with directly. When you want a written answer
instead of raw passages, a [conversation](/docs/guides/concepts/conversations)
retrieves over the same documents and cites them in its reply.


---

# Filter and refine search
Source: https://nordvec.com/docs/guides/how-to/filter-search

Narrow a document search with datasource, provider, type and date filters, and read the ranked results.

`/documents/search` runs full-text search over the title and the whole text of
your documents and returns the best matches, each with a relevance score and the
source document it came from. This guide shows how the query matches, how to
narrow the results with filters, and how to read the response.

## The request [#the-request]

Only `query` is required. Everything else narrows or bounds the results.

```bash
curl https://nordvec.com/api/v1/documents/search \
  -H "Authorization: Bearer $NORDVEC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "renewal terms",
    "limit": 20,
    "datasource": "contracts",
    "sourceProvider": "google",
    "createdAfter": "2026-01-01T00:00:00Z",
    "createdBefore": "2026-07-01T00:00:00Z"
  }'
```

| Field | Type | Notes |
|---|---|---|
| `query` | string | Required. 1 to 500 characters. Quoted phrases, `or` and a leading `-` to exclude a word are understood. |
| `limit` | integer | Optional. 1 to 50, default 20. How many results to return. |
| `datasource` | string | Optional. Restrict to one datasource by its slug (up to 200 characters). |
| `sourceProvider` | string | Optional. Restrict to one connector provider, for example `google`, `sharepoint` or `slack`. |
| `createdAfter` | string | Optional. ISO 8601 timestamp with an offset; only documents created at or after it. |
| `createdBefore` | string | Optional. ISO 8601 timestamp with an offset; only documents created at or before it. |
| `contentType` | string | Optional. Restrict to one knowledge type, the `type` a pushed document or a knowledge file declares (for example `policy` or `runbook`). |

<Callout>
Every filter is combined with AND: a document must match the query **and** every
filter you supply. Leave a filter out to widen the search.
</Callout>

## How the query matches [#how-the-query-matches]

- **The whole document is searched.** The title and every passage of the text
  count, however long the document is.
- **Words match in the form you write them, in any language.** There is no
  stemming: `invoice` does not match `invoices`, and `tilbagebetaling` does not
  match `tilbagebetalingen`. To catch several forms, join them with `or`.
- **Accents are ignored on both sides.** `cafe` finds `café`, and `børnehave`
  and `bornehave` find each other. Case is ignored too.
- **Operators.** Put words in double quotes to match them as a phrase, write
  `or` between words to match either, and put `-` before a word to leave out
  documents that contain it.

## Try it [#try-it]

Signed in, you can run a search over your own documents from this page. Change
the query in the API reference to try your own.

Try it in the API reference: [`POST /api/v1/documents/search`](https://nordvec.com/docs/api#tag/documents/POST/documents/search) (Search documents).

## The response [#the-response]

```json
{
  "results": [
    {
      "id": "0198f2a4-6c1e-7d30-b6a1-2f9d54c08a11",
      "title": "Acme Corp Master Services Agreement",
      "snippet": "Automatic **renewal**: the agreement continues for successive twelve month **terms** unless…",
      "score": 0.82,
      "datasource": "contracts",
      "source_provider": "google",
      "source_type": null,
      "mime_type": "application/pdf",
      "content_type": null,
      "status": "indexed",
      "created_at": "2026-02-14T09:00:00Z",
      "updated_at": "2026-02-14T09:00:00Z"
    }
  ],
  "totalCount": 7
}
```

Each result is one document. Its `snippet` is cut from the passage that matched
best, wherever that passage is in the text, with the matched words wrapped in
`**`; a word you typed without its accents is found and ranked, but may appear
unmarked in the snippet. The `score` runs from 0 up to, but never reaching, 1
(higher is more relevant), and the source document's metadata lets you trace
the result back. `totalCount` is how many documents matched in total, which may
be larger than the number of `results` you asked for with `limit`.

## Reading the results [#reading-the-results]

- **Results are ranked by relevance**, most relevant first. A document ranks by
  its best-matching passage. Use `score` to drop weak matches within one set of
  results; scores from different queries are not on the same scale.
- **`totalCount` vs `results.length`**: `results` holds up to `limit` items;
  `totalCount` is the full match count. If `totalCount` is much larger than your
  `limit`, tighten the `query` or add a filter; there is no second page of
  search results.
- **`status` tells you where the document is in processing.** A document is
  matched on its stored text, so one still `processing` can appear; `indexed`
  means every step finished. See
  [Documents & search](/docs/guides/concepts/documents) for the lifecycle.

<Callout>
Search only ever returns documents the caller is allowed to see. Access is
enforced in the database, not in application code, so a filter can never widen
what the caller sees. For an API key that is what the workspace shares; see
[Who sees a document](/docs/guides/concepts/documents#who-sees-a-document).
</Callout>

## Next steps [#next-steps]

<Cards>
  <Card title="Documents & search" href="/docs/guides/concepts/documents" />
  <Card title="List and retrieve documents" href="/docs/guides/how-to/list-documents" />
  <Card title="API reference" href="/docs/api" />
</Cards>


---

# List and retrieve documents
Source: https://nordvec.com/docs/guides/how-to/list-documents

Page through your documents with cursor pagination, filter and sort them, and fetch one or many by id.

Where [search](/docs/guides/how-to/filter-search) ranks documents by relevance to
a query, listing walks your whole corpus in order. Use it to sync, audit, or
build your own index over what Nordvec holds. Listing returns metadata only, no
document content.

## List with cursor pagination [#list-with-cursor-pagination]

`/documents/list` returns a page of documents plus an opaque `nextCursor`. Pass
that cursor back to get the next page, and stop when `hasMore` is `false`.

```bash
curl "https://nordvec.com/api/v1/documents/list?limit=50" \
  -H "Authorization: Bearer $NORDVEC_API_KEY"
```

```json
{
  "items": [
    { "id": "…", "title": "Q3 Financial Report", "status": "indexed", "datasource": "finance", "created_at": "2026-04-01T10:00:00Z" }
  ],
  "nextCursor": "eyJrIjoi…",
  "hasMore": true
}
```

Signed in, you can list the first page of your own documents from here:

Try it in the API reference: [`GET /api/v1/documents/list`](https://nordvec.com/docs/api#tag/documents/GET/documents/list) (List documents).

To walk every page, loop until `hasMore` is `false`, passing the previous
response's `nextCursor` each time, with the same `sort` and `direction`:

```bash
curl "https://nordvec.com/api/v1/documents/list?limit=50&cursor=eyJrIjoi…" \
  -H "Authorization: Bearer $NORDVEC_API_KEY"
```

<Callout>
The cursor is opaque, do not parse or construct it. Pass back exactly what the
previous response returned. An invalid cursor, or one from a different sort
order, is rejected.
</Callout>

## Filter and sort [#filter-and-sort]

All filters are optional and combine with AND. Sorting defaults to newest first.

| Field | Type | Notes |
|---|---|---|
| `limit` | integer | 1 to 200 (default 50). |
| `cursor` | string | Opaque cursor from the previous page. |
| `datasource` | string | Restrict to one datasource by its slug (up to 200 characters). |
| `status` | enum | `indexed`, `processing`, or `failed`. |
| `sourceProvider` | string | Restrict to one connector provider, for example `google` or `slack`. |
| `contentType` | string | Restrict to one knowledge type, for example `policy` or `runbook`. |
| `createdAfter` / `createdBefore` | string | ISO 8601 timestamps with an offset, both inclusive. |
| `sort` | enum | `createdAt` (default), `updatedAt`, or `title`. |
| `direction` | enum | `desc` (default) or `asc`. |

```bash
curl "https://nordvec.com/api/v1/documents/list?status=indexed&datasource=finance&sort=updatedAt&direction=desc&limit=100" \
  -H "Authorization: Bearer $NORDVEC_API_KEY"
```

### Which sort to walk with [#which-sort-to-walk-with]

- **A complete, one-shot enumeration**: `sort=createdAt`. The creation time
  never changes, so every document appears exactly once.
- **Incremental catch-up from a watermark**: `sort=updatedAt&direction=asc`. A
  document updated while you walk can appear twice, so upsert by `id`.
- **Display order**: `updatedAt` or `title` descending. A document updated
  between two pages can move past the cursor and be skipped, so do not use it
  to enumerate.

## Fetch a single document [#fetch-a-single-document]

`/documents/{id}` returns one document's metadata, processing state and text.
A long text can be read in windows: `contentOffset` and `contentMaxChars`
(counted in UTF-16 code units) select one window, and `content_range` reports
the window and the full length, so keep reading until `offset + length` reaches
`total`.

```bash
curl "https://nordvec.com/api/v1/documents/DOCUMENT_ID?contentMaxChars=100000" \
  -H "Authorization: Bearer $NORDVEC_API_KEY"
```

## Fetch many at once [#fetch-many-at-once]

To resolve up to 200 ids in one call, POST them to `/documents/batch` instead of
making one request per id. The batch returns metadata and processing state;
`content` is always `null`, so read the text with the single-document call.

```bash
curl https://nordvec.com/api/v1/documents/batch \
  -H "Authorization: Bearer $NORDVEC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "ids": ["DOCUMENT_ID_1", "DOCUMENT_ID_2"] }'
```

<Callout>
Listing, like search, only ever returns documents the caller is allowed to see.
`status` tells you where a document is in processing: `processing` while it is
still being indexed, `failed` when it could not be. See
[Documents & search](/docs/guides/concepts/documents) for the lifecycle.
</Callout>

## Next steps [#next-steps]

<Cards>
  <Card title="Filter and refine search" href="/docs/guides/how-to/filter-search" />
  <Card title="Documents & search" href="/docs/guides/concepts/documents" />
  <Card title="API reference" href="/docs/api" />
</Cards>
