# Lornos: answers that show their sources URL: https://lornos.dev/ Upload your documents. Your product gets grounded answers, each one cited. Lornos is in private beta. The retrievals, replies and timings on this page come from a demo help center of 42 articles (280 passages). ## How one question travels 1. **Upload** a help article: `record-a-sale.md` 2. **Index** it into passages: `record-a-sale.md#1.0` is one of 280. 3. **Ask** from your product: "How do I record a sale?" 4. **Check** the answer: it cites `[record-a-sale.md#1.0]`, retrieved in 3 ms. ## Read the passage behind every citation A citation points to a passage in your own documents, with how well it matched the question. The three passages retrieved for "How do I record a sale?": - `record-a-sale.md#1.0` (match score 15.89): 1. Open Sales and click Record sale. The Record a sale shortcut on the Dashboard opens the same form. 2. For each line, search and select an item, enter the quantity, and… - `record-a-sale.md#4.0` (match score 12.70): Stock is deducted, each line is costed at the item's current average, the sale is numbered INV-YYMMDD-0001 style, and the Dashboard and Reports update at once. Full… - `what-happens-when-you-record-a-sale.md#0.0` (match score 12.56): Everything below happens in one transaction: either the whole sale is recorded or nothing is, so a shortage can never leave a half-recorded sale behind. The answer cites `[record-a-sale.md#1.0]`. ## Citations it wasn’t given come out Lornos checks every ID as the answer streams. One it didn’t retrieve is removed, and your user is told. An example answer written to show the check, where `undo-a-sale.md#0.0` was not retrieved for this question: - What the model wrote: Open Sales and click Record sale [record-a-sale.md#1.0]. You can also undo a sale from Reports [undo-a-sale.md#0.0]. - What your user gets: Open Sales and click Record sale [record-a-sale.md#1.0]. You can also undo a sale from Reports. Citation check: removed 1 unverified source ID; statements that relied on them are not verified. ## Every request shows its timings Each request reports how long finding the passages took and how long the answer took to write, so you can see where your time goes. One request to our internal demo workspace (42 articles, 280 passages): - Finding the passages: 4 ms - First words on screen: 1.6 s - Full answer written: 3.30 s Your timings will differ. Finding passages depends on how much your workspace holds, and writing depends mostly on how long the answer is. Either way the answer streams, so your user starts reading before it is finished. ## Each workspace keeps its own index A question searches only the workspace that asked it. An empty one says so, and never borrows another’s passages. The same question in two workspaces: - Demo help center (280 passages): retrieves 3 passages and cites `[record-a-sale.md#1.0]`. - A new workspace (0 passages): retrieves 0 passages and answers "This workspace has no searchable sources yet. Add documents on the Sources page, then ask again." with no model call and no citations. Deleting a source removes its passages, so no later answer can cite it. ## Before you ask for access - **What can I upload?** Markdown, plain text, HTML, PDF and Word, up to 25 MB a file: .md .markdown .mdx .txt .html .htm .pdf .docx - **How does my code ask a question?** `POST /api/chat` with an API key that has the `chat` scope. The answer streams back with its citations. - **Are follow-up questions grounded too?** Yes. Each question gets fresh retrieval. Earlier answers only tell Lornos what “it” refers to, never what to cite. - **How many questions can I ask?** 30 a minute and 500 a day per workspace, counting the playground and every key. Past that you get a `429` with a `Retry-After` header. - **When can I start?** Lornos is in private beta. We approve early-access requests into new workspaces and write to you once. ## Crawling a website comes next Point Lornos at a URL or a sitemap and it keeps those pages indexed alongside your uploads. In progress, with no date yet. Available today: - Uploads in Markdown, plain text, HTML, PDF and Word - The chat and sources API - A citation check on every answer ## Bring your documents when we open. We approve early-access requests into new workspaces. Your first answer is one request away. Request access with your work email on the homepage. --- # Getting started URL: https://lornos.dev/docs/getting-started From an early-access request to a cited answer in your own app. Lornos is in private beta. This is the whole path, from access to the first cited answer in your own code. ## 1. Get a workspace Request early access with your work email on the [homepage](https://lornos.dev/index.md). When we approve the request we create a workspace for you and write to you once with your sign-in details. Sign in, then set your own password under **Settings**. ## 2. Upload your documents Open **Sources** and drop up to 10 files at a time: Markdown, plain text, HTML, PDF or Word, up to 25 MB each. See [Sources](https://lornos.dev/docs/sources.md). ## 3. Wait for them to index Each file is read, split into passages and indexed for your workspace only. **Jobs** shows each one's progress, and a file is ready when it says **Searchable**. See [Jobs](https://lornos.dev/docs/jobs.md). ## 4. Ask in the playground Ask a question your documents can answer. The playground shows the answer, the passages it used and how long each step took. See [Playground](https://lornos.dev/docs/playground.md). ## 5. Create an API key An owner or admin creates a key under **API keys** and picks its scopes: `chat` to ask questions, `sources` to manage documents. The key is shown once. See [API keys](https://lornos.dev/docs/api-keys.md). ## 6. Ask from your code Set `LORNOS_URL` to the address of your console without the path, and `LORNOS_KEY` to your key: ```bash curl -N "$LORNOS_URL/api/chat" \ -H "Authorization: Bearer $LORNOS_KEY" \ -H "Content-Type: application/json" \ -d '{"messages":[{"id":"q1","role":"user","parts":[{"type":"text","text":"How do I record a sale?"}]}]}' ``` The answer streams back and cites passages by ID, for example `[record-a-sale.md#1.0]`. The [API reference](https://lornos.dev/docs/api.md) covers the stream format, uploads and errors. --- # Sources URL: https://lornos.dev/docs/sources Upload documents, follow their indexing, index them again or delete them. A source is one document your workspace answers from. Answers can cite only the passages of your own sources. ## Upload Open **Sources** and drop files onto the upload area, or choose them. Each batch takes up to 10 files; extra files are left out and you're told. | Format | Extensions | |---|---| | Markdown | `.md`, `.markdown`, `.mdx` | | Plain text | `.txt` | | HTML | `.html`, `.htm` | | PDF | `.pdf` | | Word | `.docx` | A file can be up to 25 MB. Empty files and other extensions are refused before upload, with the reason. From your own code, use the sources endpoints in the [API reference](https://lornos.dev/docs/api.md#upload). Each API source holds one file, and its upload URL is valid for 10 minutes. ## What indexing does After upload, Lornos reads the file, converts PDF, Word and HTML to text, splits it into passages and indexes them for this workspace only. Each passage gets an ID that answers cite. Follow the progress in [Jobs](https://lornos.dev/docs/jobs.md). If a format can't be read right now, the file is still stored and shows **Format not indexed yet** in Jobs, ready to index once the format can be read. ## Index again To index a source again after a failure, or to rebuild its passages, use **Retry** or **Re-index** in Jobs, or `POST /api/v1/sources/{id}/reindex` from the API. ## Delete Use **Delete**, then **Confirm delete**, next to the upload. Deleting a source removes the file and its passages, so no later answer can cite it. Any member of the workspace can upload, index and delete sources. --- # Jobs URL: https://lornos.dev/docs/jobs What each indexing state means and what to do when a run fails or stalls. **Jobs** lists every upload with its state, its passage count and when it last changed. While something is indexing, the page refreshes on its own. ## States | State | Meaning | |---|---| | Uploading | The file hasn't finished uploading yet. | | Queued | The file is stored and waiting to be indexed. | | Indexing… | Lornos is reading the file and building its passages. | | Searchable | Done. Answers can cite its passages. | | Failed | Indexing stopped. The reason is shown under the file name. | | Stalled | Indexing showed no progress for 5 minutes. | | Format not indexed yet | The file is stored, but its format can't be read right now. | ## Actions The button at the end of the row depends on the state: - **Index now** for a file that is stored but not indexed yet. - **Retry** for a failed or stalled run. - **Re-index** for a searchable file, to build its passages again. A re-index replaces the source's passages with the new ones. A file whose format can't be read right now has no button; it can be indexed once the format can be read. If a run keeps failing, the reason under the file name says what went wrong in plain terms. Uploading the file again as a new source is always safe. --- # Playground URL: https://lornos.dev/docs/playground Ask your workspace a question and see the passages and timings behind the answer. The playground asks your workspace questions the same way your app does through the API, so you can check answers before you integrate. ## Ask Type a question your documents can answer. Follow-up questions work: Lornos uses the earlier turns to understand what "it" or "that step" means, and retrieves passages again for every question. ## Read the answer Beside the answer, **How this answer was found** shows each step of the request. Open **Detailed stage timings** to see how long each step took and the IDs and scores of the passages retrieved. The answer's citations can only be IDs from that list. See [Citations](https://lornos.dev/docs/citations.md). ## When there is nothing to answer from A workspace with no searchable sources gets this reply, with no citations: > This workspace has no searchable sources yet. Add documents on the Sources page, then ask again. When nothing in your sources matches the question, the reply is: > I couldn't find anything about this in this workspace's sources. Try rephrasing the question, or add a source that covers it. Questions from the playground count toward your workspace's [limits](https://lornos.dev/docs/limits.md), together with every API key. --- # API keys URL: https://lornos.dev/docs/api-keys Create keys for your own app, choose their scopes and revoke them. API keys let your own app call Lornos. Each key belongs to one workspace, and every request it makes reads and writes that workspace only. ## Create a key Owners and admins open **API keys** and choose: - **Name**, so you can tell keys apart later, for example "Production website". - **Expires**: in 30 days, 90 days, a year, or never. - **Scopes**: what the key may do. | Scope | Allows | |---|---| | `chat` | Asking questions with `POST /api/chat`, and the `ask` and `search` tools of the [MCP server](https://lornos.dev/docs/mcp.md). | | `sources` | Uploading, listing, re-indexing and deleting sources under `/api/v1/sources`, and the `list_sources` and `get_source` MCP tools. | A key can hold both. The full key is shown once, so copy it then; Lornos keeps only a hash. ## Use a key Send it as a Bearer token on every request: ```text Authorization: Bearer lrn_… ``` A missing, revoked or expired key gets `401`. The [API reference](https://lornos.dev/docs/api.md#authentication) lists every response. ## Revoke a key Use **Revoke**, then **Confirm revoke**, in the key's row. It stops working at once. The list shows each key's status (Active, Revoked or Expired), when it was last used and when it expires. --- # Members and roles URL: https://lornos.dev/docs/members Who can open a workspace, and what owners, admins and members can do. Everyone who can open a workspace is listed under **Members**, with their role. ## Roles | | Owner | Admin | Member | |---|---|---|---| | Upload, index and delete sources | Yes | Yes | Yes | | Ask in the playground | Yes | Yes | Yes | | Create and revoke API keys | Yes | Yes | No | | Add and remove members, change roles | Yes | Yes | No | | Change an owner's role, make someone an owner | Yes | No | No | | Rename the workspace | Yes | Yes | No | Nobody changes their own role from the Members page. ## Add a member An owner or admin enters the person's email, a name and a role. If they don't have a Lornos account yet, one is created and you get a one-time password to share with them. They can change it under **Settings** after signing in. ## Deleting a workspace Deleting a whole workspace isn't available during the private beta. --- # Citations URL: https://lornos.dev/docs/citations How answers cite passages, how citations are checked and what happens to one that fails. Every answer cites the passages it used, by chunk ID in square brackets: ```text Open Sales and click Record sale [record-a-sale.md#1.0]. ``` The part before `#` names the document. For a document you uploaded, it begins with the first eight characters of its source ID. ## Only what was retrieved An answer may cite only the passages retrieved for that question, from your workspace. Lornos checks each ID as the answer streams. An ID that wasn't retrieved for this request is removed, and the answer ends with a note: > Citation check: removed 1 unverified source ID; statements that relied on them are not verified. The number counts the IDs that were removed. The statements stay, so your users can see what was said and that it isn't backed by a source. ## At most five An answer cites at most five distinct passages. ## Follow-up questions Each question gets its own retrieval. Earlier answers only tell Lornos what the new question refers to; they are never used as evidence, and their citations don't carry over. ## No match, no citations When the workspace has no searchable sources, or nothing in them matches, the answer is a short fixed reply with no citations. The [Playground](https://lornos.dev/docs/playground.md) page quotes both. --- # Limits URL: https://lornos.dev/docs/limits Questions per minute and per day, upload sizes and what happens over a limit. ## Questions By default each workspace can ask 30 questions a minute and 500 a day. The playground and every API key count together. Over a limit, `POST /api/chat` answers `429` with a `Retry-After` header, in seconds, and this body: ```json { "error": { "code": "rate_limited", "message": "…" } } ``` Wait the number of seconds in `Retry-After`, then ask again. ## Uploads | Limit | Value | |---|---| | File size | 25 MB | | Files per upload in the console | 10 | | Files per API source | 1 | | Upload URL from the API | valid for 10 minutes | ## Answers An answer cites at most five distinct passages. The API's source list returns the 200 most recent sources. --- # MCP server URL: https://lornos.dev/docs/mcp Connect Claude, Cursor, VS Code and other MCP clients to your workspace and ask it questions. Lornos runs a [Model Context Protocol](https://modelcontextprotocol.io) server, so an AI assistant or coding agent can ask your workspace questions and read the passages behind the answers. It uses the same API keys as the [API](https://lornos.dev/docs/api.md), and it reads only the workspace the key belongs to. ## Server address ```text https://lornos.dev/mcp ``` The server speaks the Streamable HTTP transport and answers each request with JSON. It keeps no session between requests. ## Authentication Create an [API key](https://lornos.dev/docs/api-keys.md) and send it on every request: ```text Authorization: Bearer lrn_… ``` The key decides everything the server can reach: - **The workspace.** Every tool reads the workspace the key was created in. No tool takes a workspace name or ID, so a client can't point it at another workspace. - **The tools.** A key with the `chat` scope sees `ask` and `search`. A key with the `sources` scope sees `list_sources` and `get_source`. A key with both sees all four. - **Revoking.** A revoked or expired key gets `401` on its next request. Give an assistant its own key, with only the scopes it needs, so you can revoke it without touching your app. ## Connect a client Replace `lrn_…` with your key in each example. ### Claude Code ```bash claude mcp add --transport http lornos https://lornos.dev/mcp --header "Authorization: Bearer lrn_…" ``` ### Cursor In `.cursor/mcp.json`: ```json { "mcpServers": { "lornos": { "url": "https://lornos.dev/mcp", "headers": { "Authorization": "Bearer lrn_…" } } } } ``` ### VS Code In `.vscode/mcp.json`: ```json { "servers": { "lornos": { "type": "http", "url": "https://lornos.dev/mcp", "headers": { "Authorization": "Bearer lrn_…" } } } } ``` ### Other clients Any client that supports remote servers over HTTP with a custom header works. Clients that only connect through an OAuth sign-in can't connect yet. ## Tools | Tool | Scope | What it does | |---|---|---| | `ask` | `chat` | Answers a question from your documents and cites passages by chunk ID, for example `[refunds.md#2]`. | | `search` | `chat` | Returns the passages that best match a query, each with its chunk ID and file, so the assistant can read the evidence itself. Up to 10 passages; 5 by default. | | `list_sources` | `sources` | Lists your documents with their indexing status and passage counts. | | `get_source` | `sources` | Shows one document by its source ID, including why indexing failed if it did. | `ask` follows the same rules as every answer: it cites only passages retrieved for that question, and when nothing in your documents covers it, it says so instead of guessing. See [Citations](https://lornos.dev/docs/citations.md). ## What the MCP server doesn't do Every tool only reads. These stay in the console and the [API](https://lornos.dev/docs/api.md): - uploading, re-indexing and deleting sources; - creating or revoking API keys; - adding or removing members and changing roles; - workspace settings, usage and limits. Nothing outside your workspace is reachable through MCP. ## Limits and usage Each `ask` and each `search` counts as one question toward your workspace's limits, the same limits the playground and the API share. Over a limit, the tool returns a message saying when to try again. See [Limits](https://lornos.dev/docs/limits.md). Both tools show on the **Usage** page in the console, marked **MCP** with the key's name: `ask` as a question, `search` as a search. `list_sources` and `get_source` aren't counted. ## Check the connection To check a key without a client, list its tools: ```bash curl "https://lornos.dev/mcp" \ -H "Authorization: Bearer lrn_…" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` A valid key returns the tools its scopes allow. A missing, revoked or expired key returns `401`. --- # What's next URL: https://lornos.dev/docs/whats-next What works today, and what we are building next. Lornos is in private beta. This page lists what you can use today and what is in progress. We don't give dates until something ships. ## Available - **Document upload**: Markdown, plain text, HTML, PDF and Word, from the console or the API. - **Cited answers**: streamed answers that cite only passages retrieved for the question, with a citation check. - **Per-workspace indexes**: retrieval never crosses workspaces, and deleting a source removes it everywhere. - **Request details**: the passages retrieved and the time each stage took, in the playground. - **API keys** with `chat` and `sources` scopes, and members with owner, admin and member roles. ## In progress - **URL and sitemap crawling**: point Lornos at a page or a sitemap, and it indexes the pages as sources. Tell us what you need next when you [request access](https://lornos.dev/index.md). --- # API reference URL: https://lornos.dev/docs/api Ask questions of your workspace’s documents and get answers cited to the passages they came from. Add documents and follow their indexing from your own code. ## Overview The base URL is the address of your Lornos console without the path. The examples read it from `LORNOS_URL` and your key from `LORNOS_KEY`. | Endpoint | What it does | Scope | |---|---|---| | `POST /api/chat` | Ask a question; the answer streams back with citations | chat | | `POST /api/v1/sources` | Create a source and get an upload URL | sources | | `POST /api/v1/sources/{id}/complete` | Confirm the upload and start indexing | sources | | `GET /api/v1/sources` | List sources and their status | sources | | `GET /api/v1/sources/{id}` | One source’s status | sources | | `POST /api/v1/sources/{id}/reindex` | Index a source again | sources | | `DELETE /api/v1/sources/{id}` | Delete a source and its passages | sources | Requests and responses are JSON, except the chat answer, which streams as server-sent events. Call the API from your server: an API key is a secret and must never reach a browser or a mobile app. ## Authentication and scopes Send a workspace API key as a Bearer token on every request: ``` Authorization: Bearer lrn_… ``` Owners and admins create keys on the API keys page of the console. The full key is shown once; Lornos keeps only a hash. Each key belongs to one workspace, and every request it makes reads and writes that workspace only. No header, body field or parameter can point a key at another workspace. | Scope | Allows | |---|---| | `chat` | Asking questions with `POST /api/chat`. | | `sources` | Uploading, listing, re-indexing and deleting sources under `/api/v1/sources`. | A key can hold both scopes. A missing, malformed, revoked or expired key gets `401`. A valid key without the `sources` scope gets `403` from the sources endpoints; a key without the `chat` scope gets `401` from the chat endpoint. ## Ask a question `POST /api/chat`. Scope: chat. The answer streams back as it is written. The body is the conversation: `messages`, each with an `id`, a `role` and `parts`, a list of `{ "type": "text", "text": "…" }` objects. The last user message is the question. ```json { "messages": [ { "id": "q1", "role": "user", "parts": [ { "type": "text", "text": "How do I record a sale?" } ] } ] } ``` ```sh curl -N "$LORNOS_URL/api/chat" \ -H "Authorization: Bearer $LORNOS_KEY" \ -H "Content-Type: application/json" \ -d '{"messages":[{"id":"q1","role":"user","parts":[{"type":"text","text":"How do I record a sale?"}]}]}' ``` A `200` response has the content type `text/event-stream`. Each event is a line `data: {json}` followed by a blank line, and the stream ends with `data: [DONE]`. Join the `delta` of every `text-delta` event to get the answer. An `error` event carries `errorText` when the answer couldn’t be finished. Ignore the event types you don’t use. ``` data: {"type":"start"} data: {"type":"start-step"} data: {"type":"text-start","id":"0"} data: {"type":"text-delta","id":"0","delta":"Open Sales and choose New sale "} data: {"type":"text-delta","id":"0","delta":"[record-a-sale.md#1.0]."} data: {"type":"text-end","id":"0"} data: {"type":"finish-step"} data: {"type":"finish","finishReason":"stop"} data: [DONE] ``` ### Citations The answer cites passages by chunk ID in square brackets, for example `[record-a-sale.md#1.0]`. The part before `#` names the document; for a document you uploaded it begins with the first eight characters of its source ID. An answer cites at most five distinct passages. Every cited ID was retrieved for this request, from this workspace. Lornos checks citations as the answer streams: an ID that wasn’t retrieved in this request is removed, and the answer then ends with a note saying how many were removed. When the workspace has no searchable sources, or nothing in them matches the question, you get a short fixed reply with no citations, in the same stream format. ### Follow-up questions Send the whole conversation again with the new question last, including the earlier answers as `assistant` messages. Each question gets fresh retrieval, and earlier answers are never used as evidence: their citations don’t carry over. ```json { "messages": [ { "id": "q1", "role": "user", "parts": [ { "type": "text", "text": "How do I record a sale?" } ] }, { "id": "a1", "role": "assistant", "parts": [ { "type": "text", "text": "Open Sales and choose New sale. [record-a-sale.md#1.0]" } ] }, { "id": "q2", "role": "user", "parts": [ { "type": "text", "text": "Can I undo it afterwards?" } ] } ] } ``` ## Upload a document An upload takes three requests: create the source, send the file to the URL you get back, then complete it. 1. `POST /api/v1/sources` (scope: sources, returns 201) with `filename` (required, up to 255 characters; its extension sets the file type), `size` (required, bytes, up to 25 MB) and optionally `contentType` (must be the type listed for the extension under Limits). ```json { "id": "6f1c2d3e-8a4b-4c5d-9e6f-7a8b9c0d1e2f", "filename": "returns-policy.pdf", "status": "pending_upload", "upload": { "url": "https://…signed upload URL…", "method": "PUT", "headers": { "Content-Type": "application/pdf" } }, "expiresAt": "2026-11-02T18:40:00.000Z" } ``` 2. `PUT` the file’s bytes to `upload.url` with exactly the headers in `upload.headers`. The URL works until `expiresAt` (10 minutes). 3. `POST /api/v1/sources/{id}/complete` (scope: sources, returns 202). Lornos checks that the file arrived with the size you declared, then starts indexing in the background. A missing file or a different size gets `409` and the source is marked `failed`. ```sh # 1. Create the source and get an upload URL curl "$LORNOS_URL/api/v1/sources" \ -H "Authorization: Bearer $LORNOS_KEY" \ -H "Content-Type: application/json" \ -d '{"filename":"returns-policy.pdf","contentType":"application/pdf","size":482113}' # 2. PUT the file to upload.url, with exactly the headers in upload.headers curl -X PUT "" \ -H "Content-Type: application/pdf" \ --data-binary @returns-policy.pdf # 3. Tell Lornos the upload finished; indexing starts curl -X POST "$LORNOS_URL/api/v1/sources//complete" \ -H "Authorization: Bearer $LORNOS_KEY" ``` ## Status, re-index and delete - `GET /api/v1/sources`: every source, newest first, up to 200. - `GET /api/v1/sources/{id}`: one source. ```json { "id": "6f1c2d3e-8a4b-4c5d-9e6f-7a8b9c0d1e2f", "filename": "returns-policy.pdf", "title": "Returns policy", "status": "indexed", "error": null, "passages": 14, "embedded": 14, "createdAt": "2026-11-02T18:30:00.000Z", "indexedAt": "2026-11-02T18:30:21.000Z" } ``` | status | Meaning | |---|---| | `pending_upload` | Created; waiting for the file and the complete call. | | `uploaded` | The file is stored; indexing is about to start. | | `indexing` | The file is being read and split into passages. | | `indexed` | Searchable. Questions can now cite it. | | `failed` | Indexing or the upload failed; `error` says why. | `POST /api/v1/sources/{id}/reindex` indexes an uploaded source again (returns 202). `DELETE /api/v1/sources/{id}` removes the stored file and all of its passages at once (returns 204); a deleted source is never cited again. ```sh # All sources (newest first) curl "$LORNOS_URL/api/v1/sources" -H "Authorization: Bearer $LORNOS_KEY" # One source curl "$LORNOS_URL/api/v1/sources/" -H "Authorization: Bearer $LORNOS_KEY" # Index again curl -X POST "$LORNOS_URL/api/v1/sources//reindex" -H "Authorization: Bearer $LORNOS_KEY" # Delete the file and its passages curl -X DELETE "$LORNOS_URL/api/v1/sources/" -H "Authorization: Bearer $LORNOS_KEY" ``` ## Errors The sources endpoints answer errors with a JSON body. `code` is stable, so branch on it; `message` is written for people and may change. A source ID from another workspace gets the same `404` as one that doesn’t exist. ```json { "error": { "code": "file_too_large", "message": "Files can be up to 25 MB." } } ``` | Status | code | When | |---|---|---| | 400 | `invalid_request` | The body isn’t JSON, a field is missing or invalid, or the file is empty. | | 401 | `unauthorized` | The key is missing, invalid, revoked or expired. | | 403 | `forbidden` | The key doesn’t have the sources scope. | | 404 | `not_found` | No source with that ID in this key’s workspace. | | 409 | `upload_missing` | Complete was called but the file never arrived. | | 409 | `size_mismatch` | The stored file’s size differs from size. | | 409 | `already_completed` | The source was already completed. | | 409 | `upload_incomplete` | Re-index of a source whose upload isn’t complete. | | 409 | `cannot_index` | This file type can’t be indexed right now. | | 413 | `file_too_large` | The file is over 25 MB. | | 415 | `unsupported_type` | The extension isn’t supported, or contentType doesn’t match it. | | 429 | `rate_limited` | Too many requests; retry after Retry-After seconds. | | 500 | `internal_error` | Something went wrong on our side. Retry later. | | 503 | `unavailable` | Uploads aren’t available right now. Retry later. | ## Limits - **Questions:** per workspace, 30 a minute and 500 a day, counting the playground and all API keys together. Over the limit, `POST /api/chat` returns `429` with a `Retry-After` header in seconds. - **Uploads:** files up to 25 MB; each API source holds one file; the upload URL is valid for 10 minutes. - **Answers:** at most five distinct cited passages. The source list returns the 200 most recent sources. | Extensions | contentType | |---|---| | .md, .markdown, .mdx | `text/markdown` | | .txt | `text/plain` | | .html, .htm | `text/html` | | .pdf | `application/pdf` | | .docx | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` |