{"openapi":"3.1.0","info":{"title":"Conceptio: Open Knowledge API","version":"1.1"},"paths":{"/api/license/redeem":{"post":{"summary":"Redeem License","description":"Redeem a one-time license key, and bind it to the account when we know it.\n\nTwo writes, one act. `licenses/{key}.redeemed_by` is the one-time record\n(claimed inside an optimistic Firestore transaction — check and claim are\none atomic step, so a concurrent second redeem 409s instead of binding the\npaid key twice); `users/{uid}.licenseKey` is the bind `_get_tier` reads on the\nBearer path, so a redeemed license elevates every later request from that\naccount with no ``X-License-Key`` header. `Authorization: Bearer <idToken>`\nis therefore how a non-browser client (CLI, agent, script) redeems without\never opening the SPA.\n\nThree deliberate rules, each one a way the bind would otherwise not happen\nor could not be revoked:\n\n* **The verified uid outranks the body's ``principal``.** A token is proof;\n  a body field is a claim. Using the proof for the one-time record too keeps\n  redemption and bind naming the same account.\n* **An ``anon:`` redemption can be displaced by a verified caller.** Those\n  principals are User-Agent slices the client invents, so they prove nothing\n  and cannot own a license against an account — which is what a visitor who\n  redeems before signing in, then signs in, would otherwise be stuck with.\n  A real principal (a uid, an operator-chosen string) stays immutable.\n* **Fail-open on the bind.** The license is already redeemed by then; a\n  Firestore hiccup returns ``bound: false``, never a 500.","operationId":"redeem_license_api_license_redeem_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LicenseRedeemIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/openapi.json":{"get":{"summary":"Openapi Spec","description":"Machine-readable OpenAPI contract for the full API.\n\nFastAPI auto-generates the schema from every registered route (21 paths:\nsearch, sources, resolve, document, cite, report-issue, collections, deep,\n…). The default `/openapi.json` live route can be shadowed by a hosting\ncatch-all, so this exposes the same spec at a stable, agent-addressable\npath. ``app.openapi()`` is cached by FastAPI after first build.","operationId":"openapi_spec_api_openapi_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/health":{"get":{"summary":"Health","operationId":"health_api_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/graph/related":{"get":{"summary":"Graph Related","description":"Related documents for ``doc`` — graph-first (shared-topic reasons), with\nthe FTS subject-overlap path as fallback. Response:\n{\"source\": \"graph\"|\"fallback\", \"related\": [{id, title, source, source_label,\nurl, shared_topics?, topic_count?}]}","operationId":"graph_related_api_graph_related_get","parameters":[{"name":"doc","in":"query","required":true,"schema":{"type":"integer","title":"Doc"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"default":6,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/graph/explore":{"get":{"summary":"Graph Explore","description":"Ego-network payload (nodes + links) for the graph explorer; 503 when the\ngraph is unavailable, 404 when the doc isn't in the graph.","operationId":"graph_explore_api_graph_explore_get","parameters":[{"name":"doc","in":"query","required":true,"schema":{"type":"integer","title":"Doc"}},{"name":"neighbors","in":"query","required":false,"schema":{"type":"integer","maximum":30,"minimum":1,"default":8,"title":"Neighbors"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/graph/health":{"get":{"summary":"Graph Health","description":"Live graph status for the keepalive/monitor cron (never raises).\nBesides connected/nodes/relationships/meta it carries the breaker:\n``breaker`` (closed|open), ``cooldown_remaining`` seconds and\n``last_error`` ({type, message, age_seconds}) — the cause behind a blip,\nnot just its symptom.","operationId":"graph_health_api_graph_health_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/graph/walk":{"get":{"summary":"Graph Walk","description":"The typed compliance walk over the stored reference pair — what a\ndocument cites (``out``), what cites it (``in``), or ``both``, optionally\nfiltered by relation kind (``cites`` / ``updates``).\n\nThe pair IS the graph (the ROADMAP verdict): plain SQL over\n``books.\"references\"`` / ``books.referenced_by``, one indexed\n``source_id in (…)`` batch per hop, no edge table. Nodes carry\n``distance`` (hops from the seed); edges are CANONICAL (``from`` cites\n``to``) in every direction and carry the relation kinds the row claims.\nTraversed edges only (distances < hops); ``truncated`` names a walk a cap\nstopped early (``graph.WALK_NODE_CAP`` / ``WALK_EDGE_CAP``, measured\nbounds — the worst live two-hop closure is 856 nodes). Metadata only —\nnever spends search credits. 404 when the seed is not in the corpus.","operationId":"graph_walk_api_graph_walk_get","parameters":[{"name":"seed","in":"query","required":true,"schema":{"type":"string","title":"Seed"}},{"name":"direction","in":"query","required":false,"schema":{"type":"string","default":"out","title":"Direction"}},{"name":"hops","in":"query","required":false,"schema":{"type":"integer","maximum":2,"minimum":1,"default":2,"title":"Hops"}},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"title":"Kind"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/connectors/zotero/authorize":{"post":{"summary":"Authorize Zotero","description":"Verify and persist a Zotero credential without returning its secret.","operationId":"authorize_zotero_api_connectors_zotero_authorize_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoteroConfigureIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Authorize Zotero Api Connectors Zotero Authorize Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/connectors/zotero":{"get":{"summary":"Get Zotero Profile","operationId":"get_zotero_profile_api_connectors_zotero_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Zotero Profile Api Connectors Zotero Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Remove Zotero Profile","operationId":"remove_zotero_profile_api_connectors_zotero_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Remove Zotero Profile Api Connectors Zotero Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/connectors/zotero/send":{"post":{"summary":"Send Zotero","operationId":"send_zotero_api_connectors_zotero_send_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoteroSendIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Send Zotero Api Connectors Zotero Send Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/connectors/zotero/send-all":{"post":{"summary":"Send All Zotero","operationId":"send_all_zotero_api_connectors_zotero_send_all_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoteroSendAllIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Send All Zotero Api Connectors Zotero Send All Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/connectors/obsidian/authorize":{"post":{"summary":"Authorize Obsidian","description":"Authorize a client-side metadata export without spending a credit.","operationId":"authorize_obsidian_api_connectors_obsidian_authorize_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObsidianAuthorizeIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Authorize Obsidian Api Connectors Obsidian Authorize Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/connectors/obsidian/log":{"post":{"summary":"Log Obsidian","description":"Record one successful client-side Obsidian save, idempotently.","operationId":"log_obsidian_api_connectors_obsidian_log_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObsidianLogIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Log Obsidian Api Connectors Obsidian Log Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/me":{"get":{"summary":"Me","description":"Minimal identity/tier helper for the frontend and CLI.\n- Firebase token → includes email.\n- License key → email empty, reports license path.\n- API key → reports api_key path (hash verified) so the CLI can show\n  which credential the server actually honored.","operationId":"me_api_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Me Api Me Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/keys":{"post":{"summary":"Create Api Key","description":"Create a self-hosted API key (agent/machine credential) for the signed-in\nuser. Requires a verified Firebase ID token (human self-serve path) — any\nauthenticated account may mint one, including the free tier: a free key\nresolves to the ``public`` tier and is REFUSED on every programmatic\nsurface by the Dev-gate (2026-09-10 — free keys can no longer draw the\n20-credit browser pool; the key is the Dev-funnel entry point, and its\nfirst call shows the upgrade hint).\nReturns the plaintext key EXACTLY ONCE — it is stored only as a SHA-256\nhash, so the caller must persist it now (the response is the only copy).\nThat makes this body a secret: it is declared in the edge cache tooling's\n`MUST_NOT_BE_CACHED`, so no cache rule may ever cover this path.","operationId":"create_api_key_api_keys_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Api Key Api Keys Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"List Api Keys","description":"List the signed-in user's active API keys (masked prefix, tier, name,\ncreated, last seen). Never key material — the plaintext key is served\nexactly once, in the creation response above.","operationId":"list_api_keys_api_keys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Api Keys Api Keys Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/keys/{key_id}":{"delete":{"summary":"Revoke Api Key","description":"Revoke one of the signed-in user's API keys (soft-delete: status flip +\nrevoked_at). Scoped to the requesting email — cannot revoke others' keys.","operationId":"revoke_api_key_api_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"integer","title":"Key Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Revoke Api Key Api Keys  Key Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/account/delete":{"post":{"summary":"Delete Account","description":"GDPR erasure — the profile's Danger-zone button.\n\nPurges every record this account owns and deletes the Firebase user, in\nthat ORDER: identity dies first (a live credential over purged rows could\nmint a fresh trial the moment anything re-reads it), then our stores. A\nstore failure after identity death is residual DATA — privacy debt, logged\nand listed in the response — never a live credential over a wiped table.\n\nAn active subscription refuses (409): fiscal records are retained 10\nyears by Italian law, so the subscription must be cancelled first. An\nUNVERIFIABLE billing state refuses too (503, retryable): the guard will\nnot delete a fiscal record it could not check for a live subscription.\n\nNo ``email_verified`` check, on purpose: an unconfirmed account must\nalways be able to erase itself.","operationId":"delete_account_api_account_delete_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/stripe/create-checkout-session":{"post":{"summary":"Stripe Create Checkout Session","description":"Open a Stripe Checkout session for a paid plan.\n\n``plan`` selects the modality tier: ``pro`` (humans in the browser,\nEUR 9.99/mo) or ``dev`` (agents, MCP/API/CLI, EUR 19.99/mo). The chosen\ntier is stashed in session metadata so the webhook promotes exactly the\nplan the customer paid for — never the caller's current tier.","operationId":"stripe_create_checkout_session_api_stripe_create_checkout_session_post","parameters":[{"name":"plan","in":"query","required":false,"schema":{"type":"string","default":"pro","title":"Plan"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/stripe/portal":{"get":{"summary":"Stripe Portal","operationId":"stripe_portal_api_stripe_portal_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/stripe/subscription-status":{"get":{"summary":"Stripe Subscription Status","operationId":"stripe_subscription_status_api_stripe_subscription_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/stripe/webhook":{"post":{"summary":"Stripe Webhook","operationId":"stripe_webhook_api_stripe_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/sources":{"get":{"summary":"Get Sources","operationId":"get_sources_api_sources_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/source_list":{"get":{"summary":"Get Source List","description":"Minimal per-source metadata for source chips.\n\nC9c5 spec requires /api/sources to be lazily loaded by the Source Status panel.\nWe keep the existing chip behavior via this endpoint.","operationId":"get_source_list_api_source_list_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/categories":{"get":{"summary":"Get Categories","description":"Category doc counts, aggregated from per-source counts.","operationId":"get_categories_api_categories_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/languages":{"get":{"summary":"Get Languages","description":"Language options present in corpus (ISO 639-1 codes).","operationId":"get_languages_api_languages_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/suggest":{"get":{"summary":"Suggest","description":"Lightweight prefix suggestion. Returns source-level doc counts for query.\nUsed by frontend debounced input (300ms) for live feel without heavy FTS.","operationId":"suggest_api_suggest_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"title":"Q"}},{"name":"category","in":"query","required":false,"schema":{"type":"string","maxLength":64,"default":"","title":"Category"}},{"name":"language","in":"query","required":false,"schema":{"type":"string","maxLength":32,"default":"","title":"Language"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/search":{"get":{"summary":"Search","operationId":"search_api_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"title":"Q"}},{"name":"sources","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sources"}},{"name":"category","in":"query","required":false,"schema":{"type":"string","default":"","title":"Category"}},{"name":"language","in":"query","required":false,"schema":{"type":"string","default":"","title":"Language"}},{"name":"license","in":"query","required":false,"schema":{"type":"string","default":"","title":"License"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","default":"relevance","title":"Sort"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":0,"default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/search/jobs":{"post":{"summary":"Search Jobs","description":"Queue 1–50 searches for bounded background execution.\n\nAll validation occurs before the job is stored, so malformed requests do\nnot consume search credits. Creation consumes one request-rate slot; each\nfresh subquery consumes one normal search credit when the worker starts it.\nPolling is request-rate limited but never consumes a search credit.","operationId":"search_jobs_api_search_jobs_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchJobsIn"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchJobCreatedOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/search/jobs/{job_id}":{"get":{"summary":"Search Job Status","description":"Return an owner-scoped job snapshot; expired jobs return HTTP 410.","operationId":"search_job_status_api_search_jobs__job_id__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchJobStatusOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/search/batch":{"post":{"summary":"Search Batch","description":"Run 1–10 independent searches under one request-rate-limit decision.\n\nValidation happens before the first search is dispatched, so malformed or\noversized batches cannot consume search credits. Each valid subquery then\nuses the existing search path, preserving its trial/quota, mesh, proof, and\nfail-closed behavior. The batch envelope is deliberately thin: consumers\nreceive the exact per-query response shape they already know.","operationId":"search_batch_api_search_batch_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchSearchIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchSearchOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/resolve":{"get":{"summary":"Resolve","operationId":"resolve_api_resolve_get","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Identifier to resolve: RFC number, DOI, arXiv ID, PMID, NIST/FIPS/SP designation, or free text","title":"Id"},"description":"Identifier to resolve: RFC number, DOI, arXiv ID, PMID, NIST/FIPS/SP designation, or free text"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/hash/{sha256}":{"get":{"summary":"Resolve Hash","description":"Resolve a content hash (sha256 of the source bytes) to its document.\n\nHash resolution: proof bundles carry the sha256 `content_hash`,\nso a consumer holding a proof can resolve the hash back to the document\nrecord without guessing an id. Bounded and read-quota-safe: the lookup is\nan index read on `idx_books_sha256` (created by the ingestion pipeline's\nschema ensure), fanned out across the serving shards like resolve, first\nhit wins. Access-aware result shape — same fields as /api/resolve\nper-document results, including the proof bundle, never raw full text.","operationId":"resolve_hash_api_hash__sha256__get","parameters":[{"name":"sha256","in":"path","required":true,"schema":{"type":"string","title":"Sha256"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/document/{doc_id}/{slug}":{"get":{"summary":"Document Page","operationId":"document_page_document__doc_id___slug__get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/document/{doc_id}":{"get":{"summary":"Document Page Redirect","operationId":"document_page_redirect_document__doc_id__get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/models":{"get":{"summary":"Models Index","operationId":"models_index_models_get","responses":{"307":{"description":"Successful Response"}}}},"/models/{author}/{name}":{"get":{"summary":"Model Page","operationId":"model_page_models__author___name__get","parameters":[{"name":"author","in":"path","required":true,"schema":{"type":"string","title":"Author"}},{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/models":{"get":{"summary":"Models Api","operationId":"models_api_api_models_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/models/{author}/{name}":{"get":{"summary":"Model Api","operationId":"model_api_api_models__author___name__get","parameters":[{"name":"author","in":"path","required":true,"schema":{"type":"string","title":"Author"}},{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/data/models.jsonl":{"get":{"summary":"Models Dump","operationId":"models_dump_data_models_jsonl_get","responses":{"200":{"description":"Successful Response"}}}},"/data/models.manifest.json":{"get":{"summary":"Models Dump Manifest","operationId":"models_dump_manifest_data_models_manifest_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sitemap-models-{n}.xml":{"get":{"summary":"Sitemap Models Chunk","description":"One route, two methods — and that is the fix, not a style choice.\n\nTwo routes side by side on one path do not coexist the way they read:\n``@app.get`` registers ``methods={\"GET\"}`` (an APIRoute does not add\nHEAD), so the app-wide ``_grant_head_to_get_routes()`` handed HEAD to\nthe RENDERING half, and Starlette answers a HEAD from the first FULL\nmatch — which is the GET route, registered first. The headers-only\nhandler beside it was therefore unreachable, and a link-checker's HEAD\nrendered up to 50,000 URLs. Measured 2026-09-16; the cheap HEADs on\nthis route and on ``/data/models.jsonl`` are now declared with their GET.","operationId":"sitemap_models_chunk_sitemap_models__n__xml_get","parameters":[{"name":"n","in":"path","required":true,"schema":{"type":"integer","title":"N"}}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sitemap.xml":{"get":{"summary":"Sitemap Index Route","operationId":"sitemap_index_route_sitemap_xml_get","responses":{"200":{"description":"Successful Response"}}}},"/sitemap-docs-{n}.xml":{"get":{"summary":"Sitemap Docs Chunk","operationId":"sitemap_docs_chunk_sitemap_docs__n__xml_get","parameters":[{"name":"n","in":"path","required":true,"schema":{"type":"integer","title":"N"}}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sitemap-static.xml":{"get":{"summary":"Sitemap Static Route","description":"Static indexable pages — the landing page (the robots.txt-sanctioned\nindexable public entry), ``/docs`` (the canonical docs page: one\nserver-rendered HTML document carrying every section inline), and the\ngate-open marketing views ``/about`` ``/pricing`` ``/sources`` ``/legal``\n``/signin``: PUBLIC_VIEWS the flip made ``index, follow``, each carrying\nits Phase 4 JSON-LD in the served head and hydrating to content. The\nNOINDEX app shells (``/search`` ``/deep`` ``/saved`` ``/recent``\n``/profile``) are the views that stay out — they are noindex AND empty.\n``/docs.md`` is the same content as raw markdown for LLM ingest —\ncrawlable and advertised by llms.txt, but served noindex and so absent\nhere (see ``SITEMAP_STATIC_URLS``).\n/data/corpus.jsonl was a member 2026-09-08..2026-10-12 and is out:\nit 302s to its R2 object by design, and a sitemap lists canonicals —\nthe same rule that evicted /models (2026-09-22). The bulk dumps ride\nllms.txt, the agent channel (see ``SITEMAP_STATIC_URLS``).","operationId":"sitemap_static_route_sitemap_static_xml_get","responses":{"200":{"description":"Successful Response"}}}},"/data/corpus.jsonl":{"get":{"summary":"Corpus Dump Route","description":"Bulk corpus dump (white-card build, 2026-09-08): nightly metadata JSONL\nfor crawlers — one fetch instead of a 50k-page crawl.\n\nThe file lives on R2 (GB-scale — never in the container image); this route\n302s to it. Dormant until the first dump publishes: without\nCONCEPTIO_BULK_DUMP_URL the route 404s honestly (the static soft-404 guard\ncovers it either way). Out of sitemap-static.xml since 2026-10-12\n(SITEMAP_STATIC_URLS): a sitemap lists canonicals, and this route is a\nredirect to a machine bulk read — llms.txt is that read's channel.","operationId":"corpus_dump_route_data_corpus_jsonl_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sitemap-sources.xml":{"get":{"summary":"Sitemap Sources Route","operationId":"sitemap_sources_route_sitemap_sources_xml_get","responses":{"200":{"description":"Successful Response"}}}},"/docs":{"get":{"summary":"Docs Route","operationId":"docs_route_docs_get","responses":{"200":{"description":"Successful Response"}}}},"/blog":{"get":{"summary":"Blog Index Route","operationId":"blog_index_route_blog_get","responses":{"200":{"description":"Successful Response"}}}},"/blog/{slug}":{"get":{"summary":"Blog Post Route","operationId":"blog_post_route_blog__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/blog/claudebot-ate-my-website/crawlers.csv":{"get":{"summary":"Crawlers Csv Route","operationId":"crawlers_csv_route_blog_claudebot_ate_my_website_crawlers_csv_get","responses":{"200":{"description":"Successful Response"}}}},"/source/{source_id}":{"get":{"summary":"Source Page","description":"Source catalog page — page 1 plus keyset pagination, BOTH ways.\n\n`before` + `before_id` are the (added_at, id) cursor of the last doc on the\nPREVIOUS page: `where source = ? and (added_at, id) < (?, ?)` — O(page)\nreads per page, never an offset walk, and the page URLs stay permanent as\nnew docs land (they only shift the newest boundary, never existing pages).\nDeep pages are discovered via rel=next/prev head links — NOT enumerated in\nthe sitemap (the plan's pagination contract, CONCEPTIO_SEO Phase 2).\n\nThe walk itself is capped at SOURCE_MAX_PAGE — 10 pages, the 200 most\nrecent docs per source — and the depth rides in the cursor as `page=`,\nchecked BEFORE the corpus opens: a depth-less cursor, a depth without a\ncursor, anything past the cap, or a `before=` walk claiming to be the head\nall answer 404 — the same absence a cursor past the end gets, describing\nnothing about the ceiling it hit. rel=next stops at the cap (the renderer\nshares SOURCE_MAX_PAGE, so no served page links past it). Why the ceiling\nis server-side: the unbounded chain measured ~16% of all traffic\n(access.log, 24 h, 198,635 requests), every cursor URL a unique edge MISS\ninto origin corpus reads, buying nothing under `noindex`.\n\n`after` + `after_id` are the DOWN-walk's mirror: the FIRST doc on the page\nthe reader is looking at, so the page served ENDS just above it. That is a\ndistinct query — strictly NEWER than the boundary, walked in the INVERSE\nordering so the slice keeps the NEAREST rows rather than a far-away tail —\nand it is what the `rel=prev` link must point at. (Before 2026-09-13 the\nprev link reused `?before=docs[0]`, which asks for rows OLDER than this\npage's newest doc: it returned the page the reader was already on minus its\nnewest row, 19 of 20 rows repeated, and page 1 was unreachable going back.)","operationId":"source_page_source__source_id__get","parameters":[{"name":"source_id","in":"path","required":true,"schema":{"type":"string","title":"Source Id"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Before"}},{"name":"before_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Before Id"}},{"name":"after","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"After"}},{"name":"after_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"After Id"}},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page"}}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/topic/{topic}":{"get":{"summary":"Topic Page","description":"Render one topic hub page: graph-authoritative docs, FTS fallback.\n\nThe graph is the authoritative topic index; when it is unavailable the\nTurso FTS pool renders the page instead. When the graph is UP but has\nnothing for the name, the page 404s (never the fallback — a cleaned\nstopword must not resurrect as a phantom hub). One exception, added\n2026-10-16 after a ~55 s blip served 404s for 24 real hubs to crawlers:\na name THIS PROCESS served from the graph within the last 15 minutes\nserves that recent answer stale, marked `X-Served-From: last-known-good`\nand edge-capped at 5 minutes, instead of 404ing. Authority stays with\nthe graph — a never-seen name still 404s, and a deletion converges\nwithin the TTL.","operationId":"topic_page_topic__topic__get","parameters":[{"name":"topic","in":"path","required":true,"schema":{"type":"string","title":"Topic"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sitemap-topics.xml":{"get":{"summary":"Sitemap Topics Route","description":"Topic hub URLs (largest graph topics, bounded). When the graph is\nunavailable the urlset is empty-but-valid — the index simply omits the\nchild rather than 500ing — unless this process served the layer within\nthe last 15 minutes, in which case that list is served stale (marked\n`X-Served-From: last-known-good`, edge-capped at 5 min): an empty\nurlset tells every crawler to drop the whole family, so a blip must\nnot erase what the graph itself just advertised.","operationId":"sitemap_topics_route_sitemap_topics_xml_get","responses":{"200":{"description":"Successful Response"}}}},"/api/document/{doc_id}":{"get":{"summary":"Get Document","operationId":"get_document_api_document__doc_id__get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/document/{doc_id}/sections":{"get":{"summary":"Document Sections","description":"Section structure over the document's extracted full text.","operationId":"document_sections_api_document__doc_id__sections_get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/document/{doc_id}/search":{"get":{"summary":"Document Search Within","description":"Search inside a single document's extracted full text (snippets + offsets).\n\nAdditive Phase 7 query-to-section extraction: alongside the flat\n`snippets` list, `sections` returns the WHOLE section (title + text) for\nevery section that contains a hit, bounded by `max_sections` and\n`max_chars`. Access-aware like everything else here: extraction derives\nfrom the served full text only, so metadata-only / license-restricted\nrows return empty `sections` and `full_text_available: false`.","operationId":"document_search_within_api_document__doc_id__search_get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}},{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":200,"title":"Q"}},{"name":"max_sections","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"default":5,"title":"Max Sections"}},{"name":"max_chars","in":"query","required":false,"schema":{"type":"integer","maximum":8000,"minimum":200,"default":2000,"title":"Max Chars"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/section/search":{"get":{"summary":"Section Search","description":"Search a phrase inside a section kind across documents with full text.\n\nFor every candidate doc (source-scoped, full_text != '', ordered most-\nrecent, capped at max_docs), split sections and keep hits only from the\nrequested kind. Returns per-doc hits with section attribution, plus a\ncoverage tally so agents know how many docs actually carry the section.","operationId":"section_search_api_section_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":200,"title":"Q"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string","description":"Section kind to search within (see /api/section/kinds)","default":"risk_factors","title":"Kind"},"description":"Section kind to search within (see /api/section/kinds)"},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Restrict to one source (e.g. sec_edgar)","title":"Source"},"description":"Restrict to one source (e.g. sec_edgar)"},{"name":"max_docs","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":20,"title":"Max Docs"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/section/kinds":{"get":{"summary":"Section Kinds","description":"List supported section kinds with a short description of each.\n\nLets agents discover valid `kind` values for /api/section/search without\nguessing. The matchers themselves live in conceptio.text.SECTION_KINDS.","operationId":"section_kinds_api_section_kinds_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/document/{doc_id}/proof":{"get":{"summary":"Document Proof","description":"Machine-readable evidence package for one document.\n\nOptional ``q`` yields a passage-level proof: the matched snippet plus its\nsurrounding context, taken from the extracted full text when available.","operationId":"document_proof_api_document__doc_id__proof_get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Q"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/document/{doc_id}/proof.pdf":{"get":{"summary":"Document Proof Pdf","description":"The same evidence package, rendered as a downloadable PDF (Pro).\n\nONE route declares both methods on purpose: two routes side by side on one\npath do not coexist the way they read, because `_grant_head_to_get_routes()`\nand Starlette's first-full-match ordering left the cheap HEAD unreachable\nand a link-checker paid for the rendering half (measured 2026-09-16 on\n`/sitemap-models-{n}.xml`).\n\nGated on tier: `public` is refused before any database read, because the\nroadmap sells this artifact at Pro while the JSON proof stays open to a\nsigned-in caller.  A `pro` API KEY is already refused one layer down, in\n`_enforce_dev_gate` inside `_rate_limit` - browser-tier keys do not spend\non programmatic surfaces - so this gate is what stops the *signed-in*\npublic caller, and it costs nothing to refuse.  The refusal splits 401/403\nthe way `_enforce_search_trial` does for the JSON sibling: no credential is\nan identity problem (401), a credentialed free-tier caller is a tier\nproblem (403).  Telling a keyless client to upgrade teaches it the wrong\nnext move.","operationId":"document_proof_pdf_api_document__doc_id__proof_pdf_get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Q"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/document/{doc_id}/verify":{"post":{"summary":"Document Verify","description":"Verify a quoted span of text against a document's served full text.\n\nReturns every occurrence of ``quote`` as ``{start, end, section}`` in\ndocument order, and when ``spans`` were claimed, a per-span check that\nthe bytes at those offsets hold the quote. ``verified`` is true only for\na real occurrence (all supplied spans must match when given). Offset\nsemantics match /sections and in-document search exactly (same heading\nsplitter). Access-aware: a metadata-only / license-restricted row fails\nclosed with ``verified: false`` and ``reason: full_text_unavailable`` —\nverification never runs over text we do not serve. 404/410 follow the\ndocument tombstone contract; malformed or oversized claims are 422\nbefore any full-text work.","operationId":"document_verify_api_document__doc_id__verify_post","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/report-issue":{"post":{"summary":"Report Issue","description":"Submit a structured issue report for a document.\n\nValidates the document exists (snapshots title/source/url), enforces a\nknown issue_type, and rate-limits per IP. Returns the report id.","operationId":"report_issue_api_report_issue_post","parameters":[{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueReportIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/watch":{"post":{"summary":"Create Watch","operationId":"create_watch_api_watch_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WatchCreateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"List Watches","operationId":"list_watches_api_watch_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/watch/{watch_id}":{"get":{"summary":"Get Watch","operationId":"get_watch_api_watch__watch_id__get","parameters":[{"name":"watch_id","in":"path","required":true,"schema":{"type":"integer","title":"Watch Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Delete Watch","description":"Delete one of the caller's watches and its event history.\n\nOwnership is checked BEFORE either delete. ``watch_events`` carries no\nowner column, so an unscoped ``delete ... where watch_id=?`` removed a\n*guessable* other account's event history (an IDOR: the watch row was\nowner-scoped, the events were not). A watch that is not the caller's is a\n404 — the same answer ``get_watch``/``check_watch`` give — and neither\ntable is touched.","operationId":"delete_watch_api_watch__watch_id__delete","parameters":[{"name":"watch_id","in":"path","required":true,"schema":{"type":"integer","title":"Watch Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/watch/{watch_id}/check":{"post":{"summary":"Check Watch","description":"Recompute a watch's fingerprint and report whether anything changed.\n\nPull-driven (no daemon): an agent or cron calls this periodically. When\nclaimed, the stored fingerprint + doc_ids advance so a later check\ncompares against the NEW state. Returns the machine diff summary plus the\nprevious vs current fingerprint for the caller's verification.","operationId":"check_watch_api_watch__watch_id__check_post","parameters":[{"name":"watch_id","in":"path","required":true,"schema":{"type":"integer","title":"Watch Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/recent":{"get":{"summary":"Get Recent","operationId":"get_recent_api_recent_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":20,"title":"Limit"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"}},{"name":"sources","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sources"}},{"name":"category","in":"query","required":false,"schema":{"type":"string","default":"","title":"Category"}},{"name":"language","in":"query","required":false,"schema":{"type":"string","default":"","title":"Language"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cite/{doc_id}":{"get":{"summary":"Cite","operationId":"cite_api_cite__doc_id__get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(bibtex|apa|mla|chicago|ieee|harvard|ris|bluebook|oscola|iso690|ansiz39)$","default":"apa","title":"Format"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/collections":{"post":{"summary":"Create Collection","operationId":"create_collection_api_collections_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"List Collections","operationId":"list_collections_api_collections_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/collections/{collection_id}/items":{"post":{"summary":"Add To Collection","operationId":"add_to_collection_api_collections__collection_id__items_post","parameters":[{"name":"collection_id","in":"path","required":true,"schema":{"type":"integer","title":"Collection Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionItemAdd"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"Get Collection Items","operationId":"get_collection_items_api_collections__collection_id__items_get","parameters":[{"name":"collection_id","in":"path","required":true,"schema":{"type":"integer","title":"Collection Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-license-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-License-Key"}},{"name":"x-principal","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Principal"}},{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"ApiKeyCreateIn":{"properties":{"name":{"type":"string","title":"Name","default":""}},"type":"object","title":"ApiKeyCreateIn"},"BatchSearchIn":{"properties":{"queries":{"items":{"$ref":"#/components/schemas/BatchSearchItem"},"type":"array","maxItems":10,"minItems":1,"title":"Queries"}},"type":"object","required":["queries"],"title":"BatchSearchIn","description":"Synchronous batch search payload (1–10 independently searchable items)."},"BatchSearchItem":{"properties":{"q":{"type":"string","maxLength":220,"minLength":2,"title":"Q"},"sources":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sources"},"category":{"type":"string","title":"Category","default":""},"language":{"type":"string","title":"Language","default":""},"license":{"type":"string","title":"License","default":""},"sort":{"type":"string","title":"Sort","default":"relevance"},"limit":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Limit","default":20},"offset":{"type":"integer","maximum":1000.0,"minimum":0.0,"title":"Offset","default":0}},"type":"object","required":["q"],"title":"BatchSearchItem","description":"One query and the same bounded options accepted by ``/api/search``."},"BatchSearchOut":{"properties":{"count":{"type":"integer","title":"Count"},"tier":{"type":"string","title":"Tier"},"queries":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Queries"},"attribution":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Attribution"}},"type":"object","required":["count","tier","queries"],"title":"BatchSearchOut","description":"Thin batch envelope; each item retains the normal search response shape."},"CollectionCreate":{"properties":{"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"firebase_uid":{"type":"string","title":"Firebase Uid"},"email":{"type":"string","title":"Email"}},"type":"object","required":["name","firebase_uid","email"],"title":"CollectionCreate"},"CollectionItemAdd":{"properties":{"doc_id":{"type":"integer","title":"Doc Id"},"firebase_uid":{"type":"string","title":"Firebase Uid"}},"type":"object","required":["doc_id","firebase_uid"],"title":"CollectionItemAdd"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"IssueReportIn":{"properties":{"doc_id":{"type":"integer","title":"Doc Id"},"issue_type":{"type":"string","title":"Issue Type"},"note":{"type":"string","title":"Note","default":""}},"type":"object","required":["doc_id","issue_type"],"title":"IssueReportIn"},"LicenseRedeemIn":{"properties":{"key":{"type":"string","maxLength":64,"title":"Key","default":""},"principal":{"type":"string","maxLength":128,"title":"Principal","default":""}},"type":"object","title":"LicenseRedeemIn"},"ObsidianAuthorizeIn":{"properties":{"doc_id":{"type":"integer","title":"Doc Id"}},"type":"object","required":["doc_id"],"title":"ObsidianAuthorizeIn"},"ObsidianLogIn":{"properties":{"doc_id":{"type":"integer","title":"Doc Id"}},"type":"object","required":["doc_id"],"title":"ObsidianLogIn"},"SearchJobCreatedOut":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","enum":["queued","running","done","expired"],"title":"Status"},"created_at":{"type":"number","title":"Created At"},"expires_at":{"type":"number","title":"Expires At"},"poll_url":{"type":"string","title":"Poll Url"}},"type":"object","required":["id","status","created_at","expires_at","poll_url"],"title":"SearchJobCreatedOut","description":"Polling handle returned when an asynchronous search is accepted."},"SearchJobStatusOut":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","enum":["queued","running","done","expired"],"title":"Status"},"created_at":{"type":"number","title":"Created At"},"expires_at":{"type":"number","title":"Expires At"},"result":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Result"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"attribution":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Attribution"}},"type":"object","required":["id","status","created_at","expires_at"],"title":"SearchJobStatusOut","description":"Owner-scoped polling snapshot for an asynchronous search."},"SearchJobsIn":{"properties":{"queries":{"items":{"$ref":"#/components/schemas/BatchSearchItem"},"type":"array","maxItems":50,"minItems":1,"title":"Queries"}},"type":"object","required":["queries"],"title":"SearchJobsIn","description":"Asynchronous search payload (1–50 independently searchable items)."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VerifyIn":{"properties":{"quote":{"type":"string","maxLength":4096,"minLength":1,"title":"Quote"},"spans":{"anyOf":[{"items":{"$ref":"#/components/schemas/VerifySpanIn"},"type":"array","maxItems":20},{"type":"null"}],"title":"Spans"},"case_sensitive":{"type":"boolean","title":"Case Sensitive","default":true}},"type":"object","required":["quote"],"title":"VerifyIn","description":"Quote/span grounding verification payload.\n\n``quote`` is the claimed text (verbatim by default). ``spans`` are\noptional claimed offsets; when supplied, each must hold exactly the\nquote. ``case_sensitive=False`` opts into a weaker, case-insensitive\nmatch that is reported as such in the response."},"VerifySpanIn":{"properties":{"start":{"type":"integer","minimum":0.0,"title":"Start"},"end":{"type":"integer","minimum":0.0,"title":"End"}},"type":"object","required":["start","end"],"title":"VerifySpanIn","description":"A claimed byte span [start, end) over the document's served full text."},"WatchCreateIn":{"properties":{"target_type":{"type":"string","title":"Target Type","default":"identifier"},"target":{"type":"string","title":"Target"},"label":{"type":"string","title":"Label","default":""},"limit":{"type":"integer","title":"Limit","default":20},"webhook_url":{"type":"string","maxLength":2048,"title":"Webhook Url","default":""},"webhook_secret":{"type":"string","maxLength":256,"title":"Webhook Secret","default":""}},"type":"object","required":["target"],"title":"WatchCreateIn"},"ZoteroConfigureIn":{"properties":{"api_key":{"type":"string","title":"Api Key"},"user_id":{"type":"string","title":"User Id"}},"type":"object","required":["api_key","user_id"],"title":"ZoteroConfigureIn"},"ZoteroSendAllIn":{"properties":{"doc_ids":{"items":{"type":"integer"},"type":"array","title":"Doc Ids"}},"type":"object","required":["doc_ids"],"title":"ZoteroSendAllIn"},"ZoteroSendIn":{"properties":{"doc_id":{"type":"integer","title":"Doc Id"}},"type":"object","required":["doc_id"],"title":"ZoteroSendIn"}},"securitySchemes":{"X-Api-Key":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"Self-hosted API key minted on the profile page (agents, Dev tier)."},"Bearer":{"type":"http","scheme":"bearer","bearerFormat":"Firebase ID token","description":"Signed-in web session token (humans, Pro tier)."},"X-License-Key":{"type":"apiKey","in":"header","name":"X-License-Key","description":"One-time license key (institutional/enterprise flow)."},"X-Principal":{"type":"apiKey","in":"header","name":"X-Principal","description":"Principal identifier paired with X-License-Key."}}}}