{
  "openapi": "3.1.0",
  "info": {
    "title": "AgentLex API",
    "version": "0.1.0",
    "summary": "Retrieval with a chain back to the page.",
    "description": "Structure-aware chunking, hybrid search, verified extraction and agentic research over the documents you index. JSON in, JSON out. Authenticated endpoints take `Authorization: Bearer al_...`; every authenticated response carries `x-ratelimit-limit`, `x-ratelimit-remaining` and `x-ratelimit-reset`, and a `429 rate_limited` adds `retry-after`. Errors are `{ \"error\": { \"code\", \"message\" } }` with a meaningful status. Reference: /docs. Console: /console.",
    "contact": { "email": "contact@khateebnotes.ai" }
  },
  "servers": [
    { "url": "https://api.agentlex.si", "description": "Production" },
    { "url": "http://localhost:4400", "description": "A local server (npm start)" }
  ],
  "tags": [
    { "name": "Public", "description": "No key needed." },
    { "name": "Account", "description": "Plan, usage and API keys." },
    { "name": "Collections", "description": "One searchable library each, with its masking and domain settings." },
    { "name": "Documents", "description": "Ingestion in any supported format, listing, pages with offsets." },
    { "name": "Search", "description": "Hybrid search and verified extraction." },
    { "name": "Research", "description": "The research agent, plain or streamed." },
    { "name": "Evaluation", "description": "Retrieval metrics and sample corpora." },
    { "name": "Identifiers", "description": "Scanning for PHI and PII." }
  ],
  "security": [{ "bearer": [] }],
  "paths": {
    "/v1/health": {
      "get": {
        "tags": ["Public"], "operationId": "health", "security": [],
        "summary": "What this server can do",
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Health" }, "example": { "ok": true, "version": "0.1.0", "embedder": "jina-embeddings-v3", "rerankers": ["jina"], "llm": true, "features": ["chunkers:classical,medical,legal,prose", "formats:text,markdown,html,csv,tsv,json,pages", "expand", "claims", "stream", "mask", "redact_storage", "scan", "evaluate", "sample", "filters", "offsets", "keys", "rate_limits"] } } } }
        }
      }
    },
    "/v1/plans": {
      "get": {
        "tags": ["Public"], "operationId": "plans", "security": [],
        "summary": "Allowances and rates per plan",
        "description": "The Library plan's unlimited allowances are serialized as `null` (JSON has no Infinity).",
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Plans" }, "example": { "scribe": { "name": "Scribe", "price": 0, "storedChars": 20000000, "search": 2000, "extract": 500, "research": 50, "rate": { "perMinute": 60, "burst": 20 } }, "scholar": { "name": "Scholar", "price": 49, "storedChars": 1000000000, "search": 100000, "extract": 20000, "research": 3000, "rate": { "perMinute": 600, "burst": 100 } }, "library": { "name": "Library", "price": null, "storedChars": null, "search": null, "extract": null, "research": null, "rate": { "perMinute": 6000, "burst": 500 } } } } } }
        }
      }
    },
    "/v1/signup": {
      "post": {
        "tags": ["Public"], "operationId": "signup", "security": [],
        "summary": "Create an account and its first key",
        "description": "An existing email returns that account with a new key. The key is shown once; only its hash is stored.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SignupRequest" }, "example": { "email": "you@institute.org" } } } },
        "responses": {
          "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SignupResponse" }, "example": { "account": { "id": "acct_Zm9vYmFyYmF6", "email": "you@institute.org", "plan": "scribe" }, "api_key": "al_3kQ9...", "note": "Store this key now; it is not shown again." } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/v1/chunk": {
      "post": {
        "tags": ["Public", "Documents"], "operationId": "chunk", "security": [],
        "summary": "Preview the chunking of a document",
        "description": "The same input as adding a document. Nothing is stored and no key is needed: returns the pages and chunks the index would hold, with kinds, offsets and footnote links.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentInput" }, "example": { "text": "# Tides\n\nThe spring tide reaches the warehouse steps twice a month.", "chunker": "prose" } } } },
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChunkPreview" }, "example": { "chunker": "prose", "format": "text", "meta": { "kind": "prose" }, "pages": [{ "n": 0, "number": null, "volume": null, "heading": "Tides", "chars": 66, "meta": { "tables": [] }, "chunks": [{ "pos": 0, "text": "# Tides\n\nThe spring tide reaches the warehouse steps twice a month.", "kinds": ["heading"], "heading": "Tides", "start": 0, "end": 66, "meta": null, "footnoteOf": null }] }] } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "413": { "description": "document_too_large or body_too_large", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/v1/account": {
      "get": {
        "tags": ["Account"], "operationId": "account",
        "summary": "Plan, this month's usage, allowances and keys",
        "responses": {
          "200": { "description": "OK", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Account" }, "example": { "id": "acct_Zm9vYmFyYmF6", "email": "you@institute.org", "plan": "scribe", "usage": { "search": 12, "pages_indexed": 340, "stored_chars": 812000 }, "allowances": { "name": "Scribe", "price": 0, "storedChars": 20000000, "search": 2000, "extract": 500, "research": 50, "rate": { "perMinute": 60, "burst": 20 } }, "keys": [{ "prefix": "al_3kQ9abc", "created_at": "2026-10-01T09:00:00.000Z", "revoked_at": null }] } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/keys": {
      "post": {
        "tags": ["Account"], "operationId": "createKey",
        "summary": "Issue a new key for the account",
        "responses": {
          "201": { "description": "Created; the key is shown once", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KeyCreated" }, "example": { "api_key": "al_7hT2...", "prefix": "al_7hT2xyz", "note": "Store this key now; it is not shown again." } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "get": {
        "tags": ["Account"], "operationId": "listKeys",
        "summary": "The account's keys by prefix",
        "responses": {
          "200": { "description": "OK", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KeysList" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/keys/{prefix}": {
      "delete": {
        "tags": ["Account"], "operationId": "revokeKey",
        "summary": "Revoke a key",
        "description": "A revoked key is refused from then on (401 invalid_key). The key making the request cannot revoke itself.",
        "parameters": [{ "$ref": "#/components/parameters/KeyPrefix" }],
        "responses": {
          "200": { "description": "Revoked", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KeyRevoked" }, "example": { "revoked": "al_7hT2xyz", "revoked_at": "2026-10-15T10:12:00.000Z" } } } },
          "400": { "description": "cannot_revoke_current", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "description": "key_not_found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/scan": {
      "post": {
        "tags": ["Identifiers"], "operationId": "scan",
        "summary": "Count the identifiers a text carries",
        "description": "Counts by kind and a masked preview of the first 300 characters. Spans are never returned, so the identifiers are not echoed back. With `kinds` only those kinds are counted and masked; without it every kind is counted and the preview masks the default set (every kind but date and url).",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScanRequest" }, "example": { "text": "Call 555-123-4567 or write to ada@example.com about the 2024-01-15 visit." } } } },
        "responses": {
          "200": { "description": "OK", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScanResponse" }, "example": { "total": 3, "counts": { "phone": 1, "email": 1, "date": 1 }, "kinds": ["phone", "email", "date"], "masked_preview": "Call [PHONE-1] or write to [EMAIL-1] about the 2024-01-15 visit." } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/collections": {
      "get": {
        "tags": ["Collections"], "operationId": "listCollections",
        "summary": "The account's collections",
        "responses": {
          "200": { "description": "OK", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionsList" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "post": {
        "tags": ["Collections"], "operationId": "createCollection",
        "summary": "Create a collection",
        "description": "A collection records the embedding model it was built with; a server with a different model refuses to add vectors to it (409 embedder_mismatch). Settings default to `{ mask_identifiers: false, redact_storage: false, domain: 'auto' }`; an unknown key or a wrong type is a 400 invalid_settings.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreate" }, "example": { "name": "Case files", "settings": { "domain": "legal", "mask_identifiers": true } } } } },
        "responses": {
          "201": { "description": "Created", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Collection" }, "example": { "id": "col_8sK2mQ1xYz", "account_id": "acct_Zm9vYmFyYmF6", "name": "Case files", "embedder": "jina-embeddings-v3", "created_at": "2026-10-15T10:00:00.000Z", "settings": { "mask_identifiers": true, "redact_storage": false, "domain": "legal" }, "documents": 0, "pages": 0, "chunks": 0, "domain": null } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/collections/{id}": {
      "parameters": [{ "$ref": "#/components/parameters/CollectionId" }],
      "get": {
        "tags": ["Collections"], "operationId": "getCollection", "summary": "One collection with its counts and domain",
        "responses": {
          "200": { "description": "OK", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Collection" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "patch": {
        "tags": ["Collections"], "operationId": "updateCollection", "summary": "Rename a collection or change its settings",
        "description": "`settings` is merged over the current settings. Changing them does not touch documents already indexed.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionPatch" }, "example": { "settings": { "redact_storage": true } } } } },
        "responses": {
          "200": { "description": "OK", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Collection" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "delete": {
        "tags": ["Collections"], "operationId": "deleteCollection", "summary": "Delete a collection and everything in it",
        "responses": {
          "200": { "description": "Deleted", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Deleted" }, "example": { "deleted": "col_8sK2mQ1xYz" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/collections/{id}/documents": {
      "parameters": [{ "$ref": "#/components/parameters/CollectionId" }],
      "post": {
        "tags": ["Documents"], "operationId": "addDocument",
        "summary": "Index a document",
        "description": "Any supported format: plain text or Markdown (`text`), `html`, `csv`, `tsv`, `json` (an array of records or an object), `pages[]` for printed pages, or `content` with `format` naming what it is. A bare JSON string is a text document. The chunker is `chunker` when given, else what the format implies, else the collection's `settings.domain`, else detected from the text. Under `redact_storage` the stored text, title and author are masked and `meta` gains `identifiers` and `redacted: true`; under `mask_identifiers` only what the embedder sees is masked. `identifiers` always counts what the original text carried.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentInput" }, "examples": {
          "text": { "summary": "Markdown text", "value": { "title": "Water law", "author": "A. Rivers", "metadata": { "topic": "water", "filed": "2024-03-14" }, "text": "# Water rights\n\nIrrigation districts allocate water by seniority..." } },
          "pages": { "summary": "Printed pages", "value": { "title": "Sunan", "chunker": "classical", "pages": [{ "number": "12", "volume": "1", "heading": "Book of Purification", "text": "..." }] } },
          "csv": { "summary": "A CSV dump", "value": { "title": "Crew", "format": "csv", "text": "name,role\nAda,engineer\nGrace,admiral" } }
        } } } },
        "responses": {
          "201": { "description": "Indexed", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentCreated" }, "example": { "id": "doc_Qx1pL9vB2n", "pages": 3, "chunks": 11, "chars": 6210, "chunker": "legal", "embedder": "jina-embeddings-v3", "format": "text", "meta": { "kind": "legal", "form": "contract", "citations": [], "terms": ["Confidential Information"], "sections": ["1", "1.1", "2", "2.1"] }, "identifiers": { "total": 0, "counts": {} } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": { "description": "embedder_mismatch", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "413": { "description": "document_too_large or body_too_large", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "storage_exceeded or rate_limited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      },
      "get": {
        "tags": ["Documents"], "operationId": "listDocuments", "summary": "The documents of a collection",
        "responses": {
          "200": { "description": "OK", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentsList" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/collections/{id}/documents/{doc}": {
      "parameters": [{ "$ref": "#/components/parameters/CollectionId" }, { "$ref": "#/components/parameters/DocumentId" }],
      "get": {
        "tags": ["Documents"], "operationId": "getDocument", "summary": "One document's row",
        "responses": {
          "200": { "description": "OK", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Document" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "delete": {
        "tags": ["Documents"], "operationId": "deleteDocument", "summary": "Delete a document",
        "responses": {
          "200": { "description": "Deleted", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Deleted" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/collections/{id}/documents/{doc}/pages/{n}": {
      "parameters": [{ "$ref": "#/components/parameters/CollectionId" }, { "$ref": "#/components/parameters/DocumentId" }, { "$ref": "#/components/parameters/PageN" }],
      "get": {
        "tags": ["Documents"], "operationId": "getPage",
        "summary": "A page's text, meta and chunks with offsets",
        "description": "`n` is the page index counted from 0, the `page.n` every search result carries. Each chunk's `start` and `end` are offsets into `page.text` (null when the chunker could not place it), `kinds` names its flags, and `footnoteOf` is the position of the body chunk a classical footnote belongs to.",
        "responses": {
          "200": { "description": "OK", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PageView" }, "example": { "document": { "id": "doc_Qx1pL9vB2n", "title": "Water law", "author": null }, "page": { "n": 0, "number": null, "volume": null, "heading": "Water rights", "text": "# Water rights\n\nIrrigation districts allocate water by seniority.", "meta": { "tables": [] } }, "chunks": [{ "pos": 0, "text": "# Water rights\n\nIrrigation districts allocate water by seniority.", "kinds": ["heading"], "heading": "Water rights", "start": 0, "end": 64, "meta": null, "footnoteOf": null }] } } } },
          "400": { "description": "invalid_page", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "description": "collection_not_found, document_not_found or page_not_found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/collections/{id}/search": {
      "parameters": [{ "$ref": "#/components/parameters/CollectionId" }],
      "post": {
        "tags": ["Search"], "operationId": "search",
        "summary": "Hybrid search",
        "description": "Keyword (BM25 over chunks) and semantic (page vectors) lists fused by weighted reciprocal rank, filtered first, quoted phrases enforced, near-duplicates folded, reranked when a reranker is configured, and the best passages of each page returned with stable citation ids. Counts one search against the monthly allowance.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchRequest" }, "example": { "query": "audit rights", "k": 5, "kinds": ["clause"], "filters": { "metadata": { "topic": "law" }, "date": { "from": "2024-01-01" } } } } } },
        "responses": {
          "200": { "description": "OK", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchResponse" }, "example": { "query": "audit rights", "mode": "hybrid", "domain": "legal", "expand": true, "intent": "keyword", "phrases": [], "expansions": [], "filters": { "documents": [], "kinds": ["clause"], "metadata": { "topic": "law" }, "date": { "from": "2024-01-01", "to": null }, "headings": null }, "warnings": [], "results": [{ "rank": 1, "citation": "AL-4F2A91C0", "score": 0.0325, "document": { "id": "doc_Qx1pL9vB2n", "title": "MSA", "author": null }, "page": { "n": 1, "number": null, "volume": null, "heading": "2. Audit" }, "heading": "2. Audit", "passages": [{ "id": 381, "pos": 0, "text": "2.1 Provider shall permit one audit per year on reasonable notice.", "kinds": ["clause"], "heading": "2. Audit", "offsets": { "start": 10, "end": 77 }, "matched": ["audit"], "meta": { "label": "2.1" } }], "retrievers": ["keyword", "semantic"], "duplicates": 0, "alsoIn": 0 }], "reranker": { "name": "jina", "model": "jina-reranker-v3" }, "timings": { "expandMs": 1, "keywordMs": 3, "embedMs": 412, "semanticMs": 419, "fieldsMs": 1, "rerankMs": 380, "totalMs": 806 }, "considered": 14 } } } },
          "400": { "description": "invalid_query, query_too_long or invalid_filter", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": { "description": "embedder_mismatch (use mode keyword or re-index)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "allowance_exceeded or rate_limited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/v1/collections/{id}/extract": {
      "parameters": [{ "$ref": "#/components/parameters/CollectionId" }],
      "post": {
        "tags": ["Search"], "operationId": "extract",
        "summary": "Verified excerpts that answer a question",
        "description": "Searches (k pages, at most 20), then a fast reader copies out of each page only the sentences that answer the question. Every excerpt is matched against its page by code; one that is not on the page is counted in `rejectedExcerpts` and never returned. Counts one extract per page read against the monthly allowance. Needs a model on the server (503 llm_not_configured otherwise). With `mask_identifiers` or `redact_storage` on the collection the reader sees masked text and `masked` is true.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtractRequest" }, "example": { "question": "How often may the customer audit the provider?", "k": 6 } } } },
        "responses": {
          "200": { "description": "OK", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtractResponse" }, "example": { "question": "How often may the customer audit the provider?", "pages": [{ "citation": "AL-4F2A91C0", "relevant": true, "note": "States the audit frequency.", "excerpts": [{ "text": "2.1 Provider shall permit one audit per year on reasonable notice.", "match": "exact", "offset": { "start": 10, "end": 77 } }], "rejected": 0, "document": { "id": "doc_Qx1pL9vB2n", "title": "MSA", "author": null }, "page": { "n": 1, "number": null, "volume": null, "heading": "2. Audit" } }], "kept": 1, "rejectedExcerpts": 0, "usage": { "input_tokens": 1830, "output_tokens": 96 }, "model": "claude-haiku-5-5", "masked": false } } } },
          "400": { "description": "invalid_question or invalid_filter", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "description": "allowance_exceeded or rate_limited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "502": { "$ref": "#/components/responses/ModelFailed" },
          "503": { "$ref": "#/components/responses/ModelUnavailable" }
        }
      }
    },
    "/v1/collections/{id}/research": {
      "parameters": [{ "$ref": "#/components/parameters/CollectionId" }],
      "post": {
        "tags": ["Research"], "operationId": "research",
        "summary": "A cited answer, verified against its sources",
        "description": "A model searches and reads the collection in a tool loop (fast: 3 steps; deep: 8), an answer is written from the sources it found with `[n]` citations, every quotation is checked against the page it cites, and every sentence is judged against its cited sources. Spends five rate tokens and one research of the monthly allowance. At most two run at once per account (429 too_many_concurrent). With `stream: true` the response is `application/x-ndjson`, one event per line (see Event); the HTTP status is then always 200 and a failure arrives as a final `error` event. `documents`, `kinds` and `filters` bound every search the model issues.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResearchRequest" }, "example": { "question": "How often may the customer audit, and what records must be kept?", "tier": "deep", "stream": true, "filters": { "metadata": { "topic": "law" } } } } } },
        "responses": {
          "200": { "description": "The result, or the event stream when `stream` is true", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": {
            "application/json": { "schema": { "$ref": "#/components/schemas/ResearchResult" }, "example": { "answer": "The provider must permit one audit per year on reasonable notice [1] and keep records of each audit for three years [1].", "sources": [{ "n": 1, "citation": "AL-4F2A91C0", "document": { "id": "doc_Qx1pL9vB2n", "title": "MSA", "author": null }, "page": { "n": 1, "number": null, "volume": null, "heading": "2. Audit" }, "heading": "2. Audit", "kinds": ["clause"], "meta": { "label": "2.1" }, "cited": true, "passages": ["2.1 Provider shall permit one audit per year on reasonable notice.", "2.2 Provider shall keep records of each audit for three years."] }], "verification": { "quotes": [], "verified": 0, "total": 0, "claims": [{ "text": "The provider must permit one audit per year on reasonable notice", "sources": [1], "status": "supported", "evidence": "Provider shall permit one audit per year on reasonable notice.", "start": 0, "end": 70 }], "counts": { "supported": 2, "partial": 0, "unsupported": 0, "uncited": 0, "unchecked": 0, "total": 2 }, "summary": "2 of 2 claims supported by their sources." }, "queries": [{ "query": "How often may the customer audit, and what records must be kept?", "mode": "hybrid", "results": 3, "filters": { "documents": [], "kinds": [], "metadata": { "topic": "law" }, "date": null, "headings": null } }], "note": "Found the audit clause.", "tier": "deep", "domain": "legal", "filters": { "documents": [], "kinds": [], "metadata": { "topic": "law" }, "date": null, "headings": null }, "usage": { "input_tokens": 9120, "output_tokens": 410 }, "timings": { "researchMs": 6100, "answerMs": 2900, "verifyMs": 1800, "totalMs": 10800, "steps": 2 } } },
            "application/x-ndjson": { "schema": { "$ref": "#/components/schemas/Event" }, "example": "{\"type\":\"status\",\"stage\":\"searching\"}\n{\"type\":\"query\",\"query\":\"audit frequency\",\"mode\":\"hybrid\",\"results\":3}\n{\"type\":\"sources\",\"sources\":[...]}\n{\"type\":\"status\",\"stage\":\"answering\"}\n{\"type\":\"delta\",\"text\":\"The provider \"}\n{\"type\":\"answer\",\"text\":\"The provider must permit one audit per year [1].\"}\n{\"type\":\"status\",\"stage\":\"verifying\"}\n{\"type\":\"claims\",\"counts\":{...},\"summary\":\"1 of 1 claims supported by its source.\"}\n{\"type\":\"done\",\"result\":{...}}\n" }
          } },
          "400": { "description": "invalid_question, invalid_tier, invalid_domain or invalid_filter", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "description": "allowance_exceeded, rate_limited or too_many_concurrent", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "502": { "$ref": "#/components/responses/ModelFailed" },
          "503": { "$ref": "#/components/responses/ModelUnavailable" }
        }
      }
    },
    "/v1/collections/{id}/evaluate": {
      "parameters": [{ "$ref": "#/components/parameters/CollectionId" }],
      "post": {
        "tags": ["Evaluation"], "operationId": "evaluate",
        "summary": "Retrieval metrics per variant",
        "description": "Runs every question through every variant (keyword, semantic, hybrid, hybrid+rerank by default) and scores nDCG, precision, recall, MRR and MAP at k. Relevant items are resolved to pages before any search runs; one that cannot be resolved is a note, not a miss. Spends five rate tokens and one search of the allowance per question per variant. At most 1000 questions per call.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EvaluateRequest" }, "example": { "questions": [{ "query": "audit per year", "relevant": [{ "text": "permit one audit per year" }] }, { "query": "reservoir storage", "relevant": [{ "document": "Water law", "page": 2 }], "gains": [2] }], "k": 5, "variants": ["keyword", "hybrid"] } } } },
        "responses": {
          "200": { "description": "OK", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EvaluateResponse" }, "example": { "n": 2, "k": 5, "embedder": "jina-embeddings-v3", "reranker": null, "variants": { "keyword": { "ndcg": 0.8155, "precision": 0.2, "recall": 1, "mrr": 0.75, "map": 0.75, "latency": { "p50": 4.1, "p95": 6.3 }, "found": 2 }, "hybrid": { "ndcg": 1, "precision": 0.2, "recall": 1, "mrr": 1, "map": 1, "latency": { "p50": 31.2, "p95": 40.8 }, "found": 2 } }, "perQuestion": [{ "query": "audit per year", "variant": "keyword", "ndcg": 1, "firstHit": 1, "latencyMs": 4.1, "missing": [], "ranked": ["AL-4F2A91C0", "AL-0B77D1E3"], "hits": [{ "item": "permit one audit per year", "rank": 1, "citation": "AL-4F2A91C0" }] }], "notes": [] } } } },
          "400": { "description": "invalid_questions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "description": "allowance_exceeded or rate_limited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/v1/collections/{id}/sample": {
      "parameters": [{ "$ref": "#/components/parameters/CollectionId" }],
      "post": {
        "tags": ["Evaluation"], "operationId": "sample",
        "summary": "Index a bundled sample corpus",
        "description": "Indexes the server's sample documents of a domain into the collection, one by one through the same path as adding a document (metadata kept, storage checked, pages metered).",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SampleRequest" }, "example": { "domain": "medical" } } } },
        "responses": {
          "201": { "description": "Indexed", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SampleResponse" }, "example": { "documents": 53, "pages": 61, "chunks": 400 } } } },
          "400": { "description": "invalid_domain", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "description": "collection_not_found or sample_not_found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "storage_exceeded or rate_limited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": { "description": "sample_unreadable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": { "type": "http", "scheme": "bearer", "bearerFormat": "al_...", "description": "An API key from POST /v1/signup or POST /v1/keys." }
    },
    "headers": {
      "x-ratelimit-limit": { "description": "The plan's burst: the most tokens a key can spend at once.", "schema": { "type": "integer" } },
      "x-ratelimit-remaining": { "description": "Tokens left in the bucket after this request.", "schema": { "type": "integer" } },
      "x-ratelimit-reset": { "description": "Seconds until the bucket can serve again (0 while tokens remain).", "schema": { "type": "integer" } },
      "retry-after": { "description": "Seconds to wait before retrying (on a 429 rate_limited).", "schema": { "type": "integer" } }
    },
    "parameters": {
      "CollectionId": { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "example": "col_8sK2mQ1xYz" },
      "DocumentId": { "name": "doc", "in": "path", "required": true, "schema": { "type": "string" }, "example": "doc_Qx1pL9vB2n" },
      "PageN": { "name": "n", "in": "path", "required": true, "description": "Page index counted from 0.", "schema": { "type": "integer", "minimum": 0 } },
      "KeyPrefix": { "name": "prefix", "in": "path", "required": true, "description": "The first 10 characters of the key (al_ included).", "schema": { "type": "string" }, "example": "al_7hT2xyz" }
    },
    "responses": {
      "BadRequest": { "description": "A 400 with a code naming what is wrong (invalid_json, invalid_email, invalid_name, invalid_settings, invalid_document, invalid_text, ...).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "invalid_settings", "message": "settings.domain must be one of auto, medical, legal, classical, general" } } } } },
      "Unauthorized": { "description": "missing_key or invalid_key. No rate headers.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "invalid_key", "message": "That API key is not valid." } } } } },
      "NotFound": { "description": "collection_not_found, document_not_found or not_found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "collection_not_found", "message": "No collection col_nope." } } } } },
      "RateLimited": { "description": "rate_limited: the plan's rate for this key is spent. Wait retry-after seconds.", "headers": { "x-ratelimit-limit": { "$ref": "#/components/headers/x-ratelimit-limit" }, "x-ratelimit-remaining": { "$ref": "#/components/headers/x-ratelimit-remaining" }, "x-ratelimit-reset": { "$ref": "#/components/headers/x-ratelimit-reset" }, "retry-after": { "$ref": "#/components/headers/retry-after" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "rate_limited", "message": "Rate limit reached for the Scribe plan; retry in 12 s." } } } } },
      "ModelFailed": { "description": "llm_error, llm_unreachable or provider_* : the model or a provider failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "ModelUnavailable": { "description": "llm_not_configured (the server has no model) or llm_busy (retry shortly).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "Error": {
        "type": "object", "required": ["error"],
        "properties": { "error": { "type": "object", "required": ["code", "message"], "properties": { "code": { "type": "string", "description": "A stable code: invalid_json, invalid_email, invalid_name, invalid_settings, invalid_document, invalid_query, query_too_long, invalid_filter, invalid_question, invalid_questions, invalid_tier, invalid_domain, invalid_text, invalid_page, cannot_revoke_current, missing_key, invalid_key, not_found, collection_not_found, document_not_found, page_not_found, key_not_found, sample_not_found, embedder_mismatch, body_too_large, document_too_large, allowance_exceeded, storage_exceeded, rate_limited, too_many_concurrent, llm_error, llm_unreachable, provider_*, llm_not_configured, llm_busy, internal, sample_unreadable." }, "message": { "type": "string" } } } }
      },
      "Rate": { "type": "object", "required": ["perMinute", "burst"], "properties": { "perMinute": { "type": "integer", "description": "Sustained requests per minute." }, "burst": { "type": "integer", "description": "The most tokens a key can spend at once (research and evaluate spend 5, every other call 1)." } } },
      "Plan": {
        "type": "object", "required": ["name", "price", "storedChars", "search", "extract", "research", "rate"],
        "properties": { "name": { "type": "string" }, "price": { "type": ["number", "null"], "description": "USD per month; null when quoted." }, "storedChars": { "type": ["integer", "null"], "description": "Characters of page text the account may store; null means unlimited." }, "search": { "type": ["integer", "null"], "description": "Searches per month; null means unlimited." }, "extract": { "type": ["integer", "null"], "description": "Pages read by extraction per month." }, "research": { "type": ["integer", "null"], "description": "Research calls per month." }, "rate": { "$ref": "#/components/schemas/Rate" } }
      },
      "Plans": { "type": "object", "required": ["scribe", "scholar", "library"], "properties": { "scribe": { "$ref": "#/components/schemas/Plan" }, "scholar": { "$ref": "#/components/schemas/Plan" }, "library": { "$ref": "#/components/schemas/Plan" } } },
      "Health": {
        "type": "object", "required": ["ok", "version", "embedder", "rerankers", "llm", "features"],
        "properties": { "ok": { "type": "boolean" }, "version": { "type": ["string", "null"] }, "embedder": { "type": "string", "description": "The embedding model id this server indexes with; collections record it." }, "rerankers": { "type": "array", "items": { "type": "string" } }, "llm": { "type": "boolean", "description": "Whether extract and research are available." }, "features": { "type": "array", "items": { "type": "string" } } }
      },
      "SignupRequest": { "type": "object", "required": ["email"], "properties": { "email": { "type": "string", "format": "email" } } },
      "SignupResponse": {
        "type": "object", "required": ["account", "api_key", "note"],
        "properties": { "account": { "type": "object", "required": ["id", "email", "plan"], "properties": { "id": { "type": "string" }, "email": { "type": "string" }, "plan": { "type": "string", "enum": ["scribe", "scholar", "library"] } } }, "api_key": { "type": "string", "description": "Shown once." }, "note": { "type": "string" } }
      },
      "Key": { "type": "object", "required": ["prefix", "created_at", "revoked_at"], "properties": { "prefix": { "type": "string", "description": "The key's first 10 characters." }, "created_at": { "type": "string", "format": "date-time" }, "revoked_at": { "type": ["string", "null"], "format": "date-time" } } },
      "KeyCreated": { "type": "object", "required": ["api_key", "prefix", "note"], "properties": { "api_key": { "type": "string" }, "prefix": { "type": "string" }, "note": { "type": "string" } } },
      "KeysList": { "type": "object", "required": ["keys"], "properties": { "keys": { "type": "array", "items": { "$ref": "#/components/schemas/Key" } } } },
      "KeyRevoked": { "type": "object", "required": ["revoked", "revoked_at"], "properties": { "revoked": { "type": "string" }, "revoked_at": { "type": "string", "format": "date-time" } } },
      "Account": {
        "type": "object", "required": ["id", "email", "plan", "usage", "allowances", "keys"],
        "properties": {
          "id": { "type": "string" }, "email": { "type": "string" }, "plan": { "type": "string", "enum": ["scribe", "scholar", "library"] },
          "usage": { "type": "object", "description": "This month's counts; an operation not yet used is absent.", "required": ["stored_chars"], "properties": { "search": { "type": "integer" }, "extract": { "type": "integer" }, "research": { "type": "integer" }, "pages_indexed": { "type": "integer" }, "stored_chars": { "type": "integer" } } },
          "allowances": { "$ref": "#/components/schemas/Plan" },
          "keys": { "type": "array", "items": { "$ref": "#/components/schemas/Key" } }
        }
      },
      "IdentifierKind": { "type": "string", "enum": ["ssn", "phone", "email", "mrn", "dob", "credit_card", "ip", "url", "passport", "npi", "address", "person", "date"], "description": "Placeholders are [SSN-n], [PHONE-n], [EMAIL-n], [MRN-n], [DOB-n], [CARD-n], [IP-n], [URL-n], [PASSPORT-n], [NPI-n], [ADDRESS-n], [NAME-n], [DATE-n]; the same value of a kind keeps one placeholder within a text. date and url are counted but not masked by default." },
      "Identifiers": { "type": "object", "required": ["total", "counts"], "properties": { "total": { "type": "integer" }, "counts": { "type": "object", "additionalProperties": { "type": "integer" }, "description": "Count per identifier kind found." } } },
      "ScanRequest": { "type": "object", "required": ["text"], "properties": { "text": { "type": "string" }, "kinds": { "type": "array", "items": { "$ref": "#/components/schemas/IdentifierKind" }, "description": "Only these kinds; unknown names are ignored." } } },
      "ScanResponse": { "type": "object", "required": ["total", "counts", "kinds", "masked_preview"], "properties": { "total": { "type": "integer" }, "counts": { "type": "object", "additionalProperties": { "type": "integer" } }, "kinds": { "type": "array", "items": { "$ref": "#/components/schemas/IdentifierKind" }, "description": "The kinds found, in count order." }, "masked_preview": { "type": "string", "description": "The first 300 characters of the masked text." } } },
      "Settings": {
        "type": "object",
        "properties": {
          "mask_identifiers": { "type": "boolean", "default": false, "description": "Mask identifiers in what the embedder and the extraction reader see; the stored text keeps them. Research and search results are not masked." },
          "redact_storage": { "type": "boolean", "default": false, "description": "Mask the text, title and author before chunking and store only the masked form; the originals are never written. The document's meta records the counts under identifiers and redacted: true." },
          "domain": { "type": "string", "enum": ["auto", "medical", "legal", "classical", "general"], "default": "auto", "description": "The chunker documents get (medical, legal, classical, general = prose) unless the request names one; auto detects per document. Also the default search and research domain." },
          "guidance": { "type": ["string", "null"], "maxLength": 2000, "description": "House rules added to the research and answer prompts for this collection (the organization's terms, which document controls, how answers are laid out). They add to the built-in rules and never override them. null removes them." },
          "glossary": { "type": ["object", "null"], "maxProperties": 300, "additionalProperties": { "oneOf": [{ "type": "string", "maxLength": 120 }, { "type": "array", "items": { "type": "string", "maxLength": 120 }, "minItems": 1, "maxItems": 5 }] }, "description": "The organization's own shorthand, spelled out in queries like the built-in abbreviation lexicons and before them: { \"SSA\": \"Software Services Agreement\" }. null removes it." }
        }
      },
      "CollectionCreate": { "type": "object", "required": ["name"], "properties": { "name": { "type": "string", "maxLength": 120 }, "settings": { "$ref": "#/components/schemas/Settings" } } },
      "CollectionPatch": { "type": "object", "properties": { "name": { "type": "string", "maxLength": 120 }, "settings": { "$ref": "#/components/schemas/Settings" } } },
      "Collection": {
        "type": "object", "required": ["id", "account_id", "name", "embedder", "created_at", "settings", "documents", "pages", "chunks", "domain"],
        "properties": {
          "id": { "type": "string" }, "account_id": { "type": "string" }, "name": { "type": "string" }, "embedder": { "type": "string", "description": "The embedding model the collection was built with." }, "created_at": { "type": "string", "format": "date-time" },
          "settings": { "$ref": "#/components/schemas/Settings" },
          "documents": { "type": "integer" }, "pages": { "type": "integer" }, "chunks": { "type": "integer" },
          "domain": { "type": ["string", "null"], "enum": ["medical", "legal", "classical", "prose", null], "description": "The chunker most of its documents used; null with no documents." }
        }
      },
      "CollectionsList": { "type": "object", "required": ["collections"], "properties": { "collections": { "type": "array", "items": { "$ref": "#/components/schemas/Collection" } } } },
      "PageInput": { "oneOf": [{ "type": "string" }, { "type": "object", "required": ["text"], "properties": { "text": { "type": "string" }, "number": { "type": ["string", "integer", "null"], "description": "The printed page number, kept for citation." }, "volume": { "type": ["string", "integer", "null"] }, "heading": { "type": ["string", "null"] } } }] },
      "DocumentInput": {
        "description": "A bare string is a text document. Otherwise exactly one payload field: text (plain or Markdown; form feeds or '--- page N ---' lines split it into pages), html, csv, tsv, json (an array of records, one page each, or an object), pages[], or content with format saying what it is.",
        "oneOf": [
          { "type": "string" },
          {
            "type": "object",
            "properties": {
              "title": { "type": ["string", "null"] }, "author": { "type": ["string", "null"] },
              "metadata": { "type": "object", "additionalProperties": true, "description": "Any object, stored with the document and usable in filters.metadata. A date, encounter_date, effective_date or filed field feeds filters.date." },
              "chunker": { "type": "string", "enum": ["classical", "medical", "legal", "prose"], "description": "Forces a chunker; otherwise the format, the collection's domain setting or detection decides." },
              "format": { "type": "string", "enum": ["auto", "text", "markdown", "html", "csv", "tsv", "json", "pages"], "default": "auto" },
              "text": { "type": "string" }, "html": { "type": "string" }, "csv": { "type": "string" }, "tsv": { "type": "string" },
              "json": { "description": "A JSON string, an array of records or an object." },
              "pages": { "type": "array", "items": { "$ref": "#/components/schemas/PageInput" } },
              "content": { "description": "The payload when format names its kind." }
            }
          }
        ]
      },
      "DocumentCreated": {
        "type": "object", "required": ["id", "pages", "chunks", "chars", "chunker", "embedder", "format", "meta", "identifiers"],
        "properties": {
          "id": { "type": "string" }, "pages": { "type": "integer" }, "chunks": { "type": "integer" }, "chars": { "type": "integer", "description": "Characters of page text stored; counts against storedChars." },
          "chunker": { "type": "string", "enum": ["classical", "medical", "legal", "prose"] }, "embedder": { "type": "string" },
          "format": { "type": "string", "enum": ["text", "markdown", "html", "csv", "tsv", "json", "pages"] },
          "meta": { "type": "object", "additionalProperties": true, "description": "What the chunker found: { kind } plus, medical: encounterDate, dates, codes, sections; legal: form, citations, terms, sections. Under redact_storage also identifiers (counts) and redacted: true." },
          "identifiers": { "$ref": "#/components/schemas/Identifiers" }
        }
      },
      "Document": {
        "type": "object", "required": ["id", "title", "author", "metadata", "chunker", "pages", "chunks", "chars", "created_at", "meta"],
        "properties": { "id": { "type": "string" }, "title": { "type": ["string", "null"] }, "author": { "type": ["string", "null"] }, "metadata": { "type": "object", "additionalProperties": true }, "chunker": { "type": "string" }, "pages": { "type": "integer" }, "chunks": { "type": "integer" }, "chars": { "type": "integer" }, "created_at": { "type": "string", "format": "date-time" }, "meta": { "type": "object", "additionalProperties": true } }
      },
      "DocumentsList": { "type": "object", "required": ["documents"], "properties": { "documents": { "type": "array", "items": { "$ref": "#/components/schemas/Document" } } } },
      "Kind": { "type": "string", "enum": ["hadith", "grading", "verse", "poetry", "footnote", "heading", "takhrij", "agentlex", "table", "list", "medication", "lab", "vital", "diagnosis", "plan", "allergy", "citation", "definition", "clause", "date", "speaker", "code"], "description": "What a chunk holds. Classical Arabic: hadith, grading, verse, poetry, footnote, takhrij. Clinical notes: medication, lab, vital, diagnosis, plan, allergy. Legal texts: citation, definition, clause. Anywhere: heading, table, list, date, speaker. agentlex and code are bookkeeping bits a search does not filter by." },
      "Chunk": {
        "type": "object", "required": ["pos", "text", "kinds", "heading", "start", "end", "meta", "footnoteOf"],
        "properties": { "pos": { "type": "integer", "description": "Position on the page, from 0." }, "text": { "type": "string" }, "kinds": { "type": "array", "items": { "$ref": "#/components/schemas/Kind" } }, "heading": { "type": ["string", "null"] }, "start": { "type": ["integer", "null"], "description": "Offset of the chunk in the page text." }, "end": { "type": ["integer", "null"] }, "meta": { "type": ["object", "null"], "additionalProperties": true }, "footnoteOf": { "type": ["integer", "null"], "description": "For a classical footnote, the pos of the body chunk it belongs to." } }
      },
      "ChunkPreview": {
        "type": "object", "required": ["chunker", "format", "meta", "pages"],
        "properties": { "chunker": { "type": "string" }, "format": { "type": "string" }, "meta": { "type": "object", "additionalProperties": true }, "pages": { "type": "array", "items": { "type": "object", "required": ["n", "number", "volume", "heading", "chars", "meta", "chunks"], "properties": { "n": { "type": "integer" }, "number": { "type": ["string", "null"] }, "volume": { "type": ["string", "null"] }, "heading": { "type": ["string", "null"] }, "chars": { "type": "integer" }, "meta": { "type": ["object", "null"], "additionalProperties": true }, "chunks": { "type": "array", "items": { "$ref": "#/components/schemas/Chunk" } } } } } }
      },
      "PageView": {
        "type": "object", "required": ["document", "page", "chunks"],
        "properties": {
          "document": { "$ref": "#/components/schemas/DocumentRef" },
          "page": { "type": "object", "required": ["n", "number", "volume", "heading", "text", "meta"], "properties": { "n": { "type": "integer" }, "number": { "type": ["string", "null"] }, "volume": { "type": ["string", "null"] }, "heading": { "type": ["string", "null"] }, "text": { "type": "string" }, "meta": { "type": ["object", "null"], "additionalProperties": true } } },
          "chunks": { "type": "array", "items": { "$ref": "#/components/schemas/Chunk" } }
        }
      },
      "DocumentRef": { "type": "object", "required": ["id", "title", "author"], "properties": { "id": { "type": "string" }, "title": { "type": ["string", "null"] }, "author": { "type": ["string", "null"] } } },
      "PageRef": { "type": "object", "required": ["n", "number", "volume", "heading"], "properties": { "n": { "type": "integer", "description": "Page index from 0; use it with GET .../pages/{n}." }, "number": { "type": ["string", "null"], "description": "The printed page number when the document had one." }, "volume": { "type": ["string", "null"] }, "heading": { "type": ["string", "null"] } } },
      "Filters": {
        "type": "object", "description": "Restricts the pages searched before fusion. Search, extract, research and evaluate all take it; research also lets the model narrow within it.",
        "properties": {
          "documents": { "type": "array", "items": { "type": "string" }, "description": "Document ids." },
          "kinds": { "type": "array", "items": { "$ref": "#/components/schemas/Kind" }, "description": "Only passages of these kinds; a page with none still returns its best chunks when it matches otherwise." },
          "metadata": { "type": "object", "additionalProperties": true, "description": "Each key must equal the document's metadata value (a value or a list of accepted values; dotted keys reach into objects)." },
          "date": { "type": "object", "properties": { "from": { "type": "string", "description": "YYYY-MM-DD, 03/14/2024, 14 Mar 2024, March 14, 2024 or a year." }, "to": { "type": "string" } }, "description": "Pages dated in the range by their document metadata (date, encounter_date, effective_date, filed) or the dates the chunker found." },
          "headings": { "type": "string", "description": "Only pages whose heading or section headings contain this text." }
        }
      },
      "FiltersEcho": {
        "type": "object", "required": ["documents", "kinds", "metadata", "date", "headings"], "description": "The filters as applied, with the older top-level documents and kinds merged in.",
        "properties": { "documents": { "type": "array", "items": { "type": "string" } }, "kinds": { "type": "array", "items": { "type": "string" } }, "metadata": { "type": ["object", "null"], "additionalProperties": true }, "date": { "type": ["object", "null"], "properties": { "from": { "type": ["string", "null"] }, "to": { "type": ["string", "null"] } } }, "headings": { "type": ["string", "null"] } }
      },
      "SearchRequest": {
        "type": "object", "required": ["query"],
        "properties": {
          "query": { "type": "string", "maxLength": 4000, "description": "Quoted phrases must appear on the page." },
          "k": { "type": "integer", "minimum": 1, "maximum": 50, "default": 10, "description": "Pages to return." },
          "mode": { "type": "string", "enum": ["hybrid", "keyword", "semantic"], "default": "hybrid" },
          "passages": { "type": "integer", "minimum": 1, "maximum": 10, "default": 2, "description": "Best passages per page." },
          "rerank": { "type": "boolean", "default": true, "description": "false keeps the fused order even when a reranker is configured." },
          "expand": { "oneOf": [{ "type": "boolean" }, { "type": "string", "enum": ["llm"] }], "default": true, "description": "true: deterministic rewrites (abbreviations, section forms, Arabic spelling variants); 'llm': model paraphrases too; false: the query as typed." },
          "domain": { "type": "string", "enum": ["medical", "legal", "general"], "description": "Steers the abbreviation lexicon; defaults from the collection's settings or its documents." },
          "kinds": { "type": "array", "items": { "$ref": "#/components/schemas/Kind" }, "description": "Shorthand for filters.kinds." },
          "documents": { "type": "array", "items": { "type": "string" }, "description": "Shorthand for filters.documents." },
          "filters": { "$ref": "#/components/schemas/Filters" }
        }
      },
      "Expansion": { "type": "object", "required": ["text", "kind", "weight"], "properties": { "text": { "type": "string" }, "kind": { "type": "string", "enum": ["abbreviation", "section", "variant", "llm"] }, "weight": { "type": "number" } } },
      "Passage": {
        "type": "object", "required": ["id", "pos", "text", "kinds", "heading", "offsets", "matched", "meta"],
        "properties": {
          "id": { "type": "integer", "description": "The chunk's id in the index." }, "pos": { "type": "integer", "description": "Position on the page, from 0." }, "text": { "type": "string" },
          "kinds": { "type": "array", "items": { "$ref": "#/components/schemas/Kind" } }, "heading": { "type": ["string", "null"] },
          "offsets": { "type": ["object", "null"], "required": ["start", "end"], "properties": { "start": { "type": "integer" }, "end": { "type": "integer" } }, "description": "Where the passage sits in the page text (GET .../pages/{n}); null when unknown." },
          "matched": { "type": "array", "items": { "type": "string" }, "description": "Index terms of the query (and its expansions) the passage holds." },
          "meta": { "type": ["object", "null"], "additionalProperties": true, "description": "What the chunker noted (a legal clause label, a clinical section, dates)." }
        }
      },
      "Result": {
        "type": "object", "required": ["rank", "citation", "score", "document", "page", "heading", "passages", "retrievers", "duplicates", "alsoIn"],
        "properties": {
          "rank": { "type": "integer" },
          "citation": { "type": "string", "pattern": "^AL-[0-9A-F]{8}$", "description": "A stable id for this page of this document, the same across searches." },
          "score": { "type": "number", "description": "The fused score; comparable within one response only." },
          "document": { "$ref": "#/components/schemas/DocumentRef" }, "page": { "$ref": "#/components/schemas/PageRef" },
          "heading": { "type": ["string", "null"], "description": "The page heading, or the first passage's." },
          "passages": { "type": "array", "items": { "$ref": "#/components/schemas/Passage" } },
          "retrievers": { "type": "array", "items": { "type": "string", "enum": ["keyword", "semantic", "fields"] }, "description": "Which lists ranked the page." },
          "duplicates": { "type": "integer", "description": "Near-duplicate pages folded into this one." },
          "alsoIn": { "type": "integer", "description": "Other documents carrying the same text." }
        }
      },
      "SearchResponse": {
        "type": "object", "required": ["query", "mode", "domain", "expand", "intent", "phrases", "expansions", "filters", "warnings", "results", "reranker", "timings", "considered"],
        "properties": {
          "query": { "type": "string" }, "mode": { "type": "string" }, "domain": { "type": "string", "enum": ["medical", "legal", "general"] }, "expand": { "oneOf": [{ "type": "boolean" }, { "type": "string" }] },
          "intent": { "type": "string", "enum": ["phrase", "list", "question", "entity", "keyword"] },
          "phrases": { "type": "array", "items": { "type": "string" }, "description": "Quoted phrases that had to match." },
          "expansions": { "type": "array", "items": { "$ref": "#/components/schemas/Expansion" } },
          "filters": { "$ref": "#/components/schemas/FiltersEcho" },
          "warnings": { "type": "array", "items": { "type": "string" }, "description": "For example an unknown kind ignored, or no page holding the quoted phrase (the unfiltered order is then returned)." },
          "results": { "type": "array", "items": { "$ref": "#/components/schemas/Result" } },
          "reranker": { "type": ["object", "null"], "properties": { "name": { "type": "string" }, "model": { "type": "string" } } },
          "timings": { "type": "object", "additionalProperties": { "type": "number" }, "description": "expandMs, keywordMs, embedMs, semanticMs, fieldsMs, phraseMs, rerankMs, totalMs as they apply." },
          "considered": { "type": "integer", "description": "Pages in the fused list before the cut." }
        }
      },
      "ExtractRequest": {
        "type": "object", "required": ["question"],
        "properties": {
          "question": { "type": "string" }, "k": { "type": "integer", "minimum": 1, "maximum": 20, "default": 8, "description": "Pages to search and read." },
          "mode": { "type": "string", "enum": ["hybrid", "keyword", "semantic"] }, "kinds": { "type": "array", "items": { "$ref": "#/components/schemas/Kind" } },
          "documents": { "type": "array", "items": { "type": "string" } }, "filters": { "$ref": "#/components/schemas/Filters" },
          "domain": { "type": "string", "enum": ["medical", "legal", "general"] },
          "all": { "type": "boolean", "default": false, "description": "Also return the pages the reader judged not relevant (with their notes and no excerpts)." }
        }
      },
      "Excerpt": { "type": "object", "required": ["text", "match", "offset"], "properties": { "text": { "type": "string", "description": "Copied from the page as printed." }, "match": { "type": "string", "enum": ["exact", "normalized_exact"], "description": "normalized_exact: equal once vowel marks and letter variants are set aside." }, "offset": { "type": ["object", "null"], "properties": { "start": { "type": "integer" }, "end": { "type": "integer" } }, "description": "Where the excerpt sits in the page text, when found as written." } } },
      "ExtractPage": {
        "type": "object", "required": ["citation", "relevant", "note", "excerpts", "rejected", "document", "page"],
        "properties": { "citation": { "type": "string" }, "relevant": { "type": "boolean" }, "note": { "type": "string", "description": "One sentence on what the page contributes." }, "excerpts": { "type": "array", "items": { "$ref": "#/components/schemas/Excerpt" } }, "rejected": { "type": "integer", "description": "Excerpts the reader offered that were not on the page." }, "document": { "$ref": "#/components/schemas/DocumentRef" }, "page": { "$ref": "#/components/schemas/PageRef" } }
      },
      "Usage": { "type": "object", "required": ["input_tokens", "output_tokens"], "properties": { "input_tokens": { "type": "integer" }, "output_tokens": { "type": "integer" } } },
      "ExtractResponse": {
        "type": "object", "required": ["question", "pages", "kept", "rejectedExcerpts", "usage", "model", "masked"],
        "properties": { "question": { "type": "string" }, "pages": { "type": "array", "items": { "$ref": "#/components/schemas/ExtractPage" }, "description": "Relevant pages only, unless all is true." }, "kept": { "type": "integer", "description": "Pages with at least one verified excerpt." }, "rejectedExcerpts": { "type": "integer" }, "usage": { "$ref": "#/components/schemas/Usage" }, "model": { "type": "string" }, "masked": { "type": "boolean", "description": "True when the reader saw masked text (mask_identifiers or redact_storage)." } }
      },
      "Message": { "type": "object", "required": ["role", "content"], "properties": { "role": { "type": "string", "enum": ["user", "assistant"] }, "content": { "type": "string" } } },
      "ResearchRequest": {
        "type": "object", "required": ["question"],
        "properties": {
          "question": { "type": "string" },
          "tier": { "type": "string", "enum": ["fast", "deep"], "default": "fast", "description": "fast: a small model researches for up to 3 steps (about 10 s). deep: a stronger model, up to 8 steps." },
          "history": { "type": "array", "items": { "$ref": "#/components/schemas/Message" }, "description": "Earlier turns; the last six are used." },
          "stream": { "type": "boolean", "default": false, "description": "true streams NDJSON events instead of one JSON result." },
          "documents": { "type": "array", "items": { "type": "string" }, "description": "Bounds every search to these document ids." },
          "kinds": { "type": "array", "items": { "$ref": "#/components/schemas/Kind" }, "description": "Folded into filters.kinds." },
          "filters": { "$ref": "#/components/schemas/Filters" },
          "domain": { "type": "string", "enum": ["medical", "legal", "classical", "general"], "description": "Steers the research guidance and the search lexicon; defaults from the collection's settings, else from its documents." }
        }
      },
      "SourceSummary": {
        "type": "object", "required": ["n", "citation", "document", "page", "heading", "kinds", "meta"],
        "properties": { "n": { "type": "integer", "description": "The number the answer cites as [n]." }, "citation": { "type": "string" }, "document": { "$ref": "#/components/schemas/DocumentRef" }, "page": { "$ref": "#/components/schemas/PageRef" }, "heading": { "type": ["string", "null"] }, "kinds": { "type": "array", "items": { "$ref": "#/components/schemas/Kind" } }, "meta": { "type": ["object", "null"], "additionalProperties": true } }
      },
      "Source": {
        "allOf": [{ "$ref": "#/components/schemas/SourceSummary" }, { "type": "object", "required": ["cited", "passages"], "properties": { "cited": { "type": "boolean", "description": "Whether the answer cites [n]." }, "passages": { "type": "array", "items": { "type": "string" }, "description": "The passages the agent saw, in page order." } } }]
      },
      "Quote": { "type": "object", "required": ["quote", "source", "status"], "properties": { "quote": { "type": "string" }, "source": { "type": ["integer", "null"], "description": "The [n] cited right after the quotation; null when none." }, "status": { "type": "string", "enum": ["exact", "normalized_exact", "paraphrase", "translation", "unverified", "uncited"], "description": "exact and normalized_exact count as verified." } } },
      "Claim": {
        "type": "object", "required": ["text", "sources", "status", "evidence", "start", "end"],
        "properties": { "text": { "type": "string", "description": "The sentence without its citation markers." }, "sources": { "type": "array", "items": { "type": "integer" }, "description": "The [n] it cites." }, "status": { "type": "string", "enum": ["supported", "partial", "unsupported", "uncited", "unchecked"], "description": "supported: a cited source states it. partial: only part is stated, or the wording goes beyond the source. unsupported: the cited sources do not state it or contradict it. uncited: no [n]. unchecked: no judge ran." }, "evidence": { "type": ["string", "null"], "description": "A span verified to be in a cited source, or null." }, "start": { "type": "integer", "description": "Offsets of the sentence in the answer." }, "end": { "type": "integer" } }
      },
      "ClaimCounts": { "type": "object", "required": ["supported", "partial", "unsupported", "uncited", "unchecked", "total"], "properties": { "supported": { "type": "integer" }, "partial": { "type": "integer" }, "unsupported": { "type": "integer" }, "uncited": { "type": "integer" }, "unchecked": { "type": "integer" }, "total": { "type": "integer" } } },
      "Verification": {
        "type": "object", "required": ["quotes", "verified", "total", "claims", "counts", "summary"],
        "properties": { "quotes": { "type": "array", "items": { "$ref": "#/components/schemas/Quote" } }, "verified": { "type": "integer", "description": "Quotations found on the page they cite." }, "total": { "type": "integer", "description": "Quotations in the answer." }, "claims": { "type": "array", "items": { "$ref": "#/components/schemas/Claim" } }, "counts": { "$ref": "#/components/schemas/ClaimCounts" }, "summary": { "type": "string", "description": "One sentence, e.g. \"7 of 8 claims supported by their sources; 1 partially.\"" } }
      },
      "ResearchQuery": { "type": "object", "required": ["query", "mode", "results", "filters"], "properties": { "query": { "type": "string" }, "mode": { "type": "string" }, "results": { "type": "integer" }, "filters": { "$ref": "#/components/schemas/FiltersEcho" } } },
      "ResearchResult": {
        "type": "object", "required": ["answer", "sources", "verification", "queries", "note", "tier", "domain", "filters", "usage", "timings"],
        "properties": {
          "answer": { "type": "string", "description": "With [n] citations into sources." },
          "sources": { "type": "array", "items": { "$ref": "#/components/schemas/Source" } },
          "verification": { "$ref": "#/components/schemas/Verification" },
          "queries": { "type": "array", "items": { "$ref": "#/components/schemas/ResearchQuery" }, "description": "Every search the agent ran, in order." },
          "note": { "type": ["string", "null"], "description": "The agent's one-line note on what was found and what is missing." },
          "tier": { "type": "string", "enum": ["fast", "deep"] }, "domain": { "type": "string", "enum": ["medical", "legal", "classical", "general"] },
          "filters": { "$ref": "#/components/schemas/FiltersEcho" }, "usage": { "$ref": "#/components/schemas/Usage" },
          "timings": { "type": "object", "required": ["researchMs", "answerMs", "verifyMs", "totalMs", "steps"], "properties": { "researchMs": { "type": "integer" }, "answerMs": { "type": "integer" }, "verifyMs": { "type": "integer" }, "totalMs": { "type": "integer" }, "steps": { "type": "integer" } } }
        }
      },
      "Event": {
        "description": "One line of the research stream. In order: status (searching), query and sources as searches run, status (deepening, step n) per step, status (answering), delta per text piece, answer, status (verifying), claims, done. On failure the last line is error and the stream ends.",
        "oneOf": [
          { "$ref": "#/components/schemas/StatusEvent" }, { "$ref": "#/components/schemas/QueryEvent" }, { "$ref": "#/components/schemas/SourcesEvent" }, { "$ref": "#/components/schemas/DeltaEvent" },
          { "$ref": "#/components/schemas/AnswerEvent" }, { "$ref": "#/components/schemas/ClaimsEvent" }, { "$ref": "#/components/schemas/DoneEvent" }, { "$ref": "#/components/schemas/ErrorEvent" }
        ],
        "discriminator": { "propertyName": "type", "mapping": { "status": "#/components/schemas/StatusEvent", "query": "#/components/schemas/QueryEvent", "sources": "#/components/schemas/SourcesEvent", "delta": "#/components/schemas/DeltaEvent", "answer": "#/components/schemas/AnswerEvent", "claims": "#/components/schemas/ClaimsEvent", "done": "#/components/schemas/DoneEvent", "error": "#/components/schemas/ErrorEvent" } }
      },
      "StatusEvent": { "type": "object", "required": ["type", "stage"], "properties": { "type": { "const": "status" }, "stage": { "type": "string", "enum": ["searching", "deepening", "answering", "verifying"] }, "step": { "type": "integer", "description": "The research step, from 1." } } },
      "QueryEvent": { "type": "object", "required": ["type", "query", "mode", "results"], "properties": { "type": { "const": "query" }, "query": { "type": "string" }, "mode": { "type": "string" }, "results": { "type": "integer" } } },
      "SourcesEvent": { "type": "object", "required": ["type", "sources"], "properties": { "type": { "const": "sources" }, "sources": { "type": "array", "items": { "$ref": "#/components/schemas/SourceSummary" }, "description": "Every source found so far." } } },
      "DeltaEvent": { "type": "object", "required": ["type", "text"], "properties": { "type": { "const": "delta" }, "text": { "type": "string", "description": "The next piece of the answer." } } },
      "AnswerEvent": { "type": "object", "required": ["type", "text"], "properties": { "type": { "const": "answer" }, "text": { "type": "string", "description": "The whole answer." } } },
      "ClaimsEvent": { "type": "object", "required": ["type", "counts", "summary"], "properties": { "type": { "const": "claims" }, "counts": { "$ref": "#/components/schemas/ClaimCounts" }, "summary": { "type": "string" } } },
      "DoneEvent": { "type": "object", "required": ["type", "result"], "properties": { "type": { "const": "done" }, "result": { "$ref": "#/components/schemas/ResearchResult" } } },
      "ErrorEvent": { "type": "object", "required": ["type", "code", "message"], "properties": { "type": { "const": "error" }, "code": { "type": "string" }, "message": { "type": "string" } } },
      "RelevantItem": {
        "description": "One relevant page: by citation id, by document (id or title) and page index, or by a snippet the page text contains (optionally scoped to a document).",
        "type": "object",
        "properties": { "citation": { "type": "string" }, "document": { "type": "string", "description": "A document id or its title." }, "page": { "type": "integer", "description": "Page index from 0." }, "text": { "type": "string", "description": "A snippet the page contains, as written." } }
      },
      "EvalQuestion": {
        "type": "object", "required": ["query", "relevant"], "additionalProperties": true,
        "properties": { "query": { "type": "string" }, "relevant": { "type": "array", "items": { "$ref": "#/components/schemas/RelevantItem" } }, "gains": { "type": "array", "items": { "type": "number" }, "description": "A gain per relevant item (default 1) for graded nDCG." }, "kinds": { "type": "array", "items": { "$ref": "#/components/schemas/Kind" } }, "documents": { "type": "array", "items": { "type": "string" } }, "filters": { "$ref": "#/components/schemas/Filters" }, "passages": { "type": "integer" } }
      },
      "EvaluateRequest": {
        "type": "object", "required": ["questions"],
        "properties": { "questions": { "type": "array", "minItems": 1, "maxItems": 1000, "items": { "$ref": "#/components/schemas/EvalQuestion" } }, "k": { "type": "integer", "minimum": 1, "maximum": 50, "default": 10 }, "variants": { "type": "array", "items": { "type": "string", "enum": ["keyword", "semantic", "hybrid", "hybrid+rerank"] }, "description": "Default: all four. An unknown name is a note." }, "expand": { "oneOf": [{ "type": "boolean" }, { "type": "string", "enum": ["llm"] }], "description": "Passed to every search." } }
      },
      "VariantMetrics": { "type": "object", "required": ["ndcg", "precision", "recall", "mrr", "map", "latency", "found"], "properties": { "ndcg": { "type": "number" }, "precision": { "type": "number" }, "recall": { "type": "number" }, "mrr": { "type": "number" }, "map": { "type": "number" }, "latency": { "type": "object", "properties": { "p50": { "type": "number" }, "p95": { "type": "number" } } }, "found": { "type": "integer", "description": "Questions with at least one relevant page in the top k." } } },
      "PerQuestion": { "type": "object", "required": ["query", "variant", "ndcg", "firstHit", "latencyMs", "missing", "ranked", "hits"], "properties": { "query": { "type": "string" }, "variant": { "type": "string" }, "ndcg": { "type": "number" }, "firstHit": { "type": ["integer", "null"] }, "latencyMs": { "type": "number" }, "missing": { "type": "array", "items": { "type": "string" } }, "ranked": { "type": "array", "items": { "type": "string" }, "description": "Citation ids in result order." }, "hits": { "type": "array", "items": { "type": "object", "properties": { "item": { "type": "string" }, "rank": { "type": "integer" }, "citation": { "type": "string" } } } } } },
      "EvaluateResponse": {
        "type": "object", "required": ["n", "k", "embedder", "reranker", "variants", "perQuestion", "notes"],
        "properties": { "n": { "type": "integer", "description": "Questions with at least one resolvable relevant item." }, "k": { "type": "integer" }, "embedder": { "type": ["string", "null"] }, "reranker": { "type": ["string", "null"] }, "variants": { "type": "object", "additionalProperties": { "$ref": "#/components/schemas/VariantMetrics" } }, "perQuestion": { "type": "array", "items": { "$ref": "#/components/schemas/PerQuestion" } }, "notes": { "type": "array", "items": { "type": "string" }, "description": "Gold-set problems and skipped variants, so a retriever is never blamed for a question it could not answer." } }
      },
      "SampleRequest": { "type": "object", "required": ["domain"], "properties": { "domain": { "type": "string", "enum": ["medical", "legal", "archive"] } } },
      "SampleResponse": { "type": "object", "required": ["documents", "pages", "chunks"], "properties": { "documents": { "type": "integer" }, "pages": { "type": "integer" }, "chunks": { "type": "integer" } } },
      "Deleted": { "type": "object", "required": ["deleted"], "properties": { "deleted": { "type": "string" } } }
    }
  }
}
