# smartbotic-vectorapi — API Reference Version 0.1.0. This is the human-readable reference. Machine-readable equivalents are served live by the running service and shipped in the package: - **`GET /openapi.json`** — OpenAPI 3.1 spec (source: `api/openapi.json`). - **`GET /llms.txt`** — a compact, agent-friendly summary following the [llmstxt.org](https://llmstxt.org) convention (source: `api/llms.txt`). - **`GET /docs`** — this reference rendered as an interactive page (Redoc). ## Base & authentication - Base path: **`/api/v1`**. All `/api/v1/*` routes require authentication. - **API clients** send an API key as a bearer token: `Authorization: Bearer `. - **The web UI** logs in with a key (`POST /ui/login`) and rides a cookie session thereafter (`credentials: include`). - Public (no auth): `GET /healthz`, `GET /readyz`, `GET /openapi.json`, `GET /llms.txt`, `GET /docs`, and the static UI. ### Authorization model (MySQL-like grants) Each API key carries grants: a set of project names it may access (or `"*"` for all) plus an `admin` flag. | Route class | Requirement | On failure | |---|---|---| | `/api/v1/projects/{project}/…` (collections, documents, vectors, search, project stats) | key is `admin`, or holds `"*"`, or lists `{project}` | **403** | | `POST/DELETE /api/v1/projects`, all `/api/v1/keys`, `GET /api/v1/stats` | `admin` | **403** | | `GET /api/v1/projects` | any valid key (result filtered to granted projects) | — | | any `/api/v1/*` with a missing/unknown key | — | **401** | On first run with no keys, the service generates one **admin** key (`projects:["*"], admin:true`) and logs it once. ### Errors All errors return JSON `{"error": {"code": "", "message": ""}}` with a standard HTTP status: `400` malformed request, `401` unauthenticated, `403` forbidden (valid key lacking the grant), `404` not found, `422` validation (bad body, dimension mismatch, reserved name), `503` database/embedding unavailable, `500` unexpected. --- ## Projects A project is an isolated namespace (collections in one project are invisible to others). `default` always exists and cannot be deleted. | Method | Path | Auth | Body / notes | |---|---|---|---| | `GET` | `/api/v1/projects` | any key | → `{"projects": ["default", ...]}` (only granted projects) | | `POST` | `/api/v1/projects` | admin | `{"name":"acme"}` — name `^[a-zA-Z_][a-zA-Z0-9_-]{0,62}$` → `201 {"name":"acme"}` | | `GET` | `/api/v1/projects/{project}` | project access | → `{"name","collections"}` | | `DELETE` | `/api/v1/projects/{project}` | admin | drops the project + its collections (not `default`) | ```bash curl -s -X POST $BASE/api/v1/projects -H "Authorization: Bearer $KEY" \ -H 'Content-Type: application/json' -d '{"name":"acme"}' ``` ## API keys (admin only) Keys are referenced by a stable, non-secret **`id`** (from the list); the secret value is only shown once, at creation. | Method | Path | Body / notes | |---|---|---| | `GET` | `/api/v1/keys` | → `{"keys":[{id,label,projects,admin,created_at,key_prefix}]}` (secret masked) | | `POST` | `/api/v1/keys` | `{"label":"n8n","projects":["acme"],"admin":false}` → `201` with the full `key` **once** | | `PATCH` | `/api/v1/keys/{id}` | `{label?,projects?,admin?}` — update grants | | `DELETE` | `/api/v1/keys/{id}` | revoke (rejected with `422` if it's the last admin) | ```bash # create a scoped key for n8n, limited to project "acme" curl -s -X POST $BASE/api/v1/keys -H "Authorization: Bearer $ADMIN" \ -H 'Content-Type: application/json' \ -d '{"label":"n8n-acme","projects":["acme"],"admin":false}' # → {"id":"...","label":"n8n-acme","projects":["acme"],"admin":false,"key":""} ``` ## Collections Collections are **admin-managed**: create one before writing. `kind` is `json` (documents) or `vector` (RAG; fixed `vector_dimension`). Names may not start with `_` or the reserved `vectorapi_` prefix. | Method | Path | Body / notes | |---|---|---| | `POST` | `/api/v1/projects/{p}/collections` | `{"name","kind":"json"\|"vector","vector_dimension"?,"embedding_model"?}` | | `GET` | `/api/v1/projects/{p}/collections` | → `{"collections":[{name,kind,vector_dimension,embedding_model,document_count,size_bytes}]}` | | `GET` | `/api/v1/projects/{p}/collections/{name}` | one collection's info | | `DELETE` | `/api/v1/projects/{p}/collections/{name}` | drop it | `embedding_model` (vector collections) is the OpenAI model used when storing/ searching by text; its output dimension must equal `vector_dimension` (`text-embedding-3-small` = 1536, `text-embedding-3-large` = 3072). ```bash curl -s -X POST $BASE/api/v1/projects/acme/collections -H "Authorization: Bearer $KEY" \ -H 'Content-Type: application/json' \ -d '{"name":"memories","kind":"vector","vector_dimension":1536,"embedding_model":"text-embedding-3-small"}' ``` ## JSON documents Under `/api/v1/projects/{p}/collections/{name}/documents`. | Method | Path | Body / notes | |---|---|---| | `POST` | `.../documents` | `{"id"?,"data":{...}}` (or the raw doc) → `201 {"id"}` | | `GET` | `.../documents/{id}` | the document | | `GET` | `.../documents` | find — query params below → `{"documents":[...],"count"}` | | `PATCH` | `.../documents/{id}` | merge fields → `{"id","version"}` | | `PUT` | `.../documents/{id}` | replace the whole document | | `DELETE` | `.../documents/{id}` | delete | **Find query params:** `filter` (repeatable, `field:op:value`, AND-combined), `limit`, `offset`, `sort`, `desc` (`true`/`false`). Operators (`op`): `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `contains`, `exists`, `regex`, `search`. The value is parsed as JSON when possible, else as a string. ```bash curl -s -X POST $BASE/api/v1/projects/acme/collections/users/documents -H "Authorization: Bearer $KEY" \ -H 'Content-Type: application/json' -d '{"data":{"name":"Alice","age":30}}' curl -s "$BASE/api/v1/projects/acme/collections/users/documents?filter=age:gte:18&filter=name:search:Ali&limit=20&sort=age&desc=true" \ -H "Authorization: Bearer $KEY" ``` ## RAG: vectors & similarity search Under `/api/v1/projects/{p}/collections/{name}` (vector collections only). | Method | Path | Body | |---|---|---| | `POST` | `.../vectors` | `{"id"?,"text"?,"vector"?,"metadata"?}` — provide `vector` to store as-is, or `text` to embed (OpenAI) → `201 {"id"}` | | `POST` | `.../search` | `{"query_text"?\|"query_vector"?,"top_k":5,"min_score":0.0}` → `{"results":[{id,score,data}]}` ordered by descending cosine similarity | A provided/generated vector's length must equal the collection's `vector_dimension` (else `422`). Storing by `text` or searching by `query_text` requires `OPENAI_API_KEY` to be configured (else `422`/`503`). ```bash curl -s -X POST $BASE/api/v1/projects/acme/collections/memories/vectors -H "Authorization: Bearer $KEY" \ -H 'Content-Type: application/json' -d '{"text":"user prefers dark mode","metadata":{"src":"n8n"}}' curl -s -X POST $BASE/api/v1/projects/acme/collections/memories/search -H "Authorization: Bearer $KEY" \ -H 'Content-Type: application/json' -d '{"query_text":"dark mode","top_k":5,"min_score":0.7}' ``` ## Stats & ops | Method | Path | Auth | Returns | |---|---|---|---| | `GET` | `/api/v1/projects/{p}/stats` | project access | `{project, collections, documents}` | | `GET` | `/api/v1/stats` | admin | server-wide: documents, collections, memory, pressure level, project count | | `GET` | `/healthz` | public | `{"status":"ok"}` (liveness) | | `GET` | `/readyz` | public | `{"ready":bool}` — `200` if the database is reachable, else `503` | ## Web UI session | Method | Path | Body / notes | |---|---|---| | `POST` | `/ui/login` | `{"key":""}` → sets an HttpOnly `svapi_session` cookie; body `{ok,admin,projects}` | | `POST` | `/ui/logout` | clears the session cookie | --- ## n8n usage notes - Use an **HTTP Request** node with **Header Auth** / a generic `Authorization: Bearer ` header. - Give each workflow (or tenant) its own **project** and a **scoped key** granted only that project — so a leaked key can't reach other tenants' data. - For RAG: create a `vector` collection with the embedding model matching your dimension, `POST .../vectors` with `text` to ingest, and `POST .../search` with `query_text` to retrieve context for the LLM node. - For everything else: a `json` collection with `POST .../documents` (store) and `GET .../documents?filter=...` (query).