REST API¶
lasagna serve runs an HTTP server with 41 endpoints: 32 under /api/v1/ and 9
that are not versioned. Every endpoint takes and returns JSON, except the two
upload routes, which take multipart form data.
Start the server¶
lasagna serve
The server listens on 127.0.0.1:8000. That address is reachable only from your
machine, and it needs no login. Useful options:
| Option | Meaning |
|---|---|
--port |
Port to listen on. |
--host |
Address to bind. A non-loopback address needs auth on. |
--configurator / --no-configurator |
Serve the settings UI at /configurator. |
--open |
Open the browser. |
--mcp |
Also serve the MCP server at /mcp/sse. |
--session, -s |
The session for requests that name none. |
A running server serves interactive docs at /docs and the schema at
/openapi.json. When this page and that schema disagree, the schema is right.
The /docs page loads its scripts from cdn.jsdelivr.net, so it needs a network
connection.
Upload and search¶
lasagna upload report.pdf -s my-corpus
curl -X POST http://127.0.0.1:8000/api/v1/search \
-H "X-Session-ID: my-corpus" \
-H "Content-Type: application/json" \
-d '{"query": "What does the report conclude?", "top_k": 5}'
Uploads are indexed in the background by default. The upload response comes back
with status set to processing. Poll GET /api/v1/documents/{document_id}/status
until it reads ready.
A document id works as an idempotency key. Uploading again with an id the session
already owns replaces that document. Without an id, a file whose bytes the session
already holds is not uploaded again, and the response names the original in
duplicate_of.
The search body has these fields:
| Field | Default | Meaning |
|---|---|---|
query |
(required) | The question, 1 to 10,000 characters. |
top_k |
5 |
Results to return, 1 to 100. |
min_score |
none | Drop results scoring below this. |
document_ids |
none | Search only these documents. |
use_hyde |
true |
Use HyDE query expansion. |
use_reranking |
true |
Rerank with a cross-encoder. |
routing_tier |
none | Force fast, standard or comprehensive. |
A stage that failed and fell back is named in degraded_stages, so an empty list
means every configured stage ran.
Endpoints at a glance¶
| Group | Routes |
|---|---|
| Health | GET /api/v1/health/live, /health/ready, /health, and GET /api/v1/stats |
| Sessions | POST /api/v1/sessions |
| Documents | POST /api/v1/documents, POST /documents/batch, GET /documents, and GET, DELETE, /chunks, /status, /reindex, /archive, /restore under /documents/{document_id} |
| Admin | Garbage collection, orphan repair, the lifecycle policy and session deletion under /api/v1/admin/ |
| Search | POST /api/v1/search, /search/citations, /search/multi-query, /search/agentic |
| Citations | GET /api/v1/citations and /citations/{citation_id} |
| Config | GET and PATCH /api/v1/config, GET /config/profiles, PUT /config/profiles/{profile_name} |
| Pipelines | GET /api/v1/pipelines |
| Configurator | Eight routes under /api/config/knowledge |
| Reindex | POST /api/knowledge/reindex |
The endpoint reference documents each route with its request, response and status codes.
Sessions and identity¶
A session scopes documents and configuration. The server reads the session from the first of these that is present:
X-Session-Tokenheader (a signed token).X-Session-IDheader.session_idquery parameter.- The name
default.
A session id is at most 128 characters. A token that is present but invalid or expired is refused with 401. It does not fall through to the next source.
Authentication¶
Out of the box, auth is off, and the server binds only to loopback. Turn auth on before you expose the server to anyone else:
export RAG_REQUIRE_AUTH=true
export RAG_JWT_SECRET="$(openssl rand -hex 32)"
export RAG_API_KEYS="$(openssl rand -hex 32)"
lasagna serve --host 0.0.0.0
With RAG_REQUIRE_AUTH=true:
- Only a verified
X-Session-Tokennames a session. A bareX-Session-IDis refused with 401, and so is thedefaultfallback, unlessRAG_ALLOW_ANONYMOUS_SESSION=true. POST /api/v1/sessionsmints tokens and needs anX-API-Keyheader with a key fromRAG_API_KEYS. Keys are at least 32 characters.- Tokens are signed with
RAG_JWT_SECRETand last 7 days by default (RAG_SESSION_TOKEN_TTL_SECONDS). An unset or placeholder secret is a startup error. Without auth, an unset secret gets a random one per process, so tokens stop working on restart. - The health probes stay open.
Instance-wide admin routes need an X-Admin-Token header that matches
RAG_ADMIN_TOKEN. If that variable is unset, they answer 503.
With auth off, the server refuses to start on a non-loopback address unless
RAG_BIND_IS_PROTECTED=true says that something outside the process restricts
access. A request with a non-loopback Host header gets 421 unless the host is in
RAG_ALLOWED_HOSTS. Set RAG_CORS_ORIGINS to allow browser clients.
These security variables are read under their RAG_ names only.
Limits¶
| Limit | Default | Setting |
|---|---|---|
| One uploaded file | 50 MiB | RAG_MAX_FILE_SIZE |
| One batch upload | 200 MiB and 20 files | RAG_MAX_BATCH_BYTES, RAG_MAX_BATCH_FILES |
| Other request bodies | 1 MiB | fixed |
| One search | 120 seconds | RAG_SEARCH_TIMEOUT |
| Concurrent uploads | 5 | RAG_MAX_CONCURRENT_UPLOADS |
| Sessions per API key | 100 | RAG_MAX_SESSIONS_PER_KEY |
| Request rate | 60 per minute, burst 10, adaptive | RAG_RATE_LIMITING_ENABLED |
Rate limiting is off in the code and on in the Docker and Kubernetes configurations. Each session also has quotas: 1,000 documents, 5 GiB, 100,000 chunks, 600 searches per minute and 200 uploads per hour.
Status codes¶
| Code | Meaning |
|---|---|
| 400 | Invalid input: a bad file type, a session id over 128 characters, a malformed query. |
| 401 | Missing, invalid or expired credentials. |
| 403 | You named an identity without a valid admin token. |
| 404 | The document, citation or profile does not exist in this session. |
| 409 | A conflict: a restore after the grace window, or an embedding size that does not fit the collection. |
| 410 | The source file is gone, so the document cannot be reindexed. |
| 413 | The file, batch or body is over its limit. |
| 422 | The body failed validation, or a config key is unknown. |
| 429 | Rate limited or over quota. Rate limits include Retry-After. |
| 500 | An unhandled backend error. |
| 503 | The backend is starting, a dependency is down, the processing pool is full, or the embedding model is not downloaded. |
| 504 | The search ran past RAG_SEARCH_TIMEOUT. |
A server never downloads a model inside a request. If the session's embedding model is not on disk, search answers 503 with the command that fixes it.