{
  "openapi": "3.1.0",
  "info": {
    "title": "DocketRouter API",
    "version": "1.0.0",
    "summary": "Anti-hallucination gateway for legal AI.",
    "description": "DocketRouter fronts an OpenAI-compatible chat completions endpoint with legal grounding (Texas and federal rules, statutes and case law, plus your own case files), citation verification against a real reporter index, and provider/data-retention policy enforcement. Every route below is documented from the shipped route implementation; nothing here is speculative. All routes share one error envelope: `{ \"error\": { \"message\": string, \"type\": string, ... } }`, documented in the Error schema and per-response below. Body validation failures use that same envelope, with the first failing field in `message` and zod's flattened field map under `details` (the chat endpoint folds the field map into `message` instead).",
    "contact": {
      "name": "DocketRouter",
      "email": "hello@docketrouter.ai",
      "url": "https://docketrouter.ai"
    }
  },
  "servers": [
    {
      "url": "https://docketrouter.ai",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Chat",
      "description": "OpenAI-compatible chat completions with legal grounding and citation verification."
    },
    {
      "name": "Models",
      "description": "Public model catalog and per-model detail."
    },
    {
      "name": "Keys",
      "description": "API key lifecycle: mint, inspect, patch policy, rotate, revoke."
    },
    {
      "name": "Auth",
      "description": "Identity introspection for the credential in use."
    },
    {
      "name": "Files",
      "description": "Case files: your own documents, chunked and retrieved only for you."
    },
    {
      "name": "Matters",
      "description": "Persistent sessions: a message history plus attached case files, scoped to an owner. Available when matters are enabled on the host."
    },
    {
      "name": "RAG",
      "description": "Direct retrieval over the legal index and the verbatim rules corpus, with no model call."
    },
    {
      "name": "Citations",
      "description": "Library-only citation verification."
    },
    {
      "name": "MCP",
      "description": "The same retrieval library exposed to AI agents over the Model Context Protocol, for a client that calls tools instead of REST endpoints."
    },
    {
      "name": "Downloads",
      "description": "Bulk corpora DocketRouter offers for download."
    },
    {
      "name": "Usage",
      "description": "Request log, CSV export, per-generation reconciliation against OpenRouter, spend broken down by attributed app (`GET /apps`), and daily rollups (`GET /usage/daily`)."
    },
    {
      "name": "Billing",
      "description": "Prepaid API credits via Stripe Checkout (preset or custom amount), auto top-up, or Monero, and the balance they fund."
    },
    {
      "name": "Benchmarks",
      "description": "The Humanity's Last Lawsuit (HLL) item bank, leaderboard results, and model routing recommendations. Public read routes are available only while the benchmark is in public testing."
    },
    {
      "name": "Site",
      "description": "Internal endpoints that serve docketrouter.ai itself rather than being part of the customer-facing API surface."
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    },
    {
      "SessionAuth": []
    },
    {}
  ],
  "paths": {
    "/api/v1/chat/completions": {
      "post": {
        "tags": [
          "Chat"
        ],
        "summary": "Create a grounded, verified chat completion",
        "description": "OpenAI-compatible `POST /chat/completions`. When `docketrouter.juice` is true (the default), the request is grounded against the verbatim rules corpus, case law and, if `docketrouter.case_file` is true, your own attached case-file text, before the model sees it. When `docketrouter.verify` is true (the default) every citation the model produces is checked against the DocketRouter library and the offline CourtListener bulk citation table; on a non-streaming call a fabricated citation triggers one automatic rewrite pass. The response carries a `docketrouter` object describing exactly what was retrieved, verified and served, and the response headers name the exact upstream generation and provider.\n\nAuthentication: a `dr-` API key, a signed-in session (browser), or the site playground (unauthenticated, gated by Origin/Referer, 5 requests/minute per IP).\n\nRate limits (token bucket, 429 with a `retry-after` header on exhaustion): key 120/min (burst 40), signed-in session 30/min (burst 10), playground 5/min (burst 5).\n\nOnly models on the callable-model allowlist (see `callable_models` on `GET /models`) may be called; this also applies to every entry in `models[]`, since OpenRouter falls back to those. `local/*` model ids bypass the allowlist and are served by an in-house OpenAI-compatible endpoint instead of OpenRouter -- required when the key's `data_policy` is `private_pod`.\n\nApp attribution: the optional request headers `HTTP-Referer`, `X-Title` and `X-DocketRouter-App` (documented under parameters) tag the request with the calling app; the name and URL land on the usage row, come back as `docketrouter.app` in the response (and on the first chunk of a stream), and drive `GET /apps`, the `app` filter on `GET /usage` and `GET /usage/daily?group=app`.\n\nEvery non-admin response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (unix seconds). A key on a `limit_reset` schedule whose period cap is used up gets a 402 before any model call.",
        "operationId": "createChatCompletion",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatCompletionRequest"
              },
              "examples": {
                "grounded": {
                  "summary": "Grounded, verified, non-streaming",
                  "value": {
                    "model": "deepseek/deepseek-v4-flash",
                    "messages": [
                      {
                        "role": "user",
                        "content": "Under TRCP 21a, when is service by email complete?"
                      }
                    ],
                    "docketrouter": {
                      "jurisdiction": "tx"
                    }
                  }
                },
                "raw": {
                  "summary": "Raw pass-through, no grounding or verification",
                  "value": {
                    "model": "deepseek/deepseek-v4-flash",
                    "messages": [
                      {
                        "role": "user",
                        "content": "Summarize this paragraph."
                      }
                    ],
                    "docketrouter": {
                      "juice": false,
                      "verify": false
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Completion produced (non-streaming) or a `text/event-stream` of chunks (streaming, when `stream: true`). The rate-limit headers are omitted for the admin token, which has no bucket.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatCompletionResponse"
                }
              },
              "text/event-stream": {
                "schema": {
                  "$ref": "#/components/schemas/ChatCompletionChunk"
                },
                "description": "Only sent when the request body has `\"stream\": true`. A sequence of `data: {...}\\n\\n` frames identical in shape to OpenAI's chunk format, plus a `docketrouter` block on the first and last chunk, terminated by `data: [DONE]\\n\\n`. A verification warning, if any, is appended as an extra content chunk rather than only in metadata, because a streamed answer cannot be silently corrected."
              }
            },
            "headers": {
              "x-docketrouter-request-id": {
                "schema": {
                  "type": "string"
                },
                "description": "This request's id, also present in the JSON body as `docketrouter.request_id`. Quote it to support."
              },
              "x-docketrouter-generation-id": {
                "schema": {
                  "type": "string"
                },
                "description": "The upstream OpenRouter generation id that served this answer (empty on error or for local/private-pod models). Non-streaming only."
              },
              "x-docketrouter-provider": {
                "schema": {
                  "type": "string"
                },
                "description": "The upstream provider slug that actually served the request (e.g. \"deepinfra\"). Non-streaming only."
              },
              "access-control-expose-headers": {
                "schema": {
                  "type": "string"
                },
                "description": "\"x-docketrouter-request-id, x-docketrouter-generation-id, x-docketrouter-provider, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset\" (the generation and provider headers are non-streaming only), so browser clients can read them."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Sustained requests per minute for this caller class (API key 120, signed-in session 30, playground 5)."
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Whole tokens left in this caller's burst bucket after this request (bucket size: API key 40, session 10, playground 5)."
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time in seconds at which the bucket is full again (on a 2xx) or at which the next token arrives (on a 429)."
              }
            }
          },
          "400": {
            "description": "Invalid body, a disallowed model, or `max_tokens` above the 8192 ceiling.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The key's period spend cap is used up: `period_spent_usd` has reached `limit_usd` and `limit_reset` is `daily`, `weekly` or `monthly`. Type `payment_required_error`; the body also carries `limit_usd`, `period_spent_usd`, `limit_reset` and `resets_at` (ISO 8601, the UTC start of the next period). Keys with `limit_reset: none` never get this response from the gateway; their lifetime cap is enforced upstream instead. Checked before any model call, so nothing is billed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key's `data_policy` is `private_pod` and a non-`local/` model was requested; or the request's `docketrouter.provider`/`docketrouter.pin` tried to widen the key's provider policy (see `provider_prefs` in the response body for the policy that was in force).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`docketrouter.matter_id` was given and no such matter exists for this owner.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body above 512KB, more than 64 messages, or more than 200,000 characters across all messages.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A retrieved case-file document scored `hostile` for prompt injection and `docketrouter.injection_policy` is `\"block\"`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded for this key, session or IP. Carries `retry-after` plus the three `X-RateLimit-*` headers; `X-RateLimit-Remaining` is 0.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Sustained requests per minute for this caller class (API key 120, signed-in session 30, playground 5)."
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Whole tokens left in this caller's burst bucket after this request (bucket size: API key 40, session 10, playground 5)."
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time in seconds at which the bucket is full again (on a 2xx) or at which the next token arrives (on a 429)."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The upstream model provider failed, or the 90-second non-streaming timeout was hit. Detail is written to the usage row, not returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`docketrouter.provider`/allowlist narrowing left no permitted provider able to serve the request (OpenRouter 503/404 upstream), or `private_pod` was requested with no pod endpoint configured on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "HTTP-Referer",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The calling app's URL (http or https). Stored on the usage row as `app_url`, echoed back as `docketrouter.app.url`, and used as the app name (its hostname) when neither `X-Title` nor `X-DocketRouter-App` is sent. A bare `Referer` header is accepted as a fallback. Max 512 characters after trimming."
          },
          {
            "name": "X-Title",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The calling app's display name. Stored on the usage row as `app_name` and echoed back as `docketrouter.app.name`. Max 80 characters after trimming. Overridden by `X-DocketRouter-App` when both are sent."
          },
          {
            "name": "X-DocketRouter-App",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "DocketRouter's own app-name header. Same meaning as `X-Title` and takes precedence over it when both are sent."
          }
        ]
      }
    },
    "/api/v1/models": {
      "get": {
        "tags": [
          "Models"
        ],
        "summary": "List the model catalog",
        "description": "The full OpenRouter model catalog (text-output models only, `:free` and `openrouter/auto` excluded), annotated with whether DocketRouter will accept it in `chat/completions` (`callable`) and, when available, its Humanity's Last Lawsuit benchmark scores (`legal`). `callable_models` is the same allowlist enforced by `chat/completions`, published so a client does not have to discover it via a 400. Cacheable: supports `If-None-Match` / weak ETag, `cache-control: public, max-age=300, stale-while-revalidate=3600`.",
        "operationId": "listModels",
        "security": [],
        "parameters": [
          {
            "name": "if-none-match",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "A previously-returned ETag (weak comparison; a \"W/\" prefix added by an intermediary is accepted)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "etag": {
                "schema": {
                  "type": "string"
                }
              },
              "cache-control": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelsListResponse"
                }
              }
            }
          },
          "304": {
            "description": "Not modified (If-None-Match matched)."
          }
        }
      }
    },
    "/api/v1/models/{id}": {
      "get": {
        "tags": [
          "Models"
        ],
        "summary": "Get one model's catalog entry",
        "description": "Fetches a single catalog entry by id and attaches every Humanity's Last Lawsuit result recorded for it. The route is a catch-all (`[...id]`): the model id is rejoined from every path segment after `/models/`, so an id that itself contains a slash (e.g. `anthropic/claude-sonnet-4.5`) is passed through unencoded as `/api/v1/models/anthropic/claude-sonnet-4.5` -- do not percent-encode the slash.",
        "operationId": "getModel",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Full model id, exactly as listed in `GET /models` `data[].id`. May contain additional `/` characters."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelDetail"
                }
              }
            }
          },
          "404": {
            "description": "No such model in the catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/keys": {
      "get": {
        "tags": [
          "Keys"
        ],
        "summary": "List API keys",
        "description": "Admin: every key. Signed-in user: only their own keys. An API key credential is rejected here on purpose -- key management is deliberately not reachable with a key, so a leaked key cannot mint or enumerate keys. Each entry's spend counters are refreshed first: a key whose reset schedule has rolled over since it was last read comes back with its period counter already zeroed.",
        "operationId": "listKeys",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApiKeySummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not signed in and not the admin token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Keys"
        ],
        "summary": "Mint an API key",
        "description": "Creates a DocketRouter key with its own hard upstream spend cap. For a signed-in user, `limit_usd` is clamped to whatever spend headroom remains on the account (a $10 beta allowance plus paid balance, minus caps already granted on other keys); self-serve accounts are capped at 3 active keys during beta. A failed mint never leaves a live spendable key behind. `limit_reset` sets an optional rollover schedule -- the cap in `limit_usd` applies to the current period only, and `period_spent_usd` zeroes when the period rolls.",
        "operationId": "createKey",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateKeyRequest"
              },
              "example": {
                "name": "prod-web",
                "limit_usd": 10,
                "limit_reset": "weekly"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Key created. `key` is the secret and is never returned again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateKeyResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, including an unrecognized `limit_reset` value. Standard envelope: `message`, `type`, and `details` with zod's flattened field errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in and not the admin token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "No spend headroom left on the account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Max 3 active keys per account during beta. A quota, not a rate limit, despite the status code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The key could not be created (nothing spendable is left behind).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/keys/{id}": {
      "get": {
        "tags": [
          "Keys"
        ],
        "summary": "Get a key's settings and usage",
        "description": "Owner or admin. Includes the decoded policy, the spend-counter view (refreshed first: a due reset is rolled before the response is built), the upstream cap and usage, DocketRouter's own running totals, and the 50 most recent usage rows on this key.",
        "operationId": "getKey",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/KeyId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyDetail"
                }
              }
            }
          },
          "403": {
            "description": "The key exists and is not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No key with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Keys"
        ],
        "summary": "Update a key's settings",
        "description": "Owner or admin. A request can only ever narrow provider/data policy, never widen it (see `docketrouter.provider` rules on `chat/completions`); an owner may move between `shared` and `no_retention` but never in or out of `private_pod`, may move `limit_usd` up to the beta ceiling ($10), and cannot set `pod_id`. Owners may also set `limit_reset` freely (no admin gate); changing it starts a fresh period immediately -- `period_spent_usd` zeroes and `limit_reset_at` re-anchors to now. Only the fields you send are echoed back in the response, plus the full spend-counter view.",
        "operationId": "patchKey",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/KeyId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchKeyRequest"
              },
              "example": {
                "limit_reset": "monthly"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK. Echoes `ok`, `id`, the full decoded policy, the spend-counter view, and only the top-level fields that were set.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PatchKeyResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (standard `zod.flatten()` spread into the error object alongside `message`/`type`), including an unrecognized `limit_reset` value, or a provider-policy violation (`allowed_providers` is included alongside the error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key exists and is not yours, an owner tried to set/clear `private_pod`, or a provider-policy violation with status 403.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No key with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Keys"
        ],
        "summary": "Revoke a key",
        "description": "Owner or admin. Disables the key and cuts off its upstream spend immediately; requests using it return 401 from that moment. Terminal -- there is no un-revoke. Usage history is retained.",
        "operationId": "deleteKey",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/KeyId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    },
                    "disabled": {
                      "type": "boolean",
                      "description": "Always true: the response confirms the key is now disabled."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key exists and is not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No key with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/keys/{id}/rotate": {
      "post": {
        "tags": [
          "Keys"
        ],
        "summary": "Rotate a key's secret",
        "description": "Owner or admin. Issues a new secret (shown once) with a fresh upstream cap and cuts off the old secret's upstream spend. The key id, name, cap, settings and usage history are unchanged -- only the secret changes.",
        "operationId": "rotateKey",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/KeyId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RotateKeyResponse"
                }
              }
            }
          },
          "400": {
            "description": "The key is already disabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key exists and is not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No key with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/key": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Identify the current credential",
        "description": "Modeled on OpenRouter's `GET /auth/key`: describes whichever credential authenticated the request, at no cost (it never calls a model). Useful as a deploy health check.",
        "operationId": "getAuthKey",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK. Shape depends on the credential: an API key returns cap/spend/policy detail, a session returns `{label:\"session\",user_id}`, the admin token returns `{label:\"admin\",is_admin:true}`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthKeyResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid API key, or a playground (unauthenticated) caller, which is not accepted here.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/keys": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Exchange a PKCE authorization code for an API key",
        "description": "OpenRouter-parity OAuth PKCE exchange. A third-party app sends the signed-in user to `GET /api/oauth/authorize?callback_url=...&code_challenge=...` (S256 only); the user approves and the browser lands back on the app's `callback_url` with `?code=...`. The app then POSTs that code plus the original `code_verifier` here and receives a freshly minted, scoped DocketRouter key. The app never sees the user's credentials. Codes are single-use, expire in 10 minutes, and are burned on the first exchange attempt whether it succeeds or fails. Authentication is the code/verifier pair itself, so no bearer token or session cookie is sent; CORS is open so a browser-side app can finish the flow.",
        "operationId": "exchangeOAuthCodeForKey",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "code_verifier"
                ],
                "additionalProperties": false,
                "properties": {
                  "code": {
                    "type": "string",
                    "description": "The single-use code delivered to the app's callback_url."
                  },
                  "code_verifier": {
                    "type": "string",
                    "minLength": 43,
                    "maxLength": 128,
                    "description": "The RFC 7636 verifier whose SHA-256 (base64url) is the code_challenge sent to /api/oauth/authorize."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A newly minted key, returned once and never again.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "string",
                      "description": "The full secret, e.g. dr-\u2026 . Store it; it is not retrievable later."
                    },
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string",
                      "description": "oauth:<app name>"
                    },
                    "limit_usd": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Spend cap, at most $5 for an OAuth-minted key."
                    },
                    "jurisdiction": {
                      "type": "string"
                    },
                    "data_policy": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed body, or an unknown, expired, already-used code, or a verifier that does not match the challenge.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The approving account has no spend headroom left for another key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Per-IP rate limit on the exchange, or the account is already at its active-key ceiling.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The key could not be minted, or the approved policy could not be applied (in which case no key is issued).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "options": {
        "tags": [
          "Auth"
        ],
        "summary": "CORS preflight for the PKCE exchange",
        "operationId": "optionsOAuthKeyExchange",
        "security": [],
        "responses": {
          "204": {
            "description": "Preflight OK. Any origin may POST this endpoint with a content-type header."
          }
        }
      }
    },
    "/api/v1/files": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "List your case files",
        "operationId": "listFiles",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CaseFileSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No API key and not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Upload a case file",
        "description": "Text only (send extracted text, up to 2,000,000 characters) -- no PDF/DOCX parsing happens here. The text is scanned for prompt-injection patterns before storage; the scan result is returned but never blocks the upload. Chunked and stored per owner, and retrieved for your own `chat/completions` requests only when `docketrouter.case_file` is `true`. Never shared across owners.",
        "operationId": "createFile",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateFileRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateFileResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body. Standard envelope: `message` names the first failing field, `type` is `invalid_request_error`, and `details` carries zod's flattened `fieldErrors`/`formErrors`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key and not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/files/{id}": {
      "delete": {
        "tags": [
          "Files"
        ],
        "summary": "Delete a case file",
        "operationId": "deleteFile",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The file id returned by `POST /files`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK. Always `{ok:true}`, whether or not a file with that id existed for this owner.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No API key and not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/matters": {
      "get": {
        "tags": [
          "Matters"
        ],
        "summary": "List your matters",
        "description": "Matters must be enabled on the host; when they are not, every `/api/v1/matters*` route answers 503 before authentication even runs.",
        "operationId": "listMatters",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "archived",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1",
                "true",
                "yes"
              ]
            },
            "description": "Include archived matters in the listing."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MatterSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No API key and not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Matters are not enabled on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Matters"
        ],
        "summary": "Create a matter",
        "operationId": "createMatter",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatterSummary"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key and not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Matters are not enabled on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/matters/{id}": {
      "get": {
        "tags": [
          "Matters"
        ],
        "summary": "Get a matter, its messages and its attached files",
        "description": "A matter you do not own is a 404, never a 403 -- ids are not enumerable.",
        "operationId": "getMatter",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MatterId"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "A message id; page backward from before this id."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatterDetail"
                }
              }
            }
          },
          "400": {
            "description": "`before` was given and is not an integer message id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key and not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No matter with that id owned by you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Matters are not enabled on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Matters"
        ],
        "summary": "Rename or archive a matter",
        "operationId": "patchMatter",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MatterId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "archived": {
                    "type": "boolean"
                  }
                },
                "description": "At least one of title or archived is required."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatterSummary"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (neither field given, or a field fails validation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key and not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No matter with that id owned by you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Matters are not enabled on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Matters"
        ],
        "summary": "Delete a matter",
        "operationId": "deleteMatter",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MatterId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No API key and not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No matter with that id owned by you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Matters are not enabled on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/matters/{id}/files": {
      "post": {
        "tags": [
          "Matters"
        ],
        "summary": "Attach a case file to a matter",
        "description": "Once attached, `docketrouter.case_file` retrieval on requests carrying this `matter_id` narrows to only this matter's attached files.",
        "operationId": "attachMatterFile",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MatterId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "file_id"
                ],
                "properties": {
                  "file_id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 128
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "matter_id": {
                      "type": "string"
                    },
                    "file_id": {
                      "type": "string"
                    },
                    "files": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MatterFile"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key and not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No matter with that id owned by you, or `file_id` does not exist for this owner.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Matters are not enabled on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/matters/{id}/files/{file_id}": {
      "delete": {
        "tags": [
          "Matters"
        ],
        "summary": "Detach a case file from a matter",
        "operationId": "detachMatterFile",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MatterId"
          },
          {
            "name": "file_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "matter_id": {
                      "type": "string"
                    },
                    "file_id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No API key and not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No matter with that id owned by you, or the file is not attached to it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Matters are not enabled on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/rag/query": {
      "post": {
        "tags": [
          "RAG"
        ],
        "summary": "Search the fused legal index",
        "description": "Retrieval only, no model call: searches Texas opinions, Texas statutes, U.S. Supreme Court opinions and county local rules and returns the top hits. An index outage is a 503, never an empty 200.",
        "operationId": "ragQuery",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RagQueryRequest"
              },
              "example": {
                "q": "TRCP 21a service by email",
                "k": 5
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "x-docketrouter-request-id": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RagQueryResponse",
                  "properties": {
                    "library": {
                      "type": "object",
                      "description": "Last-update date of each source searched.",
                      "properties": {
                        "as_of": {
                          "type": "string"
                        },
                        "sources": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "as_of": {
                                "type": "string"
                              },
                              "through": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (`q` empty or over 600 characters, `k` outside 1-20, bad `sources`/`min_score`, or an unknown field -- the schema is `.strict()`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key, not signed in, and not from an allowed playground origin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body above 64KB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (30/min, burst 10 for a key/session; 10/min, burst 3 for the playground).",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The legal index is not configured on this host, or did not answer within 15 seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/rag/rules": {
      "post": {
        "tags": [
          "RAG"
        ],
        "summary": "Search the verbatim rules corpus",
        "description": "BM25 lexical search over the verbatim rules corpus (see `GET /downloads`, id `rules-corpus`). Returns the rule text itself so a caller can quote the rule rather than a model's paraphrase of it.",
        "operationId": "ragRules",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RagRulesRequest"
              },
              "example": {
                "q": "summary judgment standard",
                "k": 5
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RagRulesResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key, not signed in, and not from an allowed playground origin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body above 64KB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/citations/check": {
      "post": {
        "tags": [
          "Citations"
        ],
        "summary": "Check citations against the citation library",
        "description": "Library-only verification: a citation is `found` when it exists in the DocketRouter index or the offline CourtListener bulk citation table, otherwise `unverified`. This endpoint never asserts a citation is fabricated or nonexistent -- absence from the library is not evidence of that.",
        "operationId": "checkCitations",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CitationsCheckRequest"
              },
              "example": {
                "citations": [
                  "410 U.S. 113",
                  "999 U.S. 999"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CitationsCheckResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (`citations` empty, over 100 entries, or an entry over 200 characters -- the schema is `.strict()`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key, not signed in, and not from an allowed playground origin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body above 64KB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/citations/support": {
      "post": {
        "tags": [
          "Citations"
        ],
        "summary": "Check whether a citation supports a proposition",
        "description": "Resolves the citation through the same library as /citations/check (found or unverified, never a claim of nonexistence). When found, retrieves passages from the fused index restricted to that case, scores support mechanically by lexical overlap of the proposition's key terms, and, when a liaison model is configured, refines the verdict by asking it to select a single verbatim supporting quote. A candidate quote is accepted only if it is verified as an exact substring of the passage after whitespace normalisation; otherwise the verdict is downgraded to `unclear` and no quote is returned. Retrieval and the liaison call each have their own timeout; either timing out degrades the response to `verdict: \"unclear\"`, never a 500.",
        "operationId": "checkCitationSupport",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CitationsSupportRequest"
              },
              "example": {
                "citation": "701 S.W.2d 238",
                "proposition": "A trial court abuses its discretion when it acts without reference to any guiding rules or principles."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "x-docketrouter-request-id": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CitationsSupportResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (`citation` empty or over 200 characters, `proposition` under 10 or over 600 characters, an unrecognised `jurisdiction`, or an unknown field -- the schema is `.strict()`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key, not signed in, and not from an allowed playground origin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body above 64KB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (30/min, burst 10 for a key/session; 10/min, burst 3 for the playground).",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/downloads": {
      "get": {
        "tags": [
          "Downloads"
        ],
        "summary": "List downloadable corpora",
        "description": "Public listing with live sizes, licences and availability. Listing needs no credential; fetching an actual file (`GET /downloads/{id}`) does.",
        "operationId": "listDownloads",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "cache-control": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DownloadListing"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/downloads/{id}": {
      "get": {
        "tags": [
          "Downloads"
        ],
        "summary": "Download one corpus",
        "description": "Streams the file, honouring `Range` (for resumable downloads of the ~1GB citation table) subject to `If-Range` matching the current ETag, and `If-None-Match` for 304s. Rate-limited to 2 requests/minute per caller regardless of key type.",
        "operationId": "getDownload",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "An id from `GET /downloads` (currently `rules-corpus`, `citation-table`, `reporters`)."
          },
          {
            "name": "range",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "example": "bytes=0-1023"
            }
          },
          {
            "name": "if-range",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "if-none-match",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Full file.",
            "headers": {
              "content-type": {
                "schema": {
                  "type": "string"
                }
              },
              "content-disposition": {
                "schema": {
                  "type": "string"
                }
              },
              "etag": {
                "schema": {
                  "type": "string"
                }
              },
              "last-modified": {
                "schema": {
                  "type": "string"
                }
              },
              "accept-ranges": {
                "schema": {
                  "type": "string"
                }
              },
              "x-docketrouter-licence": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "Partial content for a satisfiable `Range` request.",
            "headers": {
              "content-range": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "304": {
            "description": "Not modified."
          },
          "401": {
            "description": "No API key and not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown download id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "416": {
            "description": "Range not satisfiable.",
            "headers": {
              "content-range": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "More than 2 requests/minute.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The file (or, for a derived entry, its source file) is not present on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/usage": {
      "get": {
        "tags": [
          "Usage"
        ],
        "summary": "List usage rows",
        "description": "An API key sees only its own rows; a signed-in session sees every key on the account and also gets `by_key` totals. Filter by `app` (exact attributed app name) or `key` (a specific key on the caller's account; a key caller may only pass its own id). `format=csv` returns a CSV attachment (capped at 5000 rows, with `app_name`/`app_url` columns after `key_name`) instead of JSON.",
        "operationId": "listUsage",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "model",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "app",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Exact match on the attributed app name (see X-DocketRouter-App)."
          },
          {
            "name": "key",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Restrict to one key on the caller's account. An API key caller may only pass its own id (403 otherwise); a signed-in session may pass any key on its account (404 if the id belongs to no key there)."
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ok",
                "error"
              ]
            }
          },
          {
            "name": "juiced",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1",
                "true",
                "false"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "csv"
              ]
            },
            "description": "Set to `csv` for a CSV export instead of JSON."
          }
        ],
        "responses": {
          "200": {
            "description": "OK (JSON, unless `format=csv`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageListResponse"
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid API key, or a playground caller (not accepted here).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "An API key caller passed `key` for a key other than its own.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "A signed-in session passed `key` for a key not on its account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/usage/{request_id}": {
      "get": {
        "tags": [
          "Usage"
        ],
        "summary": "Get one request in full",
        "description": "Metadata, the verification report, content (only if the key has `log_content` on), and, if not already synced, a live fetch of the exact OpenRouter generation stats for the upstream call behind it. Every field `UsageDetail` has, plus additive structured views of the same data (`app`, `price_usd`, `tokens`, `latency`, `grounding`, `key`, `timestamps`) so an integrator can read the shape it wants without re-deriving it. Identical body to `GET /api/v1/generation?id=`.",
        "operationId": "getUsageByRequestId",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "request_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerationDetail"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid API key, or a playground caller.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such request in this caller's scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/usage/sync": {
      "post": {
        "tags": [
          "Usage"
        ],
        "summary": "Reconcile recent requests against OpenRouter",
        "description": "For up to `limit` (default 50, max 200) unsynced rows in the caller's scope, fetches OpenRouter's per-generation truth (exact upstream cost, serving provider, native token counts, cache hits) and updates the local row. Safe to call repeatedly -- only rows without a `synced_at` are touched.",
        "operationId": "syncUsage",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "synced": {
                      "type": "integer"
                    },
                    "unresolved": {
                      "type": "integer"
                    },
                    "remaining": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid API key, or a playground caller.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/balance": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Get API credits balance and transaction history",
        "operationId": "getBalance",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceResponse"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/checkout": {
      "post": {
        "tags": [
          "Billing"
        ],
        "summary": "Start a Stripe Checkout session for API credits",
        "description": "Available only when card billing is enabled on the host. Signed-in users only. Send exactly one of amount_cents (a preset: 1000, 2500 or 10000) or amount_usd (a custom amount in whole dollars, 5 to 1000). Rate limited per user.",
        "operationId": "createCheckout",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount_cents": {
                    "type": "integer",
                    "enum": [
                      1000,
                      2500,
                      10000
                    ],
                    "description": "One of the preset buttons. Any other value is rejected."
                  },
                  "amount_usd": {
                    "type": "integer",
                    "minimum": 5,
                    "maximum": 1000,
                    "description": "Custom amount in whole dollars. Cents are not accepted."
                  }
                }
              },
              "examples": {
                "preset": {
                  "value": {
                    "amount_cents": 2500
                  }
                },
                "custom": {
                  "value": {
                    "amount_usd": 37
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri",
                      "description": "Stripe Checkout session URL to redirect the browser to."
                    },
                    "amount_cents": {
                      "type": "integer",
                      "description": "The amount the session will charge."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (standard envelope; details carries zod's field errors).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Billing is not enabled on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/webhook": {
      "post": {
        "tags": [
          "Billing"
        ],
        "summary": "Stripe webhook (server-to-server)",
        "description": "Not part of the customer-facing API: called by Stripe, not by an API client. Verifies stripe-signature against the raw body. Handles checkout.session.completed (mode payment: credits the balance, idempotent on the session id; mode setup: stores the saved card for auto top-up), payment_intent.succeeded and payment_intent.payment_failed for auto top-up intents (idempotent on the intent id; three consecutive failures switch auto top-up off), and charge.refunded (debits the balance, idempotent on the event id). Every other event type is a 200 no-op.",
        "operationId": "stripeWebhook",
        "security": [],
        "parameters": [
          {
            "name": "stripe-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "description": "Raw Stripe event payload; signature is verified against the raw body."
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event processed, or skipped if not one this endpoint acts on.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "received": {
                      "type": "boolean"
                    },
                    "ok": {
                      "type": "boolean"
                    },
                    "skipped": {
                      "type": "boolean"
                    },
                    "card_saved": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Signature verification failed. Plain-text body, not the JSON error envelope.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/monero": {
      "post": {
        "tags": [
          "Billing"
        ],
        "summary": "Create a Monero (BTCPay) checkout invoice",
        "description": "Available only when a BTCPay checkout is configured on the host. Signed-in users only.",
        "operationId": "createMoneroCheckout",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount_cents"
                ],
                "properties": {
                  "amount_cents": {
                    "type": "integer",
                    "minimum": 500,
                    "maximum": 100000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "invoice": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body. Standard envelope: `message` names the first failing field, `type` is `invalid_request_error`, and `details` carries zod's flattened `fieldErrors`/`formErrors`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The BTCPay server rejected the invoice request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Monero checkout is not enabled on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Billing"
        ],
        "summary": "BTCPay webhook (server-to-server)",
        "description": "Not part of the customer-facing API: configure BTCPay Server to POST or PUT here. Verifies the `btcpay-sig` HMAC-SHA256 header; on an `InvoiceSettled` event, credits the balance (idempotent on the BTCPay invoice id).",
        "operationId": "btcpayWebhook",
        "security": [],
        "parameters": [
          {
            "name": "btcpay-sig",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event processed (or skipped).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "received": {
                      "type": "boolean"
                    },
                    "skipped": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid `btcpay-sig`. Plain-text body.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/monero/invoice": {
      "post": {
        "tags": [
          "Billing"
        ],
        "summary": "Create a direct Monero invoice",
        "description": "Quotes USD to XMR at a public market rate at creation time, locks the rate for 30 minutes, and issues a fresh subaddress. Accepts either a signed-in session or an API key (unlike the other billing routes).",
        "operationId": "createMoneroInvoice",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount_cents"
                ],
                "properties": {
                  "amount_cents": {
                    "type": "integer",
                    "minimum": 500,
                    "maximum": 100000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MoneroInvoice"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body. Standard envelope: `message` names the first failing field, `type` is `invalid_request_error`, and `details` carries zod's flattened `fieldErrors`/`formErrors`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key and not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Monero payments are not enabled on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/monero/invoice/{id}": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Get a Monero invoice's status",
        "operationId": "getMoneroInvoice",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MoneroInvoiceStatus"
                }
              }
            }
          },
          "404": {
            "description": "No such invoice owned by this caller.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/hll/public": {
      "get": {
        "tags": [
          "Benchmarks"
        ],
        "summary": "Get the public split of the HLL item bank",
        "description": "Available only while the benchmark is in public testing (404 otherwise). Serves only the public split of the Humanity's Last Lawsuit item bank; private/embargoed items are never served here.",
        "operationId": "hllPublic",
        "security": [],
        "parameters": [
          {
            "name": "version",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "hll-0.1-tx",
              "pattern": "^[a-z0-9._-]+$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "license": {
                      "type": "string"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown or malformed version, or the benchmark is not in public testing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/hll/stats": {
      "get": {
        "tags": [
          "Benchmarks"
        ],
        "summary": "Get HLL item-bank statistics",
        "description": "Available only while the benchmark is in public testing.",
        "operationId": "hllStats",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "integer"
                    },
                    "invalid": {
                      "type": "integer"
                    },
                    "opinions": {
                      "type": "integer"
                    },
                    "categories": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "integer"
                      }
                    },
                    "difficulty": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "integer"
                      }
                    },
                    "courts": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "integer"
                      }
                    },
                    "nuclear": {
                      "type": "integer"
                    },
                    "traps": {
                      "type": "integer"
                    },
                    "splits": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "object",
                        "properties": {
                          "public": {
                            "type": "integer"
                          },
                          "private": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "generated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/hll/submissions": {
      "get": {
        "tags": [
          "Benchmarks"
        ],
        "summary": "List your HLL submissions",
        "description": "Available only while the benchmark is in public testing. Signed-in users only.",
        "operationId": "listHllSubmissions",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/HllSubmission"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Benchmarks"
        ],
        "summary": "Queue an official HLL run",
        "description": "Runs an outside model against the private split of the item bank on DocketRouter's own infrastructure. One official run per model version per 30 days per account.",
        "operationId": "createHllSubmission",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateHllSubmissionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "queued"
                      ]
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body. Standard envelope: `message` names the first failing field, `type` is `invalid_request_error`, and `details` carries zod's flattened `fieldErrors`/`formErrors`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "One official run per model version per 30 days.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/hll/submissions/{id}": {
      "get": {
        "tags": [
          "Benchmarks"
        ],
        "summary": "Get one HLL submission",
        "description": "The score is null while the submission is embargoed and the caller is not its owner or an admin.",
        "operationId": "getHllSubmission",
        "security": [
          {
            "SessionAuth": []
          },
          {}
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HllSubmission"
                }
              }
            }
          },
          "404": {
            "description": "No such submission.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/results": {
      "get": {
        "tags": [
          "Benchmarks"
        ],
        "summary": "List raw benchmark result rows",
        "description": "Available only while the benchmark is in public testing.",
        "operationId": "listResults",
        "security": [],
        "parameters": [
          {
            "name": "model",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "task",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RunRecord"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/route": {
      "post": {
        "tags": [
          "Benchmarks"
        ],
        "summary": "Recommend a model for a task under constraints",
        "description": "Ranks callable models by benchmark score for `task` (or the overall mean when `task` is `\"overall\"`), filtered by optional price/latency/score constraints, and returns only models `chat/completions` will actually accept.",
        "operationId": "routeModel",
        "security": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RouteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (standard envelope with `details` carrying zod's flattened field errors), or an unknown `task`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No callable model satisfies the given constraints.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/hit": {
      "post": {
        "tags": [
          "Site"
        ],
        "summary": "Record a page view (docketrouter.ai analytics)",
        "description": "Not part of the customer-facing API surface: first-party, cookieless page-view analytics for the docketrouter.ai marketing/product site itself. Public and unauthenticated, rate-limited to 60/min (burst 30) per IP. Honours Do Not Track (client-reported `dnt` field, the `DNT: 1` header, or `Sec-GPC: 1`) by dropping referrer and IP while still counting the view.",
        "operationId": "recordPageview",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "path"
                ],
                "properties": {
                  "path": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2048
                  },
                  "referrer": {
                    "type": "string",
                    "maxLength": 2048,
                    "default": ""
                  },
                  "w": {
                    "type": "number"
                  },
                  "dnt": {
                    "type": "boolean",
                    "default": false,
                    "description": "navigator.doNotTrack === \"1\", read client-side."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Recorded. No content."
          },
          "400": {
            "description": "Invalid body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body above 2KB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More than 60/min from this IP. Note: unlike every other rate-limited route in this API, no `retry-after` header is sent here -- the wait is only in the JSON body's `retry_after` field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/apps": {
      "get": {
        "tags": [
          "Usage"
        ],
        "summary": "Spend broken down by attributed app",
        "description": "Groups the caller's usage by the app that made the request, as attributed on `POST /chat/completions` (see the `notes` field of this fragment). A key sees only its own rows; a signed-in user sees every key on the account; the admin token sees everything and may narrow to one account with `owner`. Requests with no app attribution are never mixed into `data` -- they are folded into the single `unattributed` bucket instead.",
        "operationId": "listApps",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "ISO 8601 timestamp; only usage at or after this time is counted.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "ISO 8601 timestamp; only usage before this time is counted.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "description": "Admin only. Narrows the breakdown to one account's keys instead of the whole platform.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppsListResponse"
                }
              }
            }
          },
          "401": {
            "description": "No API key and not signed in, or the request came from the unauthenticated playground.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/autotopup": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Read auto top-up settings",
        "operationId": "getAutoTopup",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutoTopupSettings"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Billing"
        ],
        "summary": "Update auto top-up settings",
        "description": "All amounts are whole dollars. Setting enabled=true with no card on file does not enable it: the settings are saved with enabled=false and the response carries setup_url, a Stripe Checkout session in setup mode that saves a card (no charge); the webhook enables auto top-up once the card is saved. update_card=true returns a setup_url without changing enabled. Auto top-up switches itself off after 3 consecutive failed charges (see disabled_reason); turning it back on resets the count.",
        "operationId": "updateAutoTopup",
        "security": [
          {
            "SessionAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "threshold_usd": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 500,
                    "description": "Charge when available API credits fall below this. Default 5."
                  },
                  "amount_usd": {
                    "type": "integer",
                    "minimum": 10,
                    "maximum": 500,
                    "description": "How much each auto top-up adds. Default 25. Never more than this is charged per top-up."
                  },
                  "update_card": {
                    "type": "boolean",
                    "description": "Request a fresh card-setup session without changing enabled."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The settings after the update. setup_url is present only when a card must be saved first (or update_card was sent).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/AutoTopupSettings"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "setup_url": {
                          "type": "string",
                          "format": "uri"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Billing is not enabled on this host.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/credits": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Get API credits balance",
        "description": "The balance shown to a customer as \"API credits\": lifetime purchases, refunds, usage, and anything pending, plus whether real billing is turned on. Resolves the owner from the credential: an API key uses its attached account (or reports a null owner if the key was minted with none), a session uses the signed-in user, and the admin token must supply `owner`.",
        "operationId": "getCredits",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "description": "Owner id to report on. Required (and only accepted) when authenticating with the admin token.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Admin token used without an `owner` query parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid API key, or a playground (unauthenticated) caller, which is not accepted here.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/generation": {
      "get": {
        "tags": [
          "Usage"
        ],
        "summary": "Get one request in full (OpenRouter-compatible alias)",
        "description": "Identical to `GET /api/v1/usage/{request_id}`, with the id passed as a query parameter instead of a path segment, matching OpenRouter's `GET /generation?id=` shape for drop-in compatibility.",
        "operationId": "getGeneration",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The request id (same value as `request_id` on a usage row)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerationDetail"
                }
              }
            }
          },
          "400": {
            "description": "`id` is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid API key, or a playground caller.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such request in this caller's scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/usage/daily": {
      "get": {
        "tags": [
          "Usage"
        ],
        "summary": "Per-day usage rollups",
        "description": "Buckets the caller's usage by UTC calendar day, optionally split further by model or app. Defaults to the trailing 30 days (`from` = 30 days ago at 00:00Z, `to` = now). `format=csv` returns a CSV attachment instead of JSON.",
        "operationId": "getUsageDaily",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Default: 30 days ago at 00:00Z."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Default: now."
          },
          {
            "name": "model",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "app",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Exact match on the attributed app name."
          },
          {
            "name": "key",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Restrict to one key on the caller's account. An API key caller may only pass its own id (403 otherwise); a signed-in session may pass any key on its account (404 if the id belongs to no key there)."
          },
          {
            "name": "group",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "model",
                "app"
              ]
            },
            "description": "Split each day's bucket further by model or by attributed app."
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "csv"
              ]
            },
            "description": "Set to `csv` for a CSV export instead of JSON."
          }
        ],
        "responses": {
          "200": {
            "description": "OK (JSON, unless `format=csv`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageDailyResponse"
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "`group` was set to something other than `model` or `app`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid API key, or a playground caller.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "An API key caller passed `key` for a key other than its own.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "A signed-in session passed `key` for a key not on its account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/mcp": {
      "get": {
        "tags": [
          "MCP"
        ],
        "summary": "Describe the MCP server",
        "description": "A small JSON description of the server for a human or a crawler: name, version, the tool list, auth instructions, and the current per-query price. No auth required.",
        "operationId": "mcpDescribe",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpDescription"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "MCP"
        ],
        "summary": "MCP Streamable HTTP transport (JSON-RPC 2.0)",
        "description": "The Model Context Protocol server: `initialize`, `notifications/initialized`, `ping`, `tools/list`, and `tools/call` for `search_texas_law`, `search_rules`, `check_citations` and `check_support`. Each tool wraps the matching REST endpoint's logic exactly, and a `tools/call` is rate-limited and metered in the same bucket as that endpoint, so an MCP call counts as a call to it. SSE is not required; every response here is a single value returned promptly as `application/json`. The body may be a single JSON-RPC object or a non-empty array of them (a batch); the response shape (single object or array) matches.",
        "operationId": "mcpCall",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "SessionAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/JsonRpcRequest"
                  },
                  {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/JsonRpcRequest"
                    },
                    "minItems": 1
                  }
                ]
              },
              "example": {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "tools/call",
                "params": {
                  "name": "check_citations",
                  "arguments": {
                    "citations": [
                      "488 U.S. 222"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A JSON-RPC response (or, for a batch request, an array of them), one per request in the body. A pure notification body (no `id` on any item) gets no content; see 202.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/JsonRpcResponse"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JsonRpcResponse"
                      }
                    }
                  ]
                }
              }
            }
          },
          "202": {
            "description": "The body contained only notifications (no item carried an `id`); there is nothing to respond with, per JSON-RPC 2.0."
          },
          "401": {
            "description": "No API key, not signed in. For a single (non-batch) request this is the HTTP status; the body still carries a JSON-RPC error object naming the failing request's `id`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded on the scope the called tool shares with its REST endpoint (30/min, burst 10) or, for `initialize`/`tools/list`/`ping`, a lighter shared MCP scope (60/min, burst 20).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/rag/library": {
      "get": {
        "summary": "The legal library manifest",
        "description": "What the legal library holds and does not hold, with the last-update date of each queryable source. Public; no auth. Use it to check how current an answer is before relying on it.",
        "operationId": "getRagLibrary",
        "responses": {
          "200": {
            "description": "The library manifest, including as_of dates per source.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "as_of": {
                      "type": "string",
                      "description": "Manifest date (ISO)."
                    },
                    "sources": {
                      "type": "object",
                      "description": "Per queryable source: name, count, and the as_of / through date of its data.",
                      "additionalProperties": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          },
                          "as_of": {
                            "type": "string"
                          },
                          "through": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "holds": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "count": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string"
                          },
                          "as_of": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "does_not_hold": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "citation_check_semantics": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/byok": {
      "get": {
        "tags": [
          "BYOK"
        ],
        "summary": "List stored provider keys (masked)",
        "description": "Providers for which the caller has stored an upstream key of their own. Secrets are never returned; each entry carries only the provider name and a mask like sk-...abcd.",
        "operationId": "byokList",
        "responses": {
          "200": {
            "description": "Providers with masks"
          },
          "401": {
            "description": "Sign in or use a key"
          }
        }
      },
      "put": {
        "tags": [
          "BYOK"
        ],
        "summary": "Store your own upstream provider key",
        "description": "Bring-your-own-key: store an upstream provider API key (openrouter, anthropic or google), encrypted at rest with AES-256-GCM. The response carries only the mask; no route ever returns the stored secret.",
        "operationId": "byokSet",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "provider",
                  "key"
                ],
                "additionalProperties": false,
                "properties": {
                  "provider": {
                    "type": "string",
                    "enum": [
                      "openrouter",
                      "anthropic",
                      "google"
                    ]
                  },
                  "key": {
                    "type": "string",
                    "minLength": 8
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stored; response carries the mask only"
          },
          "400": {
            "description": "Unknown provider or malformed key"
          },
          "401": {
            "description": "Sign in or use a key"
          },
          "503": {
            "description": "BYOK not enabled on this deployment"
          }
        }
      },
      "delete": {
        "tags": [
          "BYOK"
        ],
        "summary": "Delete a stored provider key",
        "operationId": "byokDelete",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "provider"
                ],
                "additionalProperties": false,
                "properties": {
                  "provider": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deleted (idempotent)"
          },
          "401": {
            "description": "Sign in or use a key"
          }
        }
      }
    },
    "/api/v1/apps/top": {
      "get": {
        "tags": [
          "Apps"
        ],
        "summary": "Top apps by 30-day usage (public)",
        "description": "Public leaderboard of apps sending traffic through DocketRouter, attributed via the HTTP-Referer / X-Title headers. Spend is expressed only as a rank bucket (top-10pct / top-25pct / top-50pct / rest); no customer's raw dollar figure is ever exposed. Cached five minutes.",
        "operationId": "appsTop",
        "security": [],
        "responses": {
          "200": {
            "description": "Ranked apps with request counts and spend rank buckets"
          }
        }
      }
    },
    "/api/v1/models/health": {
      "get": {
        "tags": [
          "Models"
        ],
        "summary": "Model health (public, coarse)",
        "description": "Rolling success-rate and latency buckets per model, measured from real production outcomes. Coarse by design: ok / degraded / down and fast / normal / slow buckets with sample counts, never raw rates, raw latencies or upstream error text.",
        "operationId": "modelsHealth",
        "security": [],
        "responses": {
          "200": {
            "description": "Per-model health buckets"
          }
        }
      }
    },
    "/api/v1/jurisdictions": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Jurisdictions the product serves, with honest status",
        "description": "Every jurisdiction the API accepts in docketrouter.jurisdiction, its live/building/planned status, and the exact data behind each shard (counts and as-of dates from the library inventory). The machine-readable answer to \"does it cover my state?\". Public, cached five minutes.",
        "operationId": "listJurisdictions",
        "security": [],
        "responses": {
          "200": {
            "description": "Jurisdictions with per-shard data provenance"
          }
        }
      }
    },
    "/api/public/claims": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Civil-claim frameworks (public)",
        "description": "Generally recognized elements, what decides these cases, and common defenses for 16 civil claims. Public, CORS-open and cacheable so AI assistants can read and cite it. States plainly that these are national frameworks, not state-specific rules.",
        "operationId": "publicClaims",
        "security": [],
        "responses": {
          "200": {
            "description": "Data with provenance and caveats"
          }
        }
      }
    },
    "/api/public/courts": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "State appellate courts and DocketX coverage (public)",
        "description": "Each U.S. state's courts of last resort and intermediate appellate courts from the Free Law Project public record, plus exactly what the DocketX library holds for that state. Public and CORS-open.",
        "operationId": "publicCourts",
        "security": [],
        "responses": {
          "200": {
            "description": "Data with provenance and caveats"
          }
        }
      }
    },
    "/api/public/citation-check": {
      "post": {
        "tags": [
          "Public"
        ],
        "summary": "Check whether a citation exists (public, no key)",
        "description": "The free citation checker behind docketx.ai: one citation per call, existence only, answered from 18,123,788 citations across 989 reporters. Returns found (with the case name where the citation graph knows it) or unverified. Absence is never reported as proof a citation is fabricated. Rate-limited per IP; nothing is stored.",
        "operationId": "publicCitationCheck",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "citation"
                ],
                "additionalProperties": false,
                "properties": {
                  "citation": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 120,
                    "example": "384 U.S. 436"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "found or unverified, with a plain-language note"
          },
          "400": {
            "description": "Malformed body"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "options": {
        "tags": [
          "Public"
        ],
        "summary": "CORS preflight",
        "operationId": "publicCitationCheckPreflight",
        "security": [],
        "responses": {
          "204": {
            "description": "Preflight"
          }
        }
      }
    },
    "/api/public/contact": {
      "post": {
        "tags": [
          "Public"
        ],
        "summary": "Contact form submission (public)",
        "description": "Backend for the docketx.ai contact form. Store-first: every submission is written to a local ledger before mail is attempted, so a mail failure can never lose a message. Honeypot field, per-IP rate limit. Not a general-purpose messaging API.",
        "operationId": "publicContact",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "email",
                  "message"
                ],
                "additionalProperties": false,
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 200
                  },
                  "message": {
                    "type": "string",
                    "minLength": 5,
                    "maxLength": 5000
                  },
                  "site": {
                    "type": "string",
                    "maxLength": 40
                  },
                  "company": {
                    "type": "string",
                    "maxLength": 0,
                    "description": "Honeypot: must be empty. Bots that fill it get a success response and no delivery."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted"
          },
          "400": {
            "description": "Missing or invalid fields"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "options": {
        "tags": [
          "Public"
        ],
        "summary": "CORS preflight",
        "operationId": "publicContactPreflight",
        "security": [],
        "responses": {
          "204": {
            "description": "Preflight"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "dr-...",
        "description": "A DocketRouter API key, minted at https://docketrouter.ai/keys, sent as `Authorization: Bearer dr-...`."
      },
      "SessionAuth": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__session",
        "description": "A Clerk-issued session cookie from a signed-in browser at docketrouter.ai. Used by the web dashboard (key management, billing, matters, usage) rather than by server-to-server API automation. Not obtainable via the API itself."
      }
    },
    "parameters": {
      "KeyId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "A key id from `GET /keys` or the `id` returned by `POST /keys`."
      },
      "MatterId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "A matter id from `GET /matters` or the `id` returned by `POST /matters`."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "message",
              "type"
            ],
            "properties": {
              "message": {
                "type": "string",
                "description": "Written to be read by a person."
              },
              "type": {
                "type": "string",
                "description": "Written to be branched on by a program. `payment_required_error` (402) is returned by `POST /keys` when the account has no spend headroom and by `POST /chat/completions` when a key on a `limit_reset` schedule has used up its period cap. `conflict_error` (409) is reserved in the mapping table but not currently returned by any documented route; every other value is observed.",
                "enum": [
                  "invalid_request_error",
                  "authentication_error",
                  "payment_required_error",
                  "permission_error",
                  "not_found_error",
                  "conflict_error",
                  "rate_limit_error",
                  "api_error",
                  "upstream_error",
                  "service_unavailable_error"
                ]
              },
              "request_id": {
                "type": "string",
                "description": "Present on some 503/502 responses (chat completions, rag/query) so a caller can quote it to support."
              },
              "data_policy": {
                "type": "string",
                "description": "Present on some chat-completions provider-routing failures: the data policy that was in force."
              },
              "provider_prefs": {
                "type": "object",
                "description": "Present on some chat-completions provider-routing failures: the effective OpenRouter `provider` object that was in force."
              },
              "allowed_providers": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Present on some key provider-policy failures: the full list of providers DocketRouter permits."
              },
              "retry_after": {
                "type": "integer",
                "description": "Present on the `POST /hit` 429 only, as a body field rather than a header."
              },
              "task": {
                "type": "string",
                "description": "Present on the `POST /route` 404: the task that had no satisfying model."
              },
              "details": {
                "type": "object",
                "description": "Present on validation failures: zod field errors.",
                "properties": {
                  "fieldErrors": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "formErrors": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "limit_usd": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Present on the chat-completions 402: the key's cap in USD."
              },
              "period_spent_usd": {
                "type": "number",
                "description": "Present on the chat-completions 402: spend so far in the current period."
              },
              "limit_reset": {
                "type": "string",
                "enum": [
                  "daily",
                  "weekly",
                  "monthly"
                ],
                "description": "Present on the chat-completions 402: the key's reset schedule."
              },
              "resets_at": {
                "type": "string",
                "format": "date-time",
                "description": "Present on the chat-completions 402: the UTC start of the next period, when the cap frees up."
              }
            },
            "additionalProperties": true
          }
        }
      },
      "Message": {
        "type": "object",
        "required": [
          "role",
          "content"
        ],
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "system",
              "user",
              "assistant"
            ]
          },
          "content": {
            "type": "string"
          }
        }
      },
      "ProviderPrefsRequest": {
        "type": "object",
        "description": "Per-request OpenRouter provider narrowing. A request may only NARROW the key's stored policy (add to ignore, force data_collection deny, force zdr true, disable fallbacks, require parameters, or pick an order/only inside the key's allowlist); anything wider is a 400/403. Unknown fields are rejected.",
        "properties": {
          "order": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 32,
            "description": "Must be inside the key's allowlist."
          },
          "only": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 32
          },
          "ignore": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 64
          },
          "allow_fallbacks": {
            "type": "boolean",
            "description": "May only be set to false if the key allows fallbacks; may not be set to true if the key disables them."
          },
          "require_parameters": {
            "type": "boolean",
            "description": "May only be set to true; forced true automatically when response_format is used."
          },
          "data_collection": {
            "type": "string",
            "enum": [
              "deny"
            ],
            "description": "\"allow\" is rejected: every key routes with data_collection deny."
          },
          "zdr": {
            "type": "boolean",
            "description": "May only be set to true; may not be set to false if the key requires ZDR."
          },
          "quantizations": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "int4",
                "int8",
                "fp4",
                "mxfp4",
                "nvfp4",
                "fp6",
                "fp8",
                "mxfp8",
                "fp16",
                "bf16",
                "fp32",
                "unknown"
              ]
            }
          },
          "sort": {
            "type": "string",
            "enum": [
              "price",
              "throughput",
              "latency"
            ]
          }
        },
        "additionalProperties": false
      },
      "DocketRouterRequestOptions": {
        "type": "object",
        "description": "The `docketrouter` extension on a chat completion request. Every field defaults as shown; sending `docketrouter: {}` is equivalent to sending nothing.",
        "properties": {
          "juice": {
            "type": "boolean",
            "default": true,
            "description": "Ground the prompt against the rules corpus, case law and (if case_file) attached files."
          },
          "rules": {
            "type": "boolean",
            "default": true,
            "description": "Include the verbatim rules corpus in grounding."
          },
          "cases": {
            "type": "boolean",
            "default": true,
            "description": "Include case-law retrieval in grounding."
          },
          "verify": {
            "type": "boolean",
            "default": true,
            "description": "Check every citation the model produces and, non-streaming, revise on a fabricated one."
          },
          "revise": {
            "type": "boolean",
            "default": true,
            "description": "When verify catches a fabrication (non-streaming only), attempt one automatic rewrite using only verified authorities."
          },
          "jurisdiction": {
            "type": "string",
            "enum": [
              "tx",
              "ca",
              "ny",
              "fed"
            ],
            "description": "Defaults to the key's stored jurisdiction, else \"tx\"."
          },
          "session_id": {
            "type": "string",
            "maxLength": 128,
            "description": "Free-form label echoed back in the response; not a persistence mechanism (use matter_id for that)."
          },
          "matter_id": {
            "type": "string",
            "maxLength": 128,
            "description": "Attach this call to a persistent matter: up to the last 20 stored turns (trimmed to a 60,000-character budget) are prepended, case-file retrieval narrows to the matter's attachments, and the exchange is stored after a successful response. Requires matters to be enabled on the host."
          },
          "pin": {
            "type": "string",
            "maxLength": 64,
            "description": "Pin one provider slug (e.g. \"deepinfra/fp8\") with no fallbacks. Mutually exclusive with provider.order (unless they agree)."
          },
          "provider": {
            "$ref": "#/components/schemas/ProviderPrefsRequest"
          },
          "case_file": {
            "type": "boolean",
            "default": false,
            "description": "Retrieve from your own uploaded case files (POST /files) for this call, narrowed to matter_id's attachments if one is given."
          },
          "injection_policy": {
            "type": "string",
            "enum": [
              "flag",
              "block"
            ],
            "default": "flag",
            "description": "flag: hostile content in a case file is annotated and passed through as evidence. block: the request 422s instead."
          }
        }
      },
      "ChatCompletionRequest": {
        "type": "object",
        "required": [
          "model",
          "messages"
        ],
        "properties": {
          "model": {
            "type": "string",
            "description": "An OpenRouter model id (must be on the callable-model allowlist, see GET /models callable_models) or a local/... id served in-house."
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            },
            "minItems": 1,
            "maxItems": 64
          },
          "stream": {
            "type": "boolean",
            "default": false
          },
          "temperature": {
            "type": "number"
          },
          "max_tokens": {
            "type": "integer",
            "maximum": 8192,
            "description": "Ceiling of 8192."
          },
          "provider": {
            "type": "object",
            "description": "Legacy alias for docketrouter.provider. Same narrowing rules; never a raw pass-through.",
            "additionalProperties": true
          },
          "models": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 5,
            "description": "OpenRouter model fallbacks. Every entry is checked against the allowlist too."
          },
          "response_format": {
            "type": "object",
            "description": "Structured output (e.g. json_schema). Routed only to endpoints that support it; forces require_parameters true.",
            "additionalProperties": true
          },
          "docketrouter": {
            "$ref": "#/components/schemas/DocketRouterRequestOptions"
          }
        }
      },
      "CitationCheckResult": {
        "type": "object",
        "properties": {
          "input": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "found",
              "not_found",
              "ambiguous",
              "unverified"
            ]
          },
          "match": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "citation": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "court": {
                "type": "string"
              },
              "date": {
                "type": "string"
              },
              "url": {
                "type": "string"
              }
            }
          },
          "note": {
            "type": "string"
          }
        }
      },
      "InjectionFinding": {
        "type": "object",
        "properties": {
          "document": {
            "type": "string"
          },
          "verdict": {
            "type": "string"
          },
          "rules": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "excerpt": {
            "type": "string"
          }
        }
      },
      "SourcesBlock": {
        "type": "object",
        "description": "What grounding actually retrieved for this request (present when docketrouter.juice was true).",
        "properties": {
          "rules": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "set": {
                  "type": "string"
                },
                "source": {
                  "type": "string"
                },
                "cite": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "text": {
                  "type": "string"
                },
                "score": {
                  "type": "number"
                }
              }
            }
          },
          "cases": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "citation": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "court": {
                  "type": "string"
                },
                "date": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "snippet": {
                  "type": "string"
                }
              }
            }
          },
          "citations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CitationCheckResult"
            }
          },
          "ms": {
            "type": "number",
            "description": "Retrieval latency in milliseconds."
          },
          "injection": {
            "type": "object",
            "properties": {
              "blocks": {
                "type": "integer"
              },
              "flagged": {
                "type": "integer"
              },
              "verdict": {
                "type": "string",
                "enum": [
                  "clean",
                  "suspicious",
                  "hostile"
                ]
              },
              "findings": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/InjectionFinding"
                }
              }
            }
          },
          "degraded": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "retrieval": {
                "type": "boolean"
              },
              "index_unreachable": {
                "type": "boolean"
              },
              "reason": {
                "type": "string"
              }
            }
          }
        }
      },
      "Verification": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "checked": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CitationCheckResult"
            }
          },
          "fabricated": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Citations affirmatively not found; on a non-streaming call these trigger one revision pass."
          },
          "unverified": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Citations neither confirmed nor refuted."
          },
          "revised": {
            "type": "boolean"
          },
          "strippedCites": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "leakedCites": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "DocketRouterResponseMeta": {
        "type": "object",
        "description": "The `docketrouter` object on a chat completion response.",
        "properties": {
          "sources": {
            "$ref": "#/components/schemas/SourcesBlock"
          },
          "injection": {
            "type": "object",
            "nullable": true
          },
          "degraded": {
            "type": [
              "object",
              "null"
            ]
          },
          "upstream": {
            "type": "string",
            "description": "\"private_pod\", or the configured upstream gateway name."
          },
          "juiced": {
            "type": "boolean"
          },
          "data_policy": {
            "type": "string",
            "enum": [
              "shared",
              "no_retention",
              "private_pod"
            ]
          },
          "zdr_requested": {
            "type": "boolean"
          },
          "provider_prefs": {
            "type": [
              "object",
              "null"
            ],
            "description": "The effective OpenRouter provider object actually sent upstream."
          },
          "jurisdiction": {
            "type": "string",
            "enum": [
              "tx",
              "ca",
              "ny",
              "fed"
            ]
          },
          "session_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "matter_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "verification": {
            "$ref": "#/components/schemas/Verification"
          },
          "request_id": {
            "type": "string"
          },
          "generation_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "provider": {
            "type": [
              "string",
              "null"
            ],
            "description": "The upstream provider that actually served the request."
          },
          "provider_served": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ChatCompletionResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "chat.completion"
            ]
          },
          "created": {
            "type": "integer"
          },
          "model": {
            "type": "string"
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer"
                },
                "message": {
                  "$ref": "#/components/schemas/Message"
                },
                "finish_reason": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "usage": {
            "type": "object",
            "properties": {
              "prompt_tokens": {
                "type": "integer"
              },
              "completion_tokens": {
                "type": "integer"
              },
              "total_tokens": {
                "type": "integer"
              },
              "cost": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "DocketRouter's billed price (upstream cost x markup), when computable."
              }
            }
          },
          "docketrouter": {
            "$ref": "#/components/schemas/DocketRouterResponseMeta"
          }
        }
      },
      "ChatCompletionChunk": {
        "type": "object",
        "description": "One SSE frame's JSON payload (sent as `data: <this>\\n\\n`). The final frame has `choices[0].finish_reason` set and carries `usage`/`docketrouter.verification`; the stream is terminated by a literal `data: [DONE]\\n\\n` frame that is not JSON.",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "chat.completion.chunk"
            ]
          },
          "created": {
            "type": "integer"
          },
          "model": {
            "type": "string"
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer"
                },
                "delta": {
                  "type": "object",
                  "properties": {
                    "role": {
                      "type": "string"
                    },
                    "content": {
                      "type": "string"
                    }
                  }
                },
                "finish_reason": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "docketrouter": {
            "type": "object",
            "description": "Present on the first chunk (sources/meta minus verification) and the last chunk (verification, generation_id, provider)."
          },
          "usage": {
            "type": "object",
            "description": "Present only on the final chunk."
          }
        }
      },
      "GatewayModel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "owned_by": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "context_window": {
            "type": "integer"
          },
          "max_tokens": {
            "type": "integer"
          },
          "type": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "reasoning",
                "tool-use",
                "vision"
              ]
            }
          },
          "knowledge": {
            "type": "string"
          },
          "released": {
            "type": "integer"
          },
          "zdr": {
            "type": "string"
          },
          "modalities": {
            "type": "object",
            "properties": {
              "input": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "output": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "supported_parameters": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "pricing": {
            "type": "object",
            "properties": {
              "input": {
                "type": "string"
              },
              "output": {
                "type": "string"
              },
              "input_cache_read": {
                "type": "string"
              },
              "input_cache_write": {
                "type": "string"
              }
            },
            "description": "Per-token USD strings; already DocketRouter's marked-up price, not OpenRouter's raw price."
          }
        }
      },
      "CatalogModel": {
        "allOf": [
          {
            "$ref": "#/components/schemas/GatewayModel"
          },
          {
            "type": "object",
            "properties": {
              "callable": {
                "type": "boolean",
                "description": "Whether chat/completions will accept this model id."
              },
              "legal": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "overall": {
                    "type": "number"
                  },
                  "coverage": {
                    "type": "number"
                  },
                  "illustrative": {
                    "type": "boolean"
                  },
                  "tasks": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "ModelsListResponse": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "callable_models": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogModel"
            }
          }
        }
      },
      "ModelDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/GatewayModel"
          },
          {
            "type": "object",
            "properties": {
              "legal": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/RunRecord"
                }
              }
            }
          }
        ]
      },
      "ApiKeySummary": {
        "type": "object",
        "description": "One row of GET /keys data[]: the stored key row, its spend-counter view, local usage totals, and the upstream cap and usage behind it.",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string"
          },
          "limit_usd": {
            "type": [
              "number",
              "null"
            ]
          },
          "disabled": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "owner_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "limit_reset": {
            "$ref": "#/components/schemas/LimitReset"
          },
          "limit_reset_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "UTC start of the current period, or null when limit_reset is none."
          },
          "resets_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "UTC start of the next period, or null when limit_reset is none."
          },
          "spent_usd": {
            "type": "number",
            "description": "Lifetime spend against this key, regardless of reset schedule."
          },
          "period_spent_usd": {
            "type": "number",
            "description": "Spend since limit_reset_at. Equal to spent_usd when limit_reset is none."
          },
          "remaining_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "limit_usd minus the counter the cap applies to (period_spent_usd with a schedule, spent_usd without); null when limit_usd is null."
          },
          "local": {
            "$ref": "#/components/schemas/UsageTotals"
          },
          "openrouter": {
            "type": [
              "object",
              "null"
            ]
          }
        },
        "additionalProperties": true
      },
      "ApiKeyDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string"
          },
          "limit_usd": {
            "type": [
              "number",
              "null"
            ]
          },
          "disabled": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "log_content": {
            "type": "boolean"
          },
          "data_policy": {
            "type": "string",
            "enum": [
              "shared",
              "no_retention",
              "private_pod"
            ]
          },
          "routing": {
            "type": "string",
            "enum": [
              "shared",
              "no-train",
              "zdr",
              "pinned"
            ]
          },
          "pin_provider": {
            "type": [
              "string",
              "null"
            ]
          },
          "provider_allow": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "provider_order": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allow_fallbacks": {
            "type": "boolean"
          },
          "require_parameters": {
            "type": "boolean"
          },
          "jurisdiction": {
            "type": "string",
            "enum": [
              "tx",
              "ca",
              "ny",
              "fed"
            ]
          },
          "allowed_providers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "limit_reset": {
            "$ref": "#/components/schemas/LimitReset"
          },
          "limit_reset_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "resets_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "spent_usd": {
            "type": "number"
          },
          "period_spent_usd": {
            "type": "number"
          },
          "remaining_usd": {
            "type": [
              "number",
              "null"
            ]
          },
          "openrouter": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "usage": {
                "type": "number"
              },
              "limit": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "limit_remaining": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "disabled": {
                "type": "boolean"
              }
            }
          },
          "local": {
            "$ref": "#/components/schemas/UsageTotals"
          },
          "recent": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageRow"
            },
            "description": "Up to 50 most recent usage rows on this key."
          }
        }
      },
      "UsageTotals": {
        "type": "object",
        "properties": {
          "requests": {
            "type": "integer"
          },
          "cost": {
            "type": "number"
          },
          "prompt_tokens": {
            "type": "integer"
          },
          "completion_tokens": {
            "type": "integer"
          },
          "latency": {
            "type": "number"
          },
          "juiced": {
            "type": "integer"
          },
          "fakes_caught": {
            "type": "integer",
            "description": "Running count of citations returned not_found."
          }
        }
      },
      "CreateKeyRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "limit_usd": {
            "type": [
              "number",
              "null"
            ],
            "default": 25,
            "maximum": 10000
          },
          "limit_reset": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LimitReset"
              }
            ],
            "default": "none"
          }
        }
      },
      "CreateKeyResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "key": {
            "type": "string",
            "description": "The secret. Shown once; there is no endpoint that returns it again."
          },
          "limit_usd": {
            "type": [
              "number",
              "null"
            ]
          },
          "openrouter_backed": {
            "type": "boolean"
          },
          "limit_reset": {
            "$ref": "#/components/schemas/LimitReset"
          },
          "limit_reset_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "resets_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "spent_usd": {
            "type": "number"
          },
          "period_spent_usd": {
            "type": "number"
          },
          "remaining_usd": {
            "type": [
              "number",
              "null"
            ]
          }
        }
      },
      "PatchKeyRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "limit_usd": {
            "type": [
              "number",
              "null"
            ],
            "maximum": 10000
          },
          "data_policy": {
            "type": "string",
            "enum": [
              "shared",
              "no_retention",
              "private_pod"
            ],
            "description": "An owner may move between shared and no_retention only; private_pod is DocketRouter-managed."
          },
          "pod_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Operator-managed; silently dropped from an owner's request."
          },
          "jurisdiction": {
            "type": "string",
            "enum": [
              "tx",
              "ca",
              "ny",
              "fed"
            ]
          },
          "log_content": {
            "type": "boolean"
          },
          "routing": {
            "type": "string",
            "enum": [
              "shared",
              "no-train",
              "zdr",
              "pinned"
            ],
            "description": "Operator-managed."
          },
          "pin_provider": {
            "type": [
              "string",
              "null"
            ]
          },
          "provider_allow": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 32,
            "nullable": true
          },
          "provider_order": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 32,
            "nullable": true
          },
          "allow_fallbacks": {
            "type": "boolean"
          },
          "require_parameters": {
            "type": "boolean"
          },
          "limit_reset": {
            "$ref": "#/components/schemas/LimitReset"
          }
        }
      },
      "PatchKeyResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "id": {
            "type": "string"
          },
          "data_policy": {
            "type": "string"
          },
          "routing": {
            "type": "string"
          },
          "pin_provider": {
            "type": [
              "string",
              "null"
            ]
          },
          "provider_allow": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "provider_order": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allow_fallbacks": {
            "type": "boolean"
          },
          "require_parameters": {
            "type": "boolean"
          },
          "name": {
            "type": "string"
          },
          "limit_usd": {
            "type": "number"
          },
          "jurisdiction": {
            "type": "string"
          },
          "log_content": {
            "type": "boolean"
          },
          "limit_reset": {
            "$ref": "#/components/schemas/LimitReset"
          },
          "limit_reset_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "resets_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "spent_usd": {
            "type": "number"
          },
          "period_spent_usd": {
            "type": "number"
          },
          "remaining_usd": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "description": "name, limit_usd, jurisdiction and log_content are echoed only when they were part of the patch; the spend-counter fields (limit_reset onward) are always present."
      },
      "RotateKeyResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "key": {
            "type": "string",
            "description": "The new secret. Shown once."
          },
          "limit_usd": {
            "type": [
              "number",
              "null"
            ]
          },
          "rotated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AuthKeyResponse": {
        "type": "object",
        "properties": {
          "data": {
            "oneOf": [
              {
                "type": "object",
                "description": "An API key credential.",
                "properties": {
                  "label": {
                    "type": "string",
                    "description": "The key's masked prefix."
                  },
                  "name": {
                    "type": "string"
                  },
                  "limit": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "The key's lifetime or per-period cap in USD, or null for uncapped."
                  },
                  "limit_reset": {
                    "type": "string",
                    "enum": [
                      "none",
                      "daily",
                      "weekly",
                      "monthly"
                    ]
                  },
                  "limit_reset_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Start of the current accounting period, or null when limit_reset is 'none'."
                  },
                  "resets_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Start of the next accounting period, or null when limit_reset is 'none'."
                  },
                  "spent": {
                    "type": "number",
                    "description": "Lifetime spend in USD."
                  },
                  "period_spent": {
                    "type": "number",
                    "description": "Spend in USD since limit_reset_at."
                  },
                  "limit_remaining": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "usage": {
                    "type": "number"
                  },
                  "usage_daily": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "usage_monthly": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "rate_limit": {
                    "type": "object",
                    "properties": {
                      "requests": {
                        "type": "integer"
                      },
                      "interval": {
                        "type": "string"
                      },
                      "burst": {
                        "type": "integer"
                      }
                    }
                  },
                  "data_policy": {
                    "type": "string",
                    "enum": [
                      "shared",
                      "no_retention",
                      "private_pod"
                    ]
                  },
                  "routing": {
                    "type": "string",
                    "enum": [
                      "shared",
                      "no-train",
                      "zdr",
                      "pinned"
                    ]
                  },
                  "pin_provider": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "provider_allow": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "provider_order": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "allow_fallbacks": {
                    "type": "boolean"
                  },
                  "require_parameters": {
                    "type": "boolean"
                  },
                  "jurisdiction": {
                    "type": "string"
                  },
                  "log_content": {
                    "type": "boolean"
                  },
                  "disabled": {
                    "type": "boolean"
                  },
                  "created_at": {
                    "type": "string"
                  },
                  "local": {
                    "$ref": "#/components/schemas/UsageTotals"
                  }
                }
              },
              {
                "type": "object",
                "description": "A signed-in browser session.",
                "properties": {
                  "label": {
                    "type": "string",
                    "enum": [
                      "session"
                    ]
                  },
                  "user_id": {
                    "type": "string"
                  }
                }
              },
              {
                "type": "object",
                "description": "The admin token.",
                "properties": {
                  "label": {
                    "type": "string",
                    "enum": [
                      "admin"
                    ]
                  },
                  "is_admin": {
                    "type": "boolean"
                  }
                }
              }
            ]
          }
        }
      },
      "CaseFileSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "chars": {
            "type": "integer"
          },
          "chunks": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateFileRequest": {
        "type": "object",
        "required": [
          "name",
          "text"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000000
          }
        }
      },
      "CreateFileResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "chunks": {
            "type": "integer"
          },
          "injection": {
            "type": "object",
            "properties": {
              "verdict": {
                "type": "string",
                "enum": [
                  "clean",
                  "suspicious",
                  "hostile"
                ]
              },
              "rules": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "hidden_chars": {
                "type": "integer"
              },
              "excerpt": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "note": {
            "type": "string"
          }
        }
      },
      "MatterSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "archived_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "message_count": {
            "type": "integer"
          },
          "file_count": {
            "type": "integer"
          }
        }
      },
      "MatterFile": {
        "type": "object",
        "properties": {
          "file_id": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "chars": {
            "type": [
              "integer",
              "null"
            ]
          },
          "chunks": {
            "type": [
              "integer",
              "null"
            ]
          },
          "attached_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MatterMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "role": {
            "type": "string",
            "enum": [
              "system",
              "user",
              "assistant"
            ]
          },
          "content": {
            "type": "string"
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "verification": {
            "type": [
              "object",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MatterDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/MatterSummary"
          },
          {
            "type": "object",
            "properties": {
              "messages": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/MatterMessage"
                }
              },
              "has_more": {
                "type": "boolean"
              },
              "files": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/MatterFile"
                }
              }
            }
          }
        ]
      },
      "RagQueryRequest": {
        "type": "object",
        "required": [
          "q"
        ],
        "additionalProperties": false,
        "properties": {
          "q": {
            "type": "string",
            "minLength": 1,
            "maxLength": 600
          },
          "k": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 5
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "tx",
                "statutes",
                "scotus",
                "local"
              ]
            },
            "minItems": 1,
            "maxItems": 4,
            "description": "Defaults to all four shards."
          },
          "min_score": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        }
      },
      "RagQueryResponse": {
        "type": "object",
        "properties": {
          "hits": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "source": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "citation": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "court": {
                  "type": "string"
                },
                "date": {
                  "type": "string"
                },
                "snippet": {
                  "type": "string"
                },
                "score": {
                  "type": [
                    "number",
                    "null"
                  ]
                }
              }
            }
          },
          "took_ms": {
            "type": "number"
          },
          "sources_searched": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "RagRulesRequest": {
        "type": "object",
        "required": [
          "q"
        ],
        "additionalProperties": false,
        "properties": {
          "q": {
            "type": "string",
            "minLength": 1,
            "maxLength": 600
          },
          "k": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 5
          }
        }
      },
      "RagRulesResponse": {
        "type": "object",
        "properties": {
          "hits": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "cite": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "set": {
                  "type": "string"
                },
                "source": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "text": {
                  "type": "string"
                },
                "truncated": {
                  "type": "boolean",
                  "description": "true when text was cut at 1200 characters."
                },
                "score": {
                  "type": "number"
                }
              }
            }
          },
          "took_ms": {
            "type": "number"
          }
        }
      },
      "CitationsCheckRequest": {
        "type": "object",
        "required": [
          "citations"
        ],
        "additionalProperties": false,
        "properties": {
          "citations": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "minItems": 1,
            "maxItems": 100
          }
        }
      },
      "CitationsCheckResponse": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "input": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "found",
                    "unverified"
                  ],
                  "description": "This route only ever reports these two statuses -- it never asserts fabrication."
                },
                "note": {
                  "type": "string"
                },
                "match": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "citation": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "court": {
                      "type": "string"
                    },
                    "date": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "checked": {
            "type": "integer"
          },
          "found": {
            "type": "integer"
          },
          "took_ms": {
            "type": "number"
          }
        }
      },
      "DownloadListing": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "what": {
            "type": "string"
          },
          "filename": {
            "type": "string"
          },
          "content_type": {
            "type": "string"
          },
          "bytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "size": {
            "type": [
              "integer",
              "null"
            ]
          },
          "approx_size": {
            "type": "string"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "etag": {
            "type": [
              "string",
              "null"
            ]
          },
          "available": {
            "type": "boolean"
          },
          "generated_on_request": {
            "type": "boolean"
          },
          "licence": {
            "type": "string"
          },
          "attribution": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "auth": {
            "type": "string",
            "enum": [
              "key or session"
            ]
          },
          "rate_limit": {
            "type": "string",
            "enum": [
              "2 per minute"
            ]
          }
        }
      },
      "UsageRow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "key_id": {
            "type": "string"
          },
          "key_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "model": {
            "type": "string"
          },
          "juiced": {
            "type": "boolean"
          },
          "prompt_tokens": {
            "type": [
              "integer",
              "null"
            ]
          },
          "completion_tokens": {
            "type": [
              "integer",
              "null"
            ]
          },
          "cached_tokens": {
            "type": [
              "integer",
              "null"
            ]
          },
          "cost_usd": {
            "type": [
              "number",
              "null"
            ]
          },
          "upstream_cost_usd": {
            "type": [
              "number",
              "null"
            ]
          },
          "latency_ms": {
            "type": "number"
          },
          "retrieval_ms": {
            "type": [
              "number",
              "null"
            ]
          },
          "sources_n": {
            "type": [
              "integer",
              "null"
            ]
          },
          "citations_checked": {
            "type": [
              "integer",
              "null"
            ]
          },
          "citations_not_found": {
            "type": [
              "integer",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "error"
            ]
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "provider": {
            "type": [
              "string",
              "null"
            ]
          },
          "or_generation_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_content": {
            "type": "boolean",
            "description": "Whether content was logged for this row (only when the key has log_content on); the content itself is only returned by GET /usage/{request_id}."
          },
          "verification": {
            "$ref": "#/components/schemas/Verification"
          }
        }
      },
      "UsageListResponse": {
        "type": "object",
        "properties": {
          "totals": {
            "$ref": "#/components/schemas/UsageTotals"
          },
          "by_model": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "model": {
                  "type": "string"
                },
                "requests": {
                  "type": "integer"
                },
                "cost": {
                  "type": "number"
                },
                "tokens": {
                  "type": "integer"
                }
              }
            }
          },
          "by_key": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "requests": {
                  "type": "integer"
                },
                "cost": {
                  "type": "number"
                },
                "last_used": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            },
            "description": "Present only for a signed-in session, not for a single API key."
          },
          "by_app": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageByApp"
            },
            "description": "Spend grouped by attributed app, over the caller's whole scope."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageRow"
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "next_offset": {
            "type": [
              "integer",
              "null"
            ]
          },
          "filters": {
            "type": "object",
            "description": "Echoes the filters actually applied to `data` (fields the caller did not pass are omitted).",
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time"
              },
              "to": {
                "type": "string",
                "format": "date-time"
              },
              "model": {
                "type": "string"
              },
              "app": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "ok",
                  "error"
                ]
              },
              "juiced": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "UsageDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UsageRow"
          },
          {
            "type": "object",
            "properties": {
              "content": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "messages": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Message"
                    }
                  },
                  "answer": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "openrouter_generation": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "billed_usd": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "provider_name": {
                    "type": "string"
                  },
                  "native_tokens_prompt": {
                    "type": "integer"
                  },
                  "native_tokens_completion": {
                    "type": "integer"
                  },
                  "native_tokens_cached": {
                    "type": "integer"
                  }
                },
                "additionalProperties": true
              }
            }
          }
        ]
      },
      "BalanceResponse": {
        "type": "object",
        "properties": {
          "balance_cents": {
            "type": "integer",
            "description": "API credits: purchased minus refunded, in cents."
          },
          "available_cents": {
            "type": "integer",
            "description": "API credits minus metered usage, in cents. This is what auto top-up compares against its threshold. Can be negative."
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether card billing is on."
          },
          "monero": {
            "type": "boolean",
            "description": "Whether Monero payments are configured."
          },
          "topups": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "Preset top-up amounts in cents: [1000, 2500, 10000]."
          },
          "custom_topup": {
            "type": "object",
            "properties": {
              "min_usd": {
                "type": "integer"
              },
              "max_usd": {
                "type": "integer"
              }
            },
            "description": "Custom amount range in whole dollars: 5 to 1000."
          },
          "txns": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "kind": {
                  "type": "string",
                  "description": "topup, refund, autotopup, autotopup_pending (0), autotopup_failed (0), autotopup_reset (0), or a Monero kind."
                },
                "amount_cents": {
                  "type": "integer"
                },
                "note": {
                  "type": "string"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "MoneroInvoice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "amount_xmr": {
            "type": "string"
          },
          "amount_usd": {
            "type": "string"
          },
          "usd_per_xmr": {
            "type": "number"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "uri": {
            "type": "string",
            "description": "A monero: payment URI."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending"
            ]
          },
          "note": {
            "type": "string"
          }
        }
      },
      "MoneroInvoiceStatus": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "amount_xmr": {
            "type": "string"
          },
          "received_xmr": {
            "type": "string"
          },
          "amount_usd": {
            "type": "string"
          },
          "confirmations_required": {
            "type": "integer"
          },
          "txids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "settled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CreateHllSubmissionRequest": {
        "type": "object",
        "required": [
          "name",
          "model"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 64,
            "description": "Shown on the leaderboard as \"community \u00b7 name\"."
          },
          "version": {
            "type": "string",
            "default": "hll-0.1-tx"
          },
          "model": {
            "type": "string",
            "description": "An OpenRouter model id, or the model name your own endpoint expects."
          },
          "base_url": {
            "type": "string",
            "format": "uri",
            "description": "An OpenAI-compatible endpoint; omit to run an OpenRouter model id through DocketRouter."
          },
          "api_key": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "raw",
              "juiced"
            ],
            "default": "raw"
          },
          "embargo_days": {
            "type": "integer",
            "minimum": 0,
            "maximum": 7,
            "default": 0
          }
        }
      },
      "HllSubmission": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "raw",
              "juiced"
            ]
          },
          "status": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "score": {
            "type": [
              "number",
              "null"
            ],
            "description": "null while embargoed and the caller is not the owner or an admin."
          },
          "n": {
            "type": [
              "integer",
              "null"
            ]
          },
          "hallucinated": {
            "type": [
              "integer",
              "null"
            ]
          },
          "results": {
            "type": "object",
            "description": "Present only for the owner or an admin."
          }
        }
      },
      "RunRecord": {
        "type": "object",
        "properties": {
          "model": {
            "type": "string"
          },
          "task": {
            "type": "string"
          },
          "suite": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "raw",
              "juiced"
            ]
          },
          "upstream": {
            "type": "string",
            "enum": [
              "openrouter",
              "ai-gateway"
            ]
          },
          "n": {
            "type": "integer"
          },
          "correct": {
            "type": "integer"
          },
          "score": {
            "type": "number"
          },
          "latencyMs": {
            "type": "number"
          },
          "inputTokens": {
            "type": "integer"
          },
          "outputTokens": {
            "type": "integer"
          },
          "costUsd": {
            "type": "number"
          },
          "ranAt": {
            "type": "string",
            "format": "date-time"
          },
          "illustrative": {
            "type": "boolean",
            "description": "true = a seeded example, not a real benchmark run."
          }
        }
      },
      "RouteRequest": {
        "type": "object",
        "properties": {
          "task": {
            "type": "string",
            "default": "overall"
          },
          "max_input_price_per_m": {
            "type": "number"
          },
          "max_latency_ms": {
            "type": "number"
          },
          "min_score": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "include_illustrative": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "RouteResponse": {
        "type": "object",
        "properties": {
          "task": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "score": {
            "type": "number"
          },
          "latency_ms": {
            "type": "number"
          },
          "illustrative": {
            "type": "boolean"
          },
          "input_price_per_m": {
            "type": [
              "number",
              "null"
            ]
          },
          "alternatives": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "model": {
                  "type": "string"
                },
                "score": {
                  "type": "number"
                },
                "latency_ms": {
                  "type": "number"
                },
                "illustrative": {
                  "type": "boolean"
                },
                "input_price_per_m": {
                  "type": [
                    "number",
                    "null"
                  ]
                }
              }
            },
            "maxItems": 5
          }
        }
      },
      "CitationsSupportRequest": {
        "type": "object",
        "required": [
          "citation",
          "proposition"
        ],
        "additionalProperties": false,
        "properties": {
          "citation": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "proposition": {
            "type": "string",
            "minLength": 10,
            "maxLength": 600
          },
          "jurisdiction": {
            "type": "string",
            "enum": [
              "tx"
            ],
            "description": "Only \"tx\" is accepted today; omit for Texas."
          }
        }
      },
      "CitationsSupportResponse": {
        "type": "object",
        "required": [
          "status",
          "took_ms",
          "request_id"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "found",
              "unverified"
            ],
            "description": "found = the citation resolved in the library; only then can verdict/quote/passage/case be present. unverified has the same meaning as on /citations/check -- absence is not evidence of fabrication."
          },
          "verdict": {
            "type": "string",
            "enum": [
              "supports",
              "does_not_support",
              "unclear"
            ],
            "description": "Present only when status is \"found\"."
          },
          "quote": {
            "type": "string",
            "description": "Verbatim substring of passage supporting the proposition. Present only when verdict is \"supports\"; never fabricated or paraphrased."
          },
          "passage": {
            "type": "string",
            "description": "Up to 1200 characters of the retrieved passage the verdict was based on."
          },
          "case": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "citation": {
                "type": "string"
              },
              "court": {
                "type": "string"
              },
              "date": {
                "type": "string"
              }
            }
          },
          "note": {
            "type": "string",
            "description": "Present when status is \"unverified\"."
          },
          "took_ms": {
            "type": "number"
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "AppUsage": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true,
            "description": "App name, from X-DocketRouter-App, X-Title, or the referrer's hostname."
          },
          "url": {
            "type": "string",
            "nullable": true,
            "description": "App URL, from HTTP-Referer/Referer, when it parsed as http(s)."
          },
          "requests": {
            "type": "integer"
          },
          "cost_usd": {
            "type": "number"
          },
          "prompt_tokens": {
            "type": "integer"
          },
          "completion_tokens": {
            "type": "integer"
          },
          "last_used": {
            "type": "string",
            "nullable": true,
            "description": "ISO 8601 timestamp of the most recent request."
          }
        },
        "required": [
          "name",
          "url",
          "requests",
          "cost_usd",
          "prompt_tokens",
          "completion_tokens",
          "last_used"
        ]
      },
      "AppsListResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppUsage"
            },
            "description": "One row per attributed app, sorted by cost descending. Never includes an unattributed row."
          },
          "unattributed": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AppUsage"
              }
            ],
            "nullable": true,
            "description": "Combined totals for every request with no app attribution, or null when there are none. `name` and `url` are always null on this row."
          }
        },
        "required": [
          "data",
          "unattributed"
        ]
      },
      "AutoTopupSettings": {
        "type": "object",
        "required": [
          "enabled",
          "threshold_usd",
          "amount_usd",
          "card_on_file",
          "pending",
          "consecutive_failures",
          "last_failure_at",
          "last_failure_reason",
          "disabled_reason",
          "available_cents",
          "limits"
        ],
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "True only when the owner turned it on AND a card is on file."
          },
          "threshold_usd": {
            "type": "integer",
            "description": "Charge when available API credits fall below this. Default 5."
          },
          "amount_usd": {
            "type": "integer",
            "description": "Added per auto top-up. Default 25; allowed 10 to 500."
          },
          "card_on_file": {
            "type": "boolean"
          },
          "last4": {
            "type": "string",
            "description": "Present when a card is on file and Stripe is reachable."
          },
          "brand": {
            "type": "string"
          },
          "pending": {
            "type": "boolean",
            "description": "An auto top-up charge was created and is awaiting Stripe's confirmation; no second charge is made while true."
          },
          "consecutive_failures": {
            "type": "integer",
            "description": "Failed charges since the last success or since the owner last turned it on. At 3 auto top-up switches off."
          },
          "last_failure_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_failure_reason": {
            "type": "string",
            "nullable": true,
            "description": "Stripe decline code or error code."
          },
          "disabled_reason": {
            "type": "string",
            "nullable": true,
            "description": "Why enabled is false when the owner asked for it on: the failure breaker, or no card on file."
          },
          "available_cents": {
            "type": "integer",
            "description": "API credits minus metered usage, the figure compared against the threshold."
          },
          "limits": {
            "type": "object",
            "properties": {
              "threshold_min_usd": {
                "type": "integer"
              },
              "threshold_max_usd": {
                "type": "integer"
              },
              "amount_min_usd": {
                "type": "integer"
              },
              "amount_max_usd": {
                "type": "integer"
              }
            }
          }
        }
      },
      "CreditsResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "owner": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Null when the authenticating key has no owner_id attached; every other field is then zeroed except total_used_usd, which falls back to the key's own lifetime spend."
              },
              "balance_cents": {
                "type": "integer"
              },
              "balance_usd": {
                "type": "number"
              },
              "total_purchased_cents": {
                "type": "integer"
              },
              "total_purchased_usd": {
                "type": "number"
              },
              "total_refunded_cents": {
                "type": "integer"
              },
              "total_used_usd": {
                "type": "number"
              },
              "pending_cents": {
                "type": "integer",
                "description": "Sum of unexpired pending Monero invoices."
              },
              "pending_usd": {
                "type": "number"
              },
              "trial_allowance_usd": {
                "type": "number"
              },
              "billing_enabled": {
                "type": "boolean"
              },
              "monero": {
                "type": "boolean",
                "description": "Omitted when owner is null."
              },
              "note": {
                "type": "string",
                "description": "Present only when owner is null."
              }
            },
            "required": [
              "owner",
              "balance_cents",
              "balance_usd",
              "total_purchased_cents",
              "total_purchased_usd",
              "total_refunded_cents",
              "total_used_usd",
              "pending_cents",
              "pending_usd",
              "trial_allowance_usd",
              "billing_enabled"
            ]
          }
        }
      },
      "LimitReset": {
        "type": "string",
        "enum": [
          "none",
          "daily",
          "weekly",
          "monthly"
        ],
        "description": "Rollover schedule for the key's spend cap. none = limit_usd is a lifetime cap. daily/weekly/monthly reset period_spent_usd to 0 at UTC midnight / the Monday of the ISO week / the 1st of the month."
      },
      "DailyUsageRow": {
        "type": "object",
        "description": "One day's bucket, optionally split by `model` or `app_name` when `group` was passed.",
        "properties": {
          "day": {
            "type": "string",
            "format": "date",
            "description": "UTC calendar date, YYYY-MM-DD."
          },
          "model": {
            "type": "string",
            "description": "Present only when `group=model`."
          },
          "app_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Present only when `group=app`."
          },
          "requests": {
            "type": "integer"
          },
          "prompt_tokens": {
            "type": "integer"
          },
          "completion_tokens": {
            "type": "integer"
          },
          "cost_usd": {
            "type": "number"
          },
          "upstream_cost_usd": {
            "type": "number"
          }
        }
      },
      "UsageDailyResponse": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "format": "date-time"
          },
          "group": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "model",
              "app",
              null
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DailyUsageRow"
            }
          },
          "totals": {
            "type": "object",
            "description": "Sum across `data`, unaffected by how (or whether) it is grouped.",
            "properties": {
              "requests": {
                "type": "integer"
              },
              "prompt_tokens": {
                "type": "integer"
              },
              "completion_tokens": {
                "type": "integer"
              },
              "cost_usd": {
                "type": "number"
              },
              "upstream_cost_usd": {
                "type": "number"
              }
            }
          }
        }
      },
      "GenerationDetail": {
        "description": "`UsageDetail` plus additive structured fields. Nothing existing was removed or renamed; the flat fields (e.g. `prompt_tokens`, `sources_n`, `key_name`) stay exactly as they were, and the objects below just group the same values for convenience.",
        "allOf": [
          {
            "$ref": "#/components/schemas/UsageDetail"
          },
          {
            "type": "object",
            "properties": {
              "app": {
                "type": "object",
                "description": "The attributed calling app for this request, or nulls if none was sent.",
                "properties": {
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "url": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "price_usd": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "The DocketRouter price for this request. Same value as `cost_usd`."
              },
              "tokens": {
                "type": "object",
                "properties": {
                  "prompt": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "completion": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "cached": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  }
                }
              },
              "latency": {
                "type": "object",
                "properties": {
                  "total_ms": {
                    "type": "number"
                  },
                  "retrieval_ms": {
                    "type": [
                      "number",
                      "null"
                    ]
                  }
                }
              },
              "grounding": {
                "type": "object",
                "properties": {
                  "sources_n": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "citations_checked": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "citations_not_found": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "reranked": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "From the verification report, when present."
                  },
                  "verification": {
                    "type": [
                      "object",
                      "null"
                    ]
                  }
                }
              },
              "key": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "timestamps": {
                "type": "object",
                "properties": {
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "synced_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  }
                }
              }
            }
          }
        ]
      },
      "UsageByApp": {
        "type": "object",
        "description": "Spend attributed to one app, over the queried scope (unfiltered by the list's own from/to/model/app/key/status/juiced params -- this is the same whole-scope rollup `totals` and `by_model` already are). One row has `app_name: null` for unattributed requests.",
        "properties": {
          "app_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "app_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The most recently seen URL for this app name."
          },
          "requests": {
            "type": "integer"
          },
          "cost": {
            "type": "number"
          },
          "prompt_tokens": {
            "type": "integer"
          },
          "completion_tokens": {
            "type": "integer"
          },
          "last_used": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "description": "A JSON-RPC 2.0 request or notification. A notification (no `id`) never gets a response. The body may also be a non-empty array of these for a batch call.",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "id": {
            "description": "Present on a request, absent on a notification.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "method": {
            "type": "string",
            "description": "`initialize`, `notifications/initialized`, `ping`, `tools/list`, or `tools/call`."
          },
          "params": {
            "type": "object",
            "description": "Method-specific. `tools/call` takes `{ \"name\": string, \"arguments\": object }`."
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": [
          "jsonrpc",
          "id"
        ],
        "description": "A JSON-RPC 2.0 response: exactly one of `result` or `error`. The body is a single object for a single request, or an array of these in request order for a batch.",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "id": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "result": {
            "description": "Present on success. `tools/call` returns `{ content: [{ type: \"text\", text: string }], structuredContent: object }`, where `text` is the JSON-encoded form of `structuredContent`."
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "description": "Present on failure. `code` follows JSON-RPC's reserved ranges: -32700 parse error, -32600 invalid request, -32601 method not found, -32602 invalid params (includes an unknown tool name or a tool's own input validation), -32603 internal error, -32603..-32000 implementation-defined (auth, rate limit, or the underlying endpoint being unavailable, e.g. the legal index not configured).",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string",
                "description": "The same message the matching REST endpoint would put in `error.message`."
              },
              "data": {
                "type": "object",
                "description": "Present on an auth or rate-limit failure: `{ status: <the REST endpoint's HTTP status> }`."
              }
            }
          }
        }
      },
      "McpToolDescriptor": {
        "type": "object",
        "required": [
          "name",
          "description",
          "inputSchema"
        ],
        "properties": {
          "name": {
            "type": "string",
            "enum": [
              "search_texas_law",
              "search_rules",
              "check_citations",
              "check_support"
            ]
          },
          "description": {
            "type": "string"
          },
          "inputSchema": {
            "type": "object",
            "description": "A JSON Schema object: the same schema rendered as OpenAI `function.parameters` and Anthropic `input_schema` on the /rags page."
          }
        }
      },
      "McpDescription": {
        "type": "object",
        "description": "GET /api/mcp: a human- and crawler-readable description of the server, for discovery.",
        "properties": {
          "name": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "protocol": {
            "type": "string"
          },
          "endpoint": {
            "type": "string",
            "format": "uri"
          },
          "description": {
            "type": "string"
          },
          "auth": {
            "type": "string"
          },
          "pricing": {
            "type": "object",
            "properties": {
              "per_query_usd": {
                "type": "number",
                "description": "RAG_QUERY_PRICE_USD on this host. 0 during beta."
              },
              "label": {
                "type": "string",
                "description": "\"free during beta\", or \"$X.XX per query\"."
              }
            }
          },
          "tools": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  }
}