# Context pack: Introduction

Source: https://nordvec.com/docs/guides
Pack: https://nordvec.com/docs/packs

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. [Introduction](https://nordvec.com/docs/guides) (this guide)
2. [Getting started](https://nordvec.com/docs/guides/getting-started) (linked from this guide)
3. [Authentication](https://nordvec.com/docs/guides/authentication) (linked from this guide)
4. [Errors and rate limits](https://nordvec.com/docs/guides/errors-and-rate-limits) (linked from this guide)
5. [Documents & search](https://nordvec.com/docs/guides/concepts/documents) (linked from this guide)
6. [Conversations & citations](https://nordvec.com/docs/guides/concepts/conversations) (linked from this guide)

---

# Introduction
Source: https://nordvec.com/docs/guides

Nordvec is an EU document-search and RAG platform. Search your documents over the API, and chat with grounded, cited answers in the app.

Nordvec turns your documents into a searchable knowledge base and a chat
assistant that answers from them, with citations you can verify. Your documents
are stored and searched in the EU.

In a hurry? Ask these docs directly, answered by our own EU AI, grounded in the
guides and cited so you can verify.

<DocsAiAsk />

Two things build on your documents:

<Cards>
  <Card title="Search documents" href="/docs/guides/concepts/documents">
    Full-text search across everything you have ingested, ranked and sourced,
    from the app or with an API key.
  </Card>
  <Card title="Chat with citations" href="/docs/guides/concepts/conversations">
    Answers grounded in your documents, with every claim traced to its source,
    for signed-in people.
  </Card>
</Cards>

## Start here [#start-here]

<Cards>
  <Card title="Getting started" href="/docs/guides/getting-started">
    Create an API key and run your first search in a few minutes.
  </Card>
  <Card title="Authentication" href="/docs/guides/authentication">
    Key classes, scopes, rotation and revocation.
  </Card>
  <Card title="Errors and rate limits" href="/docs/guides/errors-and-rate-limits">
    One error envelope, the rate-limit headers, and safe retries.
  </Card>
  <Card title="API reference" href="/docs/api">
    Every endpoint, generated from our OpenAPI document.
  </Card>
</Cards>

## Who it is for [#who-it-is-for]

Nordvec serves both an individual solving their own document sprawl and a
business rolling it out across teams, over the same API. Start solo, grow into an
organization without switching platforms.


---

# Getting started
Source: https://nordvec.com/docs/guides/getting-started

Create an API key and run your first document search against Nordvec.

This guide takes you from zero to your first search result.

## 1. Create an API key [#1-create-an-api-key]

Sign in and open **Settings > API Keys** in your workspace. For searching, create
a **client** key with the `search:read` scope; it starts with `nv_eu_live_`. The
raw key is shown **once** and never again, so store it somewhere safe. A key
belongs to the workspace, not to the person who created it, and authenticates
as a bearer token.

See [Authentication](/docs/guides/authentication) for key classes, scopes and
the full lifecycle (rotate, revoke, grace windows).

## 2. Search your documents [#2-search-your-documents]

Point any HTTP client at the API base, `https://nordvec.com/api/v1`, and
send your query. Results come back ranked, each carrying the source document it
came from. `query` is required; `limit` (1-50, default 20) is optional.

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

<Callout>
Every result references its source document, so you can verify a match rather
than just trust it. That is the whole point of the platform.
</Callout>

An API key sees the documents its workspace shares. Documents a person has
connected but not shared stay visible only to that person, so a search with a
key can return fewer results than the same search in the app.

## 3. Add documents [#3-add-documents]

Documents arrive from the [connectors](/docs/guides/connectors) you connect in
the app, and from files you upload there. To index content from your own
system instead, see [Push documents](/docs/guides/how-to/push-documents).

## 4. Chat with citations [#4-chat-with-citations]

A [conversation](/docs/guides/concepts/conversations) is a chat grounded in your
documents, where every answer is cited. Conversations belong to a signed-in
person, so they run in the Nordvec app and through its session-authenticated
endpoints, not with an API key.

## Next steps [#next-steps]

<Cards>
  <Card title="Documents & search" href="/docs/guides/concepts/documents" />
  <Card title="Errors and rate limits" href="/docs/guides/errors-and-rate-limits" />
  <Card title="API reference" href="/docs/api" />
</Cards>


---

# Authentication
Source: https://nordvec.com/docs/guides/authentication

Authenticate API requests with a workspace API key sent as a bearer token, and pick the key class and scopes a job needs.

A program calls the Nordvec API with an **API key**, sent as a bearer token in
the `Authorization` header.

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

Not every endpoint takes a key. Conversations, webhooks and key management
belong to a signed-in person and answer only to a session; the
[API reference](/docs/api) marks each operation with the credentials it accepts.

## Key classes [#key-classes]

A key is one of two classes, and its prefix says which:

| Class | Prefix | For |
| --- | --- | --- |
| Client | `nv_eu_live_` | Reading: search, list and fetch documents, quota, audit trail |
| Indexing | `nv_eu_idx_` | Writing: push, delete and re-permission documents, check ingestion |

## Scopes [#scopes]

A key carries one or more scopes, and an operation answers `403` to a key
without the scope it needs. A key can only carry scopes of its own class.

| Scope | Class | Allows |
| --- | --- | --- |
| `search:read` | Client | Search, list, fetch and batch-fetch documents |
| `quota:read` | Client | Embedding quota and usage analytics |
| `audit:read` | Client | Search the workspace's audit trail and read its event catalogue |
| `index:write` | Indexing | Push and bulk-push documents, update their permissions |
| `index:delete` | Indexing | Delete pushed documents |
| `index:status` | Indexing | Push status and ingestion health |
| `index:acl-widen` | Indexing | Make an already-restricted document visible to the whole workspace. A key with this scope must have an expiry |

Give each key the fewest scopes its job needs, so a leaked key can do as
little as possible.

## Creating a key [#creating-a-key]

Create a key under **Settings > API Keys**. Creating one needs the workspace
**owner** or **admin** role; on a personal workspace that is you. The **raw key
is returned exactly once** and is never retrievable afterwards, so copy it into
your secret store straight away.

A key belongs to the workspace, not to the person who created it. It reads
what the workspace shares: documents a person has connected but not shared stay
visible only to that person, and no key can see them. A key does stop working
once the person who created it, or last rotated it, leaves the workspace, so a
departing admin's keys do not outlive their access; an owner or admin who
rotates the key takes it over, and it works again.

A key's lifetime is 1 to 365 days, and a key created without one works for 365
days. Rotating a key starts its lifetime again from the day of the rotation.
A key can also carry an IP allowlist of up to 50 addresses or CIDR ranges.

A key no request authenticates with for 45 days, counted in UTC calendar days,
is disabled. After 38 days of disuse, the workspace's owners and admins get a
notice naming the key and the date it will be disabled; one request the key
authenticates before that date keeps it, and so does rotating it. A key that
expires before that date stops working at its expiry instead, and gets no notice
unless it has already gone 45 days unused. With no owner or
admin left to notify, the key is disabled at 45 days without a notice. A
request the key's IP allowlist refuses, or one refused because the key's creator
has left, is not authenticated and does not count. A disabled key never works
again, so create a new key for any application that still needs one.

## Rotating and revoking [#rotating-and-revoking]

<Callout type="warn">
Treat an API key like a password. If one leaks, rotate or revoke it immediately.
</Callout>

- **Rotate** replaces a key's credential in place. The previous credential keeps
  working for a grace window you choose: none, 15 minutes, 1 hour, 6 hours or
  24 hours (the default), so you can roll the new credential out to your
  services without downtime.
- **Revoke** disables a key immediately; it can no longer authenticate.

Both actions require a signed-in session (they are not themselves API-key
operations), so a leaked key cannot be used to rotate itself.

## Good practice [#good-practice]

- Store keys in a secret manager or environment variable, never in source code.
- Use a separate key per service or environment so you can revoke narrowly.
- Set an expiry on keys for CI and scripts.


---

# Errors and rate limits
Source: https://nordvec.com/docs/guides/errors-and-rate-limits

The one error envelope every failed request returns, the rate-limit headers, and how to retry a write safely.

Every endpoint fails the same way, so a client handles errors, rate limits and
retries once and reuses that code everywhere, including over
[MCP](/docs/guides/mcp).

## The error envelope [#the-error-envelope]

Every non-2xx response is one JSON object:

```json
{
  "defined": false,
  "code": "TOO_MANY_REQUESTS",
  "message": "Too many requests",
  "data": { "reason": "rate_limit.exceeded", "retryAfterMs": 12000 }
}
```

- `code` is the HTTP-level error, for example `UNAUTHORIZED`, `FORBIDDEN`,
  `NOT_FOUND`, `BAD_REQUEST` or `TOO_MANY_REQUESTS`.
- `data.reason`, when present, is a finer machine-readable reason such as
  `auth.key_not_found` or `rate_limit.exceeded`. Branch on it rather than on
  `message`, which is for people and may change.
- `defined` is `true` when the operation lists that error in the
  [API reference](/docs/api), and `false` for errors any request can meet
  (authentication, rate limits, an unknown route).
- A validation failure answers `BAD_REQUEST` with the problems in
  `data.formErrors` and `data.fieldErrors`.

Every response also carries an `X-Request-ID`. Quote it when you contact
support, and we can find that exact request.

## Common statuses [#common-statuses]

| Status | Code | What to do |
| --- | --- | --- |
| `400` | `BAD_REQUEST` | Fix the request; `data.fieldErrors` names the fields |
| `401` | `UNAUTHORIZED` | Send a valid key or session |
| `403` | `FORBIDDEN` | The key lacks the scope or the role the operation needs |
| `404` | `NOT_FOUND` | The resource does not exist, or you are not allowed to see it |
| `409` | `CONFLICT` | A duplicate write is still in flight; retry shortly |
| `413` | `PAYLOAD_TOO_LARGE` | The request body is over 1 MB; split a bulk push into smaller batches |
| `422` | `UNPROCESSABLE_CONTENT` | The request is well formed but cannot be applied |
| `429` | `TOO_MANY_REQUESTS` | Wait for `Retry-After`, then retry |

## Rate limits [#rate-limits]

Every response states the limit it was counted against, in two forms:

- the `X-RateLimit-*` headers;
- the IETF structured fields `RateLimit` (live state: `r` is the requests
  remaining, `t` the seconds until the window resets) and `RateLimit-Policy`
  (the quota: `q` is the limit, `w` the window in seconds).

A `429` also carries `Retry-After` in seconds and `data.retryAfterMs`. Wait at
least that long before the next request; retrying sooner is counted and
refused again.

## Retrying writes safely [#retrying-writes-safely]

A write operation that lists an `Idempotency-Key` header in the
[API reference](/docs/api) can be retried without doing the work twice. Send
one key per logical write and repeat the same key on every retry:

- the same key with the same body within 24 hours replays the stored response;
- the same key with a different body is refused with `422`;
- a duplicate that arrives while the first is still running gets `409`.

An operation without the header is not idempotent, so retry it only when you
know the first attempt did not land.


---

# 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.


---

# Conversations & citations
Source: https://nordvec.com/docs/guides/concepts/conversations

A conversation is a chat grounded in your documents, where every answer is traced to its source.

A **conversation** is a chat grounded in your documents. You ask a question,
Nordvec retrieves the relevant passages, and it answers from them, citing the
documents it used. If the documents do not support an answer, it says so rather
than guessing.

## Citations are the point [#citations-are-the-point]

Every answer carries the sources it was built from. This is deliberate: a RAG
answer you cannot trace is a liability, not a feature. The citations let a human
jump to the exact document a claim came from and confirm it, which is exactly
what regulated and high-stakes work needs.

<Callout>
Answers are generated by AI. Citations are what make them verifiable, not just
plausible.
</Callout>

## Conversations belong to a person [#conversations-belong-to-a-person]

A conversation is owned by the signed-in person who started it, so its
endpoints answer to a **session**, never to an API key. A key is its own
principal with no chat history of its own; see
[Authentication](/docs/guides/authentication).

With a session you can:

- **Create** a conversation (`POST /conversations`).
- **List** and **search** your conversations (`GET /conversations`,
  `GET /conversations/search`).
- **Retrieve** a conversation with its recent messages
  (`GET /conversations/{id}`), and page back through older history
  (`GET /conversations/{id}/messages`).
- **List the documents** a conversation has drawn on
  (`GET /conversations/{id}/documents`).

See the [API reference](/docs/api) for the exact request and response shapes.

## Feedback [#feedback]

You can submit feedback on an answer and on the passages that were retrieved.
That signal is used to improve retrieval quality over time.
