{
  "openapi": "3.1.0",
  "info": {
    "title": "OrchestKit Docs API",
    "version": "8.41.1",
    "summary": "Public, read-only API over the OrchestKit documentation.",
    "description": "Read-only API for the OrchestKit documentation site (111 skills, 37 agents, 210 hooks). No authentication is required — see https://orchestkit.yonyon.ai/auth.md. Errors use the RFC 9457 Problem Details shape (served as application/json) and reference the Problem schema. **Versioning:** this is API v1, also reachable under the path-versioned alias `/api/v1/*` (e.g. `/api/v1/search`). Breaking changes ship under a new `/api/vN` prefix; the unversioned path tracks the latest. **Deprecation policy:** nothing is deprecated today. When an endpoint is eventually deprecated, its responses will carry the RFC 8594 `Deprecation` and `Sunset` HTTP headers (and a `Link` header with `rel=\"deprecation\"` pointing at the migration notes); the endpoint keeps working until the date in `Sunset`. Breaking replacements ship under a new `/api/vN` prefix so existing integrations stay on their current version. **Rate limits:** 120 requests per minute per IP per endpoint, communicated via the IETF `RateLimit-*` response headers on every response; exceeding the window returns 429 with `Retry-After`. Cache responses and retry transient 5xx with exponential backoff. **Pagination:** list endpoints use opaque cursors — pass `limit`, then follow the `Link: rel=\"next\"` header (or `X-Next-Cursor`) until absent. **Idempotency:** every endpoint is a read and therefore idempotent; an `Idempotency-Key` request header is accepted and echoed back so clients can correlate retries. **SDKs & packages:** Python — `pip install orchestkit-hook-contract` (https://pypi.org/project/orchestkit-hook-contract/); MCP stdio surface — `docker run -i --rm ghcr.io/yonatangross/orchestkit-docs-mcp`. Full policy page: https://orchestkit.yonyon.ai/api-policy.md.",
    "license": {
      "name": "MIT",
      "url": "https://opensource.org/license/mit"
    },
    "contact": {
      "name": "OrchestKit",
      "url": "https://github.com/yonatangross/orchestkit"
    }
  },
  "servers": [
    {
      "url": "https://orchestkit.yonyon.ai",
      "description": "Production (latest)"
    },
    {
      "url": "https://orchestkit.yonyon.ai/api/v1",
      "description": "Production (v1 alias)"
    }
  ],
  "paths": {
    "/api/search": {
      "get": {
        "operationId": "searchDocs",
        "summary": "Full-text search across the documentation",
        "description": "Returns documentation pages matching a search term, ranked by relevance. Use this to find the right doc before fetching its Markdown. Paginate with `limit` + the opaque `cursor` from `X-Next-Cursor` (or the `Link: rel=\"next\"` header).",
        "tags": [
          "search"
        ],
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "Search term, e.g. 'install' or 'memory'.",
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size for cursor-based pagination. Omit for all results. The total before pagination is returned in the `X-Total-Count` response header.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque pagination cursor from a previous response's `X-Next-Cursor` header (or `Link: rel=\"next\"`). Omit for the first page. Cursor-based — page contents do not drift between requests.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "required": false,
            "description": "Filter by content type: docs | skill | agent | hook | composition.",
            "schema": {
              "type": "string",
              "enum": [
                "docs",
                "skill",
                "agent",
                "hook",
                "composition"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked search results (one page).",
            "headers": {
              "X-Total-Count": {
                "description": "Total matches before pagination was applied.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Next-Cursor": {
                "description": "Opaque cursor for the next page; absent on the last page.",
                "schema": {
                  "type": "string"
                }
              },
              "Link": {
                "description": "RFC 8288 web link to the next page (`rel=\"next\"`); absent on the last page.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SearchResult"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (RFC 9457 Problem Details body). Honor `Retry-After` before retrying.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/ask": {
      "get": {
        "operationId": "askQuestionGet",
        "summary": "Answer a natural-language question about OrchestKit (NLWeb, GET)",
        "description": "NLWeb endpoint, GET form. Pass the natural-language question as `?query=` (aliases: `q`, `question`). Returns matching documentation as structured JSON with a `_meta` block. Pass `?streaming=true` (or `Accept: text/event-stream`) to stream results as Server-Sent Events with NLWeb event types: start, result, complete.",
        "tags": [
          "ask"
        ],
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "The natural-language question.",
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "description": "NLWeb response mode.",
            "schema": {
              "type": "string",
              "enum": [
                "list",
                "summarize",
                "generate"
              ],
              "default": "list"
            }
          },
          {
            "name": "streaming",
            "in": "query",
            "required": false,
            "description": "Set to `true` for an SSE stream instead of a JSON document.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-chosen key for correlating retries. Every endpoint is a read (naturally idempotent); the key is echoed back in the response's `Idempotency-Key` header.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Structured answer with matching documentation.",
            "headers": {
              "Idempotency-Key": {
                "description": "Echo of the request's Idempotency-Key, when sent.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskResponse"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "SSE stream of NLWeb events: start, result, complete."
                }
              }
            }
          },
          "400": {
            "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (RFC 9457 Problem Details body). Honor `Retry-After` before retrying.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "askQuestion",
        "summary": "Answer a natural-language question about OrchestKit (NLWeb, POST)",
        "description": "NLWeb endpoint. Accepts a natural-language query (JSON or form-encoded body) and returns matching documentation as structured JSON (with a `_meta` block). Send `Accept: text/event-stream`, `streaming: true`, or `prefer.streaming: true` to stream results as Server-Sent Events.",
        "tags": [
          "ask"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-chosen key for correlating retries. Every endpoint is a read (naturally idempotent); the key is echoed back in the response's `Idempotency-Key` header.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AskRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/AskRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Structured answer with matching documentation.",
            "headers": {
              "Idempotency-Key": {
                "description": "Echo of the request's Idempotency-Key, when sent.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskResponse"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "SSE stream of NLWeb events: start, result, complete."
                }
              }
            }
          },
          "400": {
            "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (RFC 9457 Problem Details body). Honor `Retry-After` before retrying.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/md/batch": {
      "post": {
        "operationId": "batchGetMarkdown",
        "summary": "Fetch up to 20 documentation pages as Markdown in one request",
        "description": "Batch read: pass an array of doc URL paths (e.g. `/docs/getting-started/installation`) and receive each page's Markdown in a single response. Per-path failures are reported inline so one bad path does not fail the batch. Reads are idempotent; an `Idempotency-Key` header is echoed back. **Async-job pattern (RFC 7240):** send `Prefer: respond-async` (or `?async=1`) to receive `202 Accepted` with a `Location` header pointing at `/api/jobs/{jobId}`; poll that URL for the job status and result. Jobs are stateless and deterministic — they complete by the first poll.",
        "tags": [
          "docs"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-chosen key for correlating retries. Every endpoint is a read (naturally idempotent); the key is echoed back in the response's `Idempotency-Key` header.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchMarkdownRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-path results, in request order.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchMarkdownResponse"
                }
              }
            }
          },
          "202": {
            "description": "Async accepted (when `Prefer: respond-async` or `?async=1` is sent). `Location` points at the job status URL.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Location": {
                "description": "Job status URL (`/api/jobs/{jobId}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "accepted"
                      ]
                    },
                    "status_url": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "job_id",
                    "status",
                    "status_url"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (RFC 9457 Problem Details body). Honor `Retry-After` before retrying.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/jobs/{jobId}": {
      "get": {
        "operationId": "getJobStatus",
        "summary": "Async-job status + result",
        "description": "Poll target for the `Prefer: respond-async` flow on `POST /api/md/batch`. Job ids are stateless: they encode the deterministic read-only request, so this endpoint executes it idempotently and a valid job always reports `completed` with its result inline. Unknown or malformed ids return an RFC 9457 problem with status 404.",
        "tags": [
          "docs"
        ],
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "description": "Job id issued by the 202 response of `POST /api/md/batch`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job state; `completed` jobs carry the result inline.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "completed"
                      ]
                    },
                    "result": {
                      "$ref": "#/components/schemas/BatchMarkdownResponse"
                    }
                  },
                  "required": [
                    "job_id",
                    "status",
                    "result"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (RFC 9457 Problem Details body). Honor `Retry-After` before retrying.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Liveness check",
        "description": "Returns 200 with service name and version when serving.",
        "tags": [
          "meta"
        ],
        "responses": {
          "200": {
            "description": "Service is serving.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "examples": [
                        "ok"
                      ]
                    },
                    "service": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          },
          "503": {
            "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Client-chosen key for correlating retries. Every endpoint is a read (naturally idempotent); the key is echoed back in the response's `Idempotency-Key` header.",
        "schema": {
          "type": "string",
          "maxLength": 255
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Page size. Omit for all results. The total before pagination is returned in the `X-Total-Count` response header.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Opaque pagination cursor from a previous response's `X-Next-Cursor` header (or `Link: rel=\"next\"`). Omit for the first page. Cursor-based — page contents do not drift between requests.",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "RateLimit-Limit": {
        "description": "Requests allowed in the current window (per IP, per endpoint).",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests remaining in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the current window resets.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Policy": {
        "description": "Quota policy, e.g. \"120;w=60\" (120 requests per 60-second window).",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "SearchResult": {
        "type": "object",
        "description": "A single documentation search hit.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable result id."
          },
          "url": {
            "type": "string",
            "description": "Path to the doc page."
          },
          "type": {
            "type": "string",
            "description": "Match granularity (page | heading | text)."
          },
          "content": {
            "type": "string",
            "description": "Matching heading or text excerpt."
          }
        },
        "required": [
          "id",
          "url"
        ]
      },
      "NlwebResult": {
        "type": "object",
        "description": "NLWeb result: a documentation page answering the query. Carries both NLWeb-canonical fields (name, site, score, description) and this API's stable fields (id, title, url, content).",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable result id."
          },
          "name": {
            "type": "string",
            "description": "Page title (NLWeb-canonical)."
          },
          "title": {
            "type": "string",
            "description": "Page title."
          },
          "url": {
            "type": "string",
            "description": "Absolute URL of the doc page."
          },
          "site": {
            "type": "string",
            "description": "Origin site of the result."
          },
          "score": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Relevance score, 1 = best match."
          },
          "description": {
            "type": "string",
            "description": "Text excerpt (NLWeb-canonical)."
          },
          "content": {
            "type": "string",
            "description": "Text excerpt."
          }
        },
        "required": [
          "name",
          "url",
          "score"
        ]
      },
      "AskRequest": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "minLength": 1,
            "description": "The natural-language question (aliases: `q`, `question`)."
          },
          "mode": {
            "type": "string",
            "enum": [
              "list",
              "summarize",
              "generate"
            ],
            "default": "list",
            "description": "NLWeb response mode."
          },
          "streaming": {
            "type": "boolean",
            "default": false,
            "description": "Stream the response as Server-Sent Events."
          }
        },
        "required": [
          "query"
        ]
      },
      "AskResponse": {
        "type": "object",
        "properties": {
          "query_id": {
            "type": "string",
            "description": "Server-assigned id for this query."
          },
          "query": {
            "type": "string",
            "description": "The query as interpreted."
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NlwebResult"
            }
          },
          "_meta": {
            "type": "object",
            "description": "NLWeb metadata.",
            "properties": {
              "response_type": {
                "type": "string",
                "examples": [
                  "list"
                ]
              },
              "version": {
                "type": "string"
              },
              "count": {
                "type": "integer"
              }
            },
            "required": [
              "response_type",
              "version"
            ]
          }
        },
        "required": [
          "results",
          "_meta"
        ]
      },
      "BatchMarkdownRequest": {
        "type": "object",
        "properties": {
          "paths": {
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "items": {
              "type": "string"
            },
            "description": "Doc URL paths to fetch, e.g. /docs/getting-started/installation."
          }
        },
        "required": [
          "paths"
        ]
      },
      "BatchMarkdownResponse": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "description": "One entry per requested path, in request order.",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string"
                },
                "ok": {
                  "type": "boolean"
                },
                "title": {
                  "type": "string",
                  "description": "Present when ok."
                },
                "markdown": {
                  "type": "string",
                  "description": "Present when ok."
                },
                "status": {
                  "type": "integer",
                  "description": "Per-path error status; present when not ok."
                },
                "detail": {
                  "type": "string",
                  "description": "Present when not ok."
                }
              },
              "required": [
                "path",
                "ok"
              ]
            }
          }
        },
        "required": [
          "results"
        ]
      },
      "Problem": {
        "type": "object",
        "description": "RFC 9457 Problem Details.",
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string"
          }
        },
        "required": [
          "title",
          "status"
        ]
      }
    },
    "responses": {
      "AskOk": {
        "description": "Structured answer with matching documentation.",
        "headers": {
          "Idempotency-Key": {
            "description": "Echo of the request's Idempotency-Key, when sent.",
            "schema": {
              "type": "string"
            }
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AskResponse"
            }
          },
          "text/event-stream": {
            "schema": {
              "type": "string",
              "description": "SSE stream of NLWeb events: start, result, complete."
            }
          }
        }
      },
      "Problem": {
        "description": "Error using the RFC 9457 Problem Details shape, served as application/json.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded (RFC 9457 Problem Details body). Honor `Retry-After` before retrying.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying.",
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    }
  }
}