{
    "swagger": "2.0",
    "info": {
        "description": "Go + PocketBase backend for QuantumAtlas: paper\ncollection + search + database. Write endpoints and\nthe search/paper read surface require a bearer token\n(PAT or PocketBase session) plus the matching scope.\nSee the auth model docs for the scope vocabulary.",
        "title": "QuantumAtlas API",
        "contact": {
            "name": "QuantumAtlas",
            "url": "https://quantum-atlas.ai"
        },
        "version": "1.0"
    },
    "basePath": "/",
    "paths": {
        "/api/admin/assets/search": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Admin-only: find papers with at least one asset.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Admin"
                ],
                "summary": "Search papers with assets",
                "parameters": [
                    {
                        "type": "string",
                        "description": "search query",
                        "name": "q",
                        "in": "query",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/api/admin/assets/{paper_id}": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Admin-only asset listing: every PDF / markdown object the\npaper owns, with S3 object keys, sizes, SHA256s.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Admin"
                ],
                "summary": "List paper assets",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id (qa_...)",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/admin/assets/{paper_id}/{kind}/download": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Admin-only proxy stream with Content-Disposition: attachment.",
                "produces": [
                    "application/octet-stream"
                ],
                "tags": [
                    "Admin"
                ],
                "summary": "Download paper asset",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "pdf | markdown",
                        "name": "kind",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "file"
                        }
                    }
                }
            }
        },
        "/api/admin/assets/{paper_id}/{kind}/url": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Admin-only: time-limited presigned URL for direct browser-to-S3.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Admin"
                ],
                "summary": "Presigned asset URL",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "pdf | markdown",
                        "name": "kind",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "duration (default 1h, max 24h)",
                        "name": "ttl",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/api/admin/db/schema": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Read-only introspection of the QATLAS_POSTGRES_DSN database:\nevery table in schema public (goose_db_version included,\nalphabetical) with row estimate, total size, columns\n(type/nullable/default/is_pk), indexes and constraints.\nAdmin-only: session token + is_admin or is_superadmin\non the users record.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Admin"
                ],
                "summary": "Postgres schema browser",
                "responses": {
                    "200": {
                        "description": "{database, tables:[{name, row_estimate, total_size, columns, indexes, constraints}]}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "admin only",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "postgres registry unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/admin/devdoc/ticket": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns {url} — a short-lived signed link into the admin-only\ndev-docs site (/devdoc). Opening it sets an HttpOnly cookie and\nredirects to the ticket-free URL; the cookie authorizes the\ndocs for 12h. Admin-only: session token + is_admin or\nis_superadmin on the users record.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Admin"
                ],
                "summary": "Dev-docs ticket",
                "responses": {
                    "200": {
                        "description": "{url}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "admin only",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "dev docs not bundled",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/admin/users": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Every users record: {users:[{id, name, email, github_login,\ngitea_login, is_admin, is_superadmin, disabled, created,\nupdated}], total}.\nRequires a session AND one of: is_admin, is_superadmin\n(userAdminGuard — PATs rejected; the YAML admin lists are\nonly a boot-time seed for those flags).",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Admin"
                ],
                "summary": "List users",
                "responses": {
                    "200": {
                        "description": "{users, total}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "user management requires admin / PAT auth not accepted",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/admin/users/{id}": {
            "patch": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Body is a JSON object with any of {\"disabled\": bool,\n\"is_admin\": bool} — at least one key required. disabled needs\nadmin-or-above; is_admin needs the is_superadmin flag.\nSelf-protection: nobody may disable\nthemselves or change their own is_admin; admins cannot\ndisable superadmins. is_superadmin is not patchable here.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Admin"
                ],
                "summary": "Update user flags",
                "parameters": [
                    {
                        "type": "string",
                        "description": "users record id",
                        "name": "id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "the updated user record",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "empty or invalid body",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "role too low / self-protection",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "user not found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/admin/whoami": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns {login, is_admin, is_user_admin, is_superadmin}\nfor the signed-in session user. login is the github_login,\nfalling back to gitea_login when only that is stamped.\nAll three flags read the users-record role flags (the\nauth.admin_logins / superadmin_logins YAML lists only\nseed them at boot): is_admin and is_user_admin are true\nwhen the record holds is_admin or is_superadmin; the\nSPA uses them to decide which admin surfaces to render.\nNon-admins get false flags rather than a 403.\nSession-token auth only (PAT auth\nrefused, same as /api/pat).",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Admin"
                ],
                "summary": "Admin whoami",
                "responses": {
                    "200": {
                        "description": "{login, is_admin, is_user_admin, is_superadmin}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "PAT auth not accepted",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/discussions/{discussion_id}": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "The discussion plus the first (or cursor-continued) page\nof replies, oldest first, keyset-paginated by reply_id.\nThe ETag response header carries the body revision integer\nused for If-Match CAS edits.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Comments"
                ],
                "summary": "Discussion detail",
                "parameters": [
                    {
                        "type": "string",
                        "description": "cd_ discussion id",
                        "name": "discussion_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "keyset cursor (last reply_id)",
                        "name": "cursor",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "replies page size (default 20, max 100)",
                        "name": "per_page",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/discussions/{discussion_id}/body": {
            "patch": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Author-only edit (the caller must be the body's author —\nadmins do not edit other people's words). Requires\nIf-Match with the current revision integer; a stale value\nis a 409, the new body increments the revision and the\nold/new pair is appended to the revision history. Editing\nnever reopens the status.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Comments"
                ],
                "summary": "Edit discussion body",
                "parameters": [
                    {
                        "type": "string",
                        "description": "cd_ discussion id",
                        "name": "discussion_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "current revision integer (the ETag)",
                        "name": "If-Match",
                        "in": "header",
                        "required": true
                    },
                    {
                        "description": "{body}",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "missing/invalid If-Match",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "not the author",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "revision conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "413": {
                        "description": "Request Entity Too Large",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/discussions/{discussion_id}/replies": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Flat reply inside the discussion. Same identity/idempotency/\nbody-budget contract as discussion creation.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Comments"
                ],
                "summary": "Reply to a discussion",
                "parameters": [
                    {
                        "type": "string",
                        "description": "cd_ discussion id",
                        "name": "discussion_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "replay key",
                        "name": "Idempotency-Key",
                        "in": "header"
                    },
                    {
                        "description": "{body, model?}",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "201": {
                        "description": "Created",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "Conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "413": {
                        "description": "Request Entity Too Large",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/discussions/{discussion_id}/replies/{reply_id}/body": {
            "patch": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Same author-only CAS contract as the discussion body edit,\napplied to one reply of the discussion.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Comments"
                ],
                "summary": "Edit reply body",
                "parameters": [
                    {
                        "type": "string",
                        "description": "cd_ discussion id",
                        "name": "discussion_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "cr_ reply id",
                        "name": "reply_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "current revision integer",
                        "name": "If-Match",
                        "in": "header",
                        "required": true
                    },
                    {
                        "description": "{body}",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "not the author",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "revision conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "413": {
                        "description": "Request Entity Too Large",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/discussions/{discussion_id}/revisions": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Append-only trail of one discussion: body_revisions (the\ndiscussion's and its replies' edits: target / target_id /\nold_body / new_body / editor / created_at) and\nstatus_events (from / to / reason / actor / created_at),\nboth oldest-first.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Comments"
                ],
                "summary": "Discussion history",
                "parameters": [
                    {
                        "type": "string",
                        "description": "cd_ discussion id",
                        "name": "discussion_id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/discussions/{discussion_id}/status": {
            "patch": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Sets the status machine (pending / confirmed / retracted;\nreopen allowed). `reason` is mandatory and every change is\nappended to the status-event trail with the acting account.\nAllowed for the discussion's root author or a platform\nadmin (users-record is_admin / is_superadmin — a user PAT\nof an admin works; this is NOT the session-only admin\nconsole gate). System PATs and everyone else get 403.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Comments"
                ],
                "summary": "Change discussion status",
                "parameters": [
                    {
                        "type": "string",
                        "description": "cd_ discussion id",
                        "name": "discussion_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "{status, reason}",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "missing reason / bad status",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/downloader/fetch": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Parses each input line (DOI, arXiv id, or paper URL),\nresolve-or-mints it into the registry and enqueues the\nmulti-paradigm acquisition ladder (arXiv direct → OA\nAPIs → publisher URL patterns → landing page → LLM\nagent fallback). Every fetched body is validated (%PDF-\nmagic, trailer, size bounds, HTML-disguise classification)\nbefore it is stored and the MinerU pipeline is triggered.\nTrack progress via GET /api/downloader/jobs or the paper\ndetail acquisition block. Requires papers:write.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Downloader"
                ],
                "summary": "Robust download (enqueue)",
                "parameters": [
                    {
                        "description": "{items: [\\",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{items: [{input, kind, paper_id?, created, error?}], enqueued}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "downloader or registry not configured",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/downloader/jobs": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "In-process snapshot of downloader jobs (active + recent\nterminal): per-paper state/phase, the winning strategy,\nthe full attempt trace and healthz-style counters. The\nSPA's Robust Downloader page polls this every 2s while\njobs are active. Requires papers:read.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Downloader"
                ],
                "summary": "Downloader jobs",
                "responses": {
                    "200": {
                        "description": "{jobs: [...], counters: {queued, in_flight, succeeded, failed, skipped}}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/downloader/remote-jobs": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "PostgreSQL-persisted snapshot of outbound fleet tasks (most\nrecently updated first, capped at 500): id, worker_id, state\n(queued/running/staged/done/failed), identifier and error.\nUnlike /api/downloader/jobs this survives server restarts.\nAnswers 200 with {enabled: false, jobs: []} when the remote\nfleet is disabled. Requires papers:read.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Downloader"
                ],
                "summary": "Downloader remote jobs",
                "responses": {
                    "200": {
                        "description": "{enabled: bool, jobs: [{id, worker_id, state, identifier, error?, updated_at}]}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/health": {
            "get": {
                "description": "Liveness plus parallel dependency probes (rawstore, postgres,\nregistry). HTTP status is always 200; read data.status for the\nreal verdict (\"healthy\" | \"degraded\").",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "System"
                ],
                "summary": "Health check",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/api/me": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns the signed-in user's profile:\n{id, email, name, avatar, github_login, gitea_login,\ngithub_bound, gitea_bound, is_admin, is_superadmin,\ncreated}.\navatar is the PocketBase file name — build the URL via the\nPocketBase files API. *_bound reports which OAuth2\nidentities are linked to the record (the dashboard's\naccount-binding panels).",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Me"
                ],
                "summary": "My profile",
                "responses": {
                    "200": {
                        "description": "{id, email, name, avatar, github_login, gitea_login, github_bound, gitea_bound, is_admin, is_superadmin, created}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "PAT auth not accepted",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/me/search-keys": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "The caller's per-user third-party search API key inventory\n(backend name, masked hint, updated_at — never the key\nmaterial). enabled=false means the server has no encryption\nsecret configured and the whole feature is off. Session-only\n(same reasoning as /api/pat: a leaked PAT must not read the\nowner's third-party keys).",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Me"
                ],
                "summary": "List my search API keys",
                "responses": {
                    "200": {
                        "description": "{enabled: bool, keys: [{backend, hint, updated_at}]}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "PAT auth not accepted",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/me/search-keys/{backend}": {
            "put": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Upserts the caller's API key for one backend (AES-256-GCM\nencrypted at rest; injected into multi/agentic search calls\non the caller's behalf). The backend must be a catalog\nbackend with a user-key slot. 503 when the server has no\nencryption secret configured. Session-only.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Me"
                ],
                "summary": "Store my search API key",
                "parameters": [
                    {
                        "type": "string",
                        "description": "backend name (e.g. ieee, tavily)",
                        "name": "backend",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "{key: string}",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{ok: true}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "boolean"
                            }
                        }
                    },
                    "400": {
                        "description": "unknown backend / no user-key slot / empty key",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "PAT auth not accepted",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "feature disabled",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            },
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Removes the caller's stored key for one backend (opaque 404\nwhen absent). Session-only.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Me"
                ],
                "summary": "Delete my search API key",
                "parameters": [
                    {
                        "type": "string",
                        "description": "backend name",
                        "name": "backend",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{ok: true}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "boolean"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "PAT auth not accepted",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "key not found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "feature disabled",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/me/usage": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns {metric, today, limit, llm_tokens} for the\nagentic-search surface: today's call count, the caller's\neffective daily limit (per-user override \u003e plan \u003e\nsearch.agentic.daily_limit) and the LLM tokens consumed\ntoday. Same metering shape as the agentic response's usage\nblock.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Me"
                ],
                "summary": "My usage",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/routes.meUsageResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "PAT auth not accepted",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "postgres registry unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/oauth/device/approve": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Session-only (PAT auth rejected). Marks the pending request\nas approved so the next /token poll mints the PAT bound to\nthe approving user.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "OAuth Device"
                ],
                "summary": "Approve a device request",
                "parameters": [
                    {
                        "description": "user_code from /\u003clang\u003e/device",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/routes.oauthDeviceUserCodeBody"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{\\\"status\\\":\\\"approved\\\"}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/oauth/device/code": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Session-only (PAT auth rejected). Returns the PAT spec\n(name, description, scopes, expires_in_days) tied to a\npending user_code so the /\u003clang\u003e/device page can show the\nuser what they're about to approve.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "OAuth Device"
                ],
                "summary": "Look up pending device request",
                "parameters": [
                    {
                        "type": "string",
                        "description": "short user code (with or without dashes)",
                        "name": "user_code",
                        "in": "query",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/routes.oauthDeviceLookupResponse"
                        }
                    },
                    "400": {
                        "description": "invalid user_code",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "not found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            },
            "post": {
                "description": "RFC 8628 §3.1. Anonymous (no auth) — the CLI uses this to\nobtain a device_code (kept secret, polled on /token) and a\nshort user_code that the human enters on the SPA's\n/\u003clang\u003e/device page after authenticating.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "OAuth Device"
                ],
                "summary": "Start device authorization",
                "parameters": [
                    {
                        "description": "PAT spec to mint after approval",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/routes.oauthDeviceCodeRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/routes.oauthDeviceCodeResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/oauth/device/deny": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Session-only (PAT auth rejected). Marks the pending request\nas denied so the next /token poll returns \"access_denied\".",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "OAuth Device"
                ],
                "summary": "Deny a device request",
                "parameters": [
                    {
                        "description": "user_code from /\u003clang\u003e/device",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/routes.oauthDeviceUserCodeBody"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{\\\"status\\\":\\\"denied\\\"}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/oauth/device/token": {
            "post": {
                "description": "RFC 8628 §3.4 + §3.5. Anonymous. The CLI polls this with\ndevice_code at the published interval until the server\nreturns the minted PAT plaintext (success) or an error\nstring (\"authorization_pending\", \"slow_down\", \"expired_token\",\n\"access_denied\", \"invalid_grant\"). HTTP status is always\n400 on errors per the RFC so the CLI can switch on `error`.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "OAuth Device"
                ],
                "summary": "Poll for PAT after device approval",
                "parameters": [
                    {
                        "description": "device_code from /code",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/routes.oauthDeviceTokenRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "minted PAT plaintext (returned exactly once)",
                        "schema": {
                            "$ref": "#/definitions/routes.oauthDeviceTokenResponse"
                        }
                    },
                    "400": {
                        "description": "RFC 8628 error string",
                        "schema": {
                            "$ref": "#/definitions/routes.oauthDeviceTokenError"
                        }
                    }
                }
            }
        },
        "/api/papers": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Paginated list of registry papers (merged tombstones\nexcluded). has_md filters on converted markdown present on\nthe paper's default asset (the \"converted papers\" page);\nstatus filters by lifecycle; q is a case-insensitive title\nsubstring; arxiv_id / doi / paper_id are exact-identity\nfilters (arxiv_id may carry a version suffix and doi a\nURL prefix — both are canonicalized before matching).\nSorted by created_at (default) or updated_at,\ndescending. Requires the papers:read scope.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "List papers",
                "parameters": [
                    {
                        "type": "boolean",
                        "description": "true = only papers with converted markdown on the default asset",
                        "name": "has_md",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "pending | ready | failed",
                        "name": "status",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "title substring (case-insensitive)",
                        "name": "q",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "exact arXiv id filter (version suffix optional)",
                        "name": "arxiv_id",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "exact DOI filter (URL prefix tolerated)",
                        "name": "doi",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "exact surrogate paper id (qa_...)",
                        "name": "paper_id",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "1-based page (default 1)",
                        "name": "page",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "page size (default 20, max 100)",
                        "name": "per_page",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "created_at (default) | updated_at, descending",
                        "name": "sort",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{items:[{paper_id,arxiv_id,doi,title,status,has_pdf,has_md,image_count,created_at,updated_at}], total, page, per_page}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "invalid query parameter",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "registry unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/lookup": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Resolves comma-separated namespaced `kind:id` refs\n(arxiv:… / openalex:… / doi:…) against the local OpenAlex\ncorpus. Returns per-ref {ref,title,authors,year,hosted,\nhas_md,resolved} plus corpus_available — hosted reports\nwhether QuantumAtlas hosts the ref, and has_md (meaningful\nonly when hosted) whether its default asset carries\nconverted markdown, so batch consumers learn both facts in\none call (≤200 refs per batch). Exact-by-id only; fuzzy\nsearch is a separate deferred capability.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Resolve references (batch, exact)",
                "parameters": [
                    {
                        "type": "string",
                        "description": "comma-separated kind:id refs",
                        "name": "ids",
                        "in": "query",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/match": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Decides whether papers (qatlas-id / DOI / arXiv id /\nOpenAlex id / paper URL / title) are already inside the\nqatlas registry and returns the unified qa_… id.\nPrecision-first: identifiers match exactly after strict\nnormalization (trim / lowercase / known URL prefix /\narXiv vN); titles match only when EVERY word matches\n(case-insensitive, order-insensitive). Merged papers\nresolve to the surviving paper. Free-form `inputs` are\nauto-detected per entry; the typed fields force a kind;\nauthor/year narrow ambiguous title matches.\nUpstream failures are never reported as empty matches: HTTP 504\ndenotes timeout; HTTP 502 includes a stable code distinguishing\ntransport, upstream HTTP and invalid response failures. Only the\nnumeric upstream_status is exposed, never private upstream bodies.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Match paper identities against the registry",
                "parameters": [
                    {
                        "description": "{inputs: [string], doi?, arxiv_id?, openalex_id?, qatlas_id?, url?, title?, author?, year?}",
                        "name": "request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{results: [{input, kind, normalized, matched, qatlas_id, method, ambiguous, paper, candidates, truncated, reason}]}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "502": {
                        "description": "code, detail, optional numeric upstream_status",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "503": {
                        "description": "match backend disabled",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "504": {
                        "description": "match_upstream_timeout; no match result available",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/api/papers/needs-mineru": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Papers needing MinerU",
                "parameters": [
                    {
                        "type": "integer",
                        "description": "max results",
                        "name": "limit",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/source-register": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Requires papers:write, registry/object storage and pdftotext. Accepts only public HTTPS URLs; every redirect and DNS destination is validated and pinned. The PDF title must match first-page front matter. No DOI is synthesized and distinct URLs are not title-merged. IACR ePrint landing/PDF forms share one eprint identity. Same work and SHA reuse an immutable source; changed bytes create a new source. No parse or Markdown is fabricated. HTTP Idempotency-Key response replay is not promised: check server state after a transport failure. At most two registrations acquire PDFs concurrently in one process; excess requests receive 503 with Retry-After before acquisition.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Register external PDF original",
                "parameters": [
                    {
                        "description": "source_url HTTPS original, title (max 2000 UTF-8 bytes), authors (1..100, max 500 bytes each), year (1..9999)",
                        "name": "request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/routes.externalSourceRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{paper_id,created,external_id,source_url,source:{source_id,origin,sha256,size_bytes,created_at,source_url,retrieved_url,retrieved_at,pdf_endpoint}}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "invalid JSON, bibliographic fields or URL",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "requires papers:write",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "acquisition, size/format or title/provenance rejected",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "500": {
                        "description": "immutable storage/integrity or registration failure",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "catalog/storage unavailable or registration busy (Retry-After)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/stats": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Counts of PDFs, markdown, JSON and mineru-pending papers from\nthe S3 paper index. Returns {available:false} when no S3\nbackend is configured.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Paper statistics",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/status/batch": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Folds the .../markdown/status agent-decision surface into\none request: for each comma-separated id (qa_ surrogate,\narXiv id, or DOI — de-duplicated, max 200) the entry\ncarries requested_id, the resolved serving id (bare\narXiv inputs pin to the highest ingested asset version),\nmd_ready / pdf_ready store probes, the converter\nstate machine's state/phase labels, the default asset's\nimage_count, and an error field (\"not found\" for ids the\nregistry does not host; \"catalog unavailable\" when\nPostgreSQL is down — per-entry, never failing the batch).",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Batch paper asset status",
                "parameters": [
                    {
                        "type": "string",
                        "description": "comma-separated paper ids (qa_... | arXiv id | DOI; max 200)",
                        "name": "ids",
                        "in": "query",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{results:[{requested_id, resolved_id, md_ready, pdf_ready, image_count, phase, state, error}]}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "missing or over-limit ids list",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{arxiv_id}/mineru-claim": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Same claim_id/exact-source locator contract as /api/v1/papers/{id}/mineru-lease. Gate on: authenticated same-origin pdf_url with source_id,pdf_requires_auth:true,verifiedSHA; gate off: external arXiv URL+catalogSHA only. No NAS/S3 presigns or user authentication forwarding to providers.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Claim MinerU processing",
                "parameters": [
                    {
                        "type": "string",
                        "description": "arXiv identifier",
                        "name": "arxiv_id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "201": {
                        "description": "claim granted (body is the claim record)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "invalid arxiv_id",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "not claimable (no PDF in catalog, or markdown already exists)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "already claimed by someone else (body includes existing claim details)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "500": {
                        "description": "internal error",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "catalog unavailable (PostgreSQL unreachable)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{arxiv_id}/mineru-claim/{claim_id}": {
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Release MinerU claim",
                "parameters": [
                    {
                        "type": "string",
                        "description": "arXiv identifier",
                        "name": "arxiv_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "claim id",
                        "name": "claim_id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "204": {
                        "description": "claim released (empty body)"
                    },
                    "400": {
                        "description": "invalid arxiv_id",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "claim_id does not match the active claim",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "500": {
                        "description": "internal error",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "catalog unavailable (PostgreSQL unreachable)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{arxiv_id}/upload-mineru": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Requires a COMPLETE supported original Middle AND markdown.md/full.md. Supported profiles are docvortex.middle/schema_version 2.0 and genuine native pdf_info[] layout.json (mineru.native.middle/pdf_info-v1); producer engine version is not the structural schema version. MD-only, ContentList-only, incomplete or invalid schema packages are 422. All valid producer-original relative paths/names/bytes, including every JSON and unknown file, are retained; originals are never renamed or reserialized. Traversal, absolute/backslash paths, duplicate names, symlinks, CRC/ZIP errors fail the whole archive. Limits: ZIP/member 128MiB, expanded aggregate 256MiB, at most 10000 files.\nWrites create-only revision-scoped files into the new content bucket, verifies frozen exact PDF and every persisted member, writes separate manifest LAST, then publishes through PG (required). Every reupload is a new immutable revision/current publication; overwrite never changes historical revisions. Raw ZIP retention is optional; verified members+manifest are mandatory. No deferred-success indexing.\n\nThe {arxiv_id} slot also accepts a DOI for a published edition's exact already-uploaded PDF. Both paths share source-bound immutable bundle publication; there is no title/authors metadata override. PDF contribution establishes DOI metadata (see upload-pdf). A tier label records producer/request metadata, not proof of an executed provider quality mode.",
                "consumes": [
                    "multipart/form-data"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Upload paper MinerU bundle (arXiv id or DOI)",
                "parameters": [
                    {
                        "type": "string",
                        "description": "arXiv identifier (with vN) OR DOI (10.x/...)",
                        "name": "arxiv_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "boolean",
                        "description": "legacy input accepted; never replaces a frozen source or published revision",
                        "name": "overwrite",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "client-computed zip sha256 (in-transit integrity check)",
                        "name": "expected_sha256",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "sha256 of the exact frozen PDF parsed; cross-checked before publication",
                        "name": "pdf_sha256",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "legacy input; DOI metadata belongs to PDF contribution, not a parse metadata override",
                        "name": "verify",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "producer/contributor run label",
                        "name": "source",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "exact source id under this paper; source/version must match",
                        "name": "source_id",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "producer metadata label; default standard, not legacy model_version",
                        "name": "tier",
                        "in": "query"
                    },
                    {
                        "type": "file",
                        "description": "complete supported Middle + Markdown ZIP; all original members retained",
                        "name": "mineru_zip",
                        "in": "formData",
                        "required": true
                    }
                ],
                "responses": {
                    "201": {
                        "description": "{paper_id,source_id,source_sha256,revision_id,revision,is_current,tier,schema,schema_version,artifact_sha256,manifest_sha256,manifest,read_endpoint,blocks_endpoint}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "malformed upload or source PDF SHA mismatch",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "404": {
                        "description": "upload exact source PDF first",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "409": {
                        "description": "source/version identity conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "413": {
                        "description": "multipart ZIP byte cap exceeded",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "422": {
                        "description": "unsupported/incomplete package or integrity failure",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "503": {
                        "description": "PG/object store unavailable; no deferred success",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/api/papers/{arxiv_id}/upload-pdf": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Freeze exact PDF bytes into the new content bucket and publish the alias/source identity through PostgreSQL (PG required, no deferred-success indexing). 200 for the same bound alias/SHA, 201 for a new frozen binding. The identifier alias is permanently bound to the first successful exact PDF: different bytes return 409 EVEN with overwrite=true. Same paper/SHA may reuse a source ID; semantic arXiv vN is not S3VersionId. Never mutate an old source or parse revision.\n\nThe {arxiv_id} slot also accepts a DOI (`10.\u003cregistrant\u003e/\u003csuffix\u003e`)\nfor contributing a *published* version that may have no arXiv\npreprint. DOI PDF bytes also use immutable content/{paper}/{source}/source.pdf;\nits canonical import alias is separate from arXiv vN. The server resolves the DOI's title / authors /\nlinked arxiv id from OpenAlex — the contributor cannot supply\nthat metadata. The result is reported in `X-QAtlas-Verification`\nand the JSON `verification` block; `verify=strict` rejects a\ndoi-not-found with 409 (or metadata-unavailable / unconfigured\nwith 503).",
                "consumes": [
                    "multipart/form-data"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Upload paper PDF (arXiv id or DOI)",
                "parameters": [
                    {
                        "type": "string",
                        "description": "arXiv identifier (with vN) OR DOI (10.x/...)",
                        "name": "arxiv_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "boolean",
                        "description": "legacy input accepted; never replaces a frozen source or published revision",
                        "name": "overwrite",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "client-computed PDF sha256 (in-transit guard)",
                        "name": "expected_sha256",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "DOI only: 'strict' rejects when OpenAlex cannot resolve the DOI (default warn)",
                        "name": "verify",
                        "in": "query"
                    },
                    {
                        "type": "file",
                        "description": "PDF file",
                        "name": "pdf",
                        "in": "formData",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "unchanged",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "201": {
                        "description": "created",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "409": {
                        "description": "frozen alias conflict, including overwrite=true",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "413": {
                        "description": "PDF byte cap exceeded",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "422": {
                        "description": "frozen source integrity failure",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "503": {
                        "description": "PG/object store unavailable; no deferred success",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/api/papers/{id_or_doi}/images/zip": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Requires paper_access.enabled and papers:read for every identity. Uses verified original members of a complete immutable bundle; legacy image buckets/ZIPs are ignored. Preserves producer-relative member names/bytes. No-pin missing content may return 202 and start lazy parsing; poll the shared pure read/status until ready then re-GET pinned source/revision. Does not expose NAS/S3 presigns.",
                "produces": [
                    "application/zip"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Get source/revision-pinned images ZIP",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "id_or_doi",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "exact source; mutually exclusive with version",
                        "name": "source_id",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "exact semantic arXiv vN",
                        "name": "version",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "complete immutable parse revision",
                        "name": "revision",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "images ZIP assembled from verified original members",
                        "schema": {
                            "type": "file"
                        }
                    },
                    "202": {
                        "description": "source-specific parse pending",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        },
                        "headers": {
                            "Operation-Location": {
                                "type": "string",
                                "description": "pure readiness poll URL"
                            },
                            "Retry-After": {
                                "type": "string",
                                "description": "poll delay in seconds"
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "disabled or exact pins/members missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "Conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "Unprocessable Entity",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{id_or_doi}/markdown": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Requires paper_access.enabled and papers:read for canonical IDs and aliases. Uses a verified complete immutable bundle of the exact frozen source, not legacy MD/JSON/images. Original Markdown bytes are preserved (this is not /read's derived JSON). Missing no-pin content may start lazy parsing and return 202; explicit pins never repair/fall back. Only content access triggers inference, never nightly/boot/ingest.\nformat=link returns an authenticated same-origin /parses/{revision}/files/{original_path} locator, not a private NAS/S3 presign. Every follow-up download remains gated/authenticated. Poll Operation-Location read/status until verified ready, then re-GET with source/revision pins.",
                "produces": [
                    "text/plain"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Get source/revision-pinned Markdown",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "id_or_doi",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "exact frozen source; mutually exclusive with version",
                        "name": "source_id",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "exact semantic arXiv vN",
                        "name": "version",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "immutable complete parse revision",
                        "name": "revision",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "bytes (default) or authenticated internal link",
                        "name": "format",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "original Markdown bytes or source/revision-pinned link JSON",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "202": {
                        "description": "pending source-specific parsing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        },
                        "headers": {
                            "Operation-Location": {
                                "type": "string",
                                "description": "pure readiness poll URL"
                            },
                            "Retry-After": {
                                "type": "string",
                                "description": "poll delay in seconds"
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "disabled or exact pins/members missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "source/revision conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "immutable content integrity failure",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "backend or parser unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{id_or_doi}/markdown/status": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Same gate/auth/pure verified bundle readiness as /read/status. Never downloads or starts parsing. Historical MD/Middle or process Done alone is not readiness. Returns top-level source_id/revision pins and ready/state; pending may be 202 with Operation-Location and Retry-After. Compatibility flags may additionally include pdf_ready/md_ready.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Get complete Markdown readiness status",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "id_or_doi",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "exact frozen PDF source",
                        "name": "source_id",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "immutable complete parse revision",
                        "name": "revision",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "source/revision-pinned readiness payload",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "202": {
                        "description": "source-specific operation pending",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        },
                        "headers": {
                            "Operation-Location": {
                                "type": "string",
                                "description": "pure readiness poll URL"
                            },
                            "Retry-After": {
                                "type": "string",
                                "description": "poll delay in seconds"
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "disabled or exact pins missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "Conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "Unprocessable Entity",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{id_or_doi}/pdf": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Requires paper_access.enabled and papers:read for every ID, including qa_ aliases and Range. Disabled returns 404, not 410. Direct access does not require source-list rows first: a legacy PDF may be lazily frozen; no old MD/JSON/images are copied. Stored PDF retrieval requires no MinerU token or parsing. Frozen missing/corrupt bytes fail closed, never fall back to legacy/current/latest bytes.\nsource_id and version are mutually exclusive exact pins. arXiv vN is semantic source origin, not S3VersionId. Same paper/SHA may reuse a source ID. Explicit pin mismatch or ambiguity never substitutes a source/version. Full response SHA headers describe the whole PDF, not a partial Range.",
                "produces": [
                    "application/pdf"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Get source-pinned paper PDF",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "id_or_doi",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "exact source PDF id; mutually exclusive with version",
                        "name": "source_id",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "exact semantic arXiv version vN; not an S3 VersionId",
                        "name": "version",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "bytes (default) or link: same-origin authenticated pinned PDF locator, never bucket presign",
                        "name": "format",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "standard bytes range; same authentication as full GET",
                        "name": "Range",
                        "in": "header"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "source PDF original bytes",
                        "schema": {
                            "type": "file"
                        },
                        "headers": {
                            "X-QAtlas-PDF-SHA256": {
                                "type": "string",
                                "description": "whole PDF SHA-256"
                            },
                            "X-QAtlas-Paper-Id": {
                                "type": "string",
                                "description": "canonical paper id"
                            },
                            "X-QAtlas-Resolved-Id": {
                                "type": "string",
                                "description": "resolved canonical identity"
                            },
                            "X-QAtlas-Sha256": {
                                "type": "string",
                                "description": "whole PDF SHA-256"
                            },
                            "X-QAtlas-Source-Id": {
                                "type": "string",
                                "description": "immutable PDF source id"
                            },
                            "X-QAtlas-Source-Origin": {
                                "type": "string",
                                "description": "semantic origin, e.g. arxiv:v2"
                            }
                        }
                    },
                    "206": {
                        "description": "authenticated partial content",
                        "schema": {
                            "type": "file"
                        }
                    },
                    "400": {
                        "description": "malformed or mutually exclusive query pins",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "paper access disabled or exact source/version/bytes missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "ambiguous source or identity conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "416": {
                        "description": "invalid byte range",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "frozen PDF integrity mismatch",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "registry/store unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{id_or_doi}/pdf/status": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Side-effect-free probe reporting the pdf_ready /\nmd_ready booleans for a paper. This compatibility probe\ndoes not start parsing; direct /pdf now serves authenticated\nimmutable bytes. The body carries no bucket/presigned URL. The id\nmay be an arXiv id, a DOI, or a qa_ paper_id. Only\nregistered when QATLAS_PAPER_ACCESS_ENABLED=true.\n\nCanonical resolution: same DOI-wins rule as\n/api/papers/{id_or_doi}/markdown. Pass `?force_arxiv=1`\nto query the arxiv-side status instead.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Get PDF fetch status",
                "parameters": [
                    {
                        "type": "string",
                        "description": "arXiv canonical id or DOI",
                        "name": "id_or_doi",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "1/true: bypass DOI-canonical default",
                        "name": "force_arxiv",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "status payload (state ∈ cached|queued|running|none|failed|unavailable)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "asset storage unavailable; retryable=true with Retry-After",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/api/papers/{id}/figures": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Requires paper_access.enabled and papers:read for every ID including qa_. Uses original Markdown and manifest image members from one verified complete bundle. Member URLs target authenticated immutable /parses/{revision}/files/{original_path}, never flattened hashes or NAS presigns. No-pin missing content may be 202; poll shared read/status then re-GET source/revision pinned.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Get source/revision-pinned figures index",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "exact source; mutually exclusive with version",
                        "name": "source_id",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "exact semantic arXiv vN",
                        "name": "version",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "complete immutable revision",
                        "name": "revision",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{paper_id,resolved_id,source_id,revision,markdown_ready,figures,unmatched_images,image_count}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "202": {
                        "description": "source-specific parse pending",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        },
                        "headers": {
                            "Operation-Location": {
                                "type": "string",
                                "description": "pure readiness poll URL"
                            },
                            "Retry-After": {
                                "type": "string",
                                "description": "poll delay in seconds"
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "disabled or exact pins/members missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "Conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "Unprocessable Entity",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{id}/images/{name}": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Requires paper_access.enabled and papers:read for every identity. Resolves the original producer-relative image path inside one complete immutable bundle, without flattening names or guessing another revision. No-pin unready content may return202; poll shared read/status then re-GET fixed source/revision. Downloads remain authenticated and private/no-cache; no public NAS presigns. Exact pins/missing members never fall back.",
                "produces": [
                    "application/octet-stream"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Get source/revision-pinned image",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "original relative image name/path from figures or manifest",
                        "name": "name",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "exact source; mutually exclusive with version",
                        "name": "source_id",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "exact semantic arXiv vN",
                        "name": "version",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "complete immutable revision",
                        "name": "revision",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "verified original image bytes",
                        "schema": {
                            "type": "file"
                        }
                    },
                    "202": {
                        "description": "source-specific parse pending",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        },
                        "headers": {
                            "Operation-Location": {
                                "type": "string",
                                "description": "pure readiness poll URL"
                            },
                            "Retry-After": {
                                "type": "string",
                                "description": "poll delay in seconds"
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "disabled or exact pins/member missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "Conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "Unprocessable Entity",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{id}/read": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Requires paper_access.enabled and papers:read. This is derived Markdown-reading JSON, NOT raw Middle or Structured Content. Returns paper_id/source_id/revision/source_sha256/artifact_sha256/bundle_sha256?/renderer/format/content, request_scope, content_ranges, truncated, next_request and warnings?. page and block are 1-based exact producer indexes (not array offsets); block requires page. limit is a Unicode content budget, default 30000, max 100000.\nAn opaque cursor pins canonical paper/source/revision, PDF/Middle/bundle hashes, renderer and selection. Cursor-only resolves pins BEFORE selecting current; conflicting explicit pins/selectors are 409. Pinned failures never trigger repair, reparse, fallback or a current/latest substitution. Only content GET without a fixed revision/cursor may parse the selected verified source. source_id pins PDF, not a parse revision; absent PDF acquisition requires no source/revision/cursor pin. Old MD/JSON/images are ignored; legacy PDFs may be lazily frozen without deleting old storage.\nPending work returns 202 + Operation-Location=/api/papers/{canonical}/read/status?source_id=S once the source is known (otherwise retains the exact requested DOI/arXiv identity), plus Retry-After. Poll status without starting another job; only verified complete bundle readiness, not process Done or historical Middle existence, permits re-GET with fixed source/revision.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Read paper content with immutable pins and continuation",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "exact PDF source id; mutually exclusive with version",
                        "name": "source_id",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "exact semantic arXiv vN source pin",
                        "name": "version",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "immutable parse revision pin",
                        "name": "revision",
                        "in": "query"
                    },
                    {
                        "minimum": 1,
                        "type": "integer",
                        "description": "1-based page number",
                        "name": "page",
                        "in": "query"
                    },
                    {
                        "minimum": 1,
                        "type": "integer",
                        "description": "1-based exact producer block index; requires page",
                        "name": "block",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "opaque next_request.cursor; pinned revision loaded before current",
                        "name": "cursor",
                        "in": "query"
                    },
                    {
                        "maximum": 100000,
                        "minimum": 1,
                        "type": "integer",
                        "default": 30000,
                        "description": "Unicode character budget",
                        "name": "limit",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Middle-derived reading envelope; truncated=true includes next_request.cursor",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "202": {
                        "description": "source-pinned operation pending",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        },
                        "headers": {
                            "Operation-Location": {
                                "type": "string",
                                "description": "pure status poll URL"
                            },
                            "Retry-After": {
                                "type": "string",
                                "description": "poll delay in seconds"
                            }
                        }
                    },
                    "400": {
                        "description": "malformed query/cursor or invalid bounds",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "paper access disabled or exact pins/members missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "pin/selection/renderer/hash identity conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "frozen bytes or bundle integrity failure",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "backend or parser unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{id}/read/status": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Pure status request: never calls EnsureSource, downloads or starts parsing. Gate and auth match /read. Verifies frozen source and the published complete manifest/members. Historical Middle or a process Done flag is not readiness. Returns top-level paper_id/source_id/source_sha256/revision/state/ready; ready is 200, queued/running/pending may be 202 with the same Operation-Location and Retry-After. Explicit pins never fall back.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Poll source-specific paper reading readiness",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "exact PDF source id",
                        "name": "source_id",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "immutable parse revision pin",
                        "name": "revision",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{paper_id,source_id,source_sha256,revision,state,ready,md_ready,pdf_ready,read_url,markdown_url}; cached|failed|none",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "202": {
                        "description": "{paper_id,source_id,source_sha256,revision,state,ready:false}; pending|queued|running",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        },
                        "headers": {
                            "Operation-Location": {
                                "type": "string",
                                "description": "same status URL"
                            },
                            "Retry-After": {
                                "type": "string",
                                "description": "poll delay in seconds"
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "disabled or specified pins missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "source/revision identity conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "bundle/source integrity failure",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "backend unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{paper_id}": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns the registry paper (status, identities) plus its\nasset rows. The id is the surrogate paper_id (\"qa_\" +\nULID), an arXiv id (new or old style, with or without a\nvN suffix), or a DOI — identifier forms resolve against\nthe registry; a valid identifier the server does not\nhost answers 404 pointing at GET /api/papers/lookup for\nmetadata resolution.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Get paper by id",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "registry unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{paper_id}/discussions": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Issue-style discussion list for one paper, filtered by\nanchor (parse_revision / page_idx / block_index), content\nscope, type and status. Ordering is newest-first; the\ncursor is the last discussion_id of the page (keyset).\nscope=lean returns public+lean (the Lean view includes\nshared public discussion); status=none selects statusless\nnotes. per_page defaults to 20, max 100. Requires\npapers:read or comments:read; system PATs may read.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Comments"
                ],
                "summary": "List block discussions",
                "parameters": [
                    {
                        "type": "string",
                        "description": "qa_ paper id",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "public|lean (lean includes public)",
                        "name": "scope",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "type slug, e.g. transcription_error",
                        "name": "type",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "pending|confirmed|retracted|none",
                        "name": "status",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "parse revision id",
                        "name": "parse_revision",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "0-based page index",
                        "name": "page_idx",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "1-based public block index",
                        "name": "block_index",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "keyset cursor (last discussion_id)",
                        "name": "cursor",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "page size (default 20, max 100)",
                        "name": "per_page",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "comment store unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            },
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Creates a root discussion anchored at (parse_revision,\npage_idx, block_index) of the paper. actor and timestamps\nare recorded server-side; the optional `model` string is a\npure client declaration. status may be set at creation\n(then `reason` is required) or omitted for a statusless\nnote. Bodies are capped at comments.max_body_chars (default\n20000 Unicode chars, 413 beyond, never truncated); requests\nare capped at 1 MiB. Anchor misses 404 — never a\nnearest-block fallback. Idempotency-Key: same key + same\nrequest replays the original result; different request\n409s. Requires a user identity (session or user PAT) with\ncomments:write — system PATs are read-only and get 403.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Comments"
                ],
                "summary": "Create a discussion",
                "parameters": [
                    {
                        "type": "string",
                        "description": "qa_ paper id",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "replay key (SHA-256 of method+path+body compared)",
                        "name": "Idempotency-Key",
                        "in": "header"
                    },
                    {
                        "description": "{parse_revision, page_idx, block_index, type?, scope?, status?, reason?, body, model?}",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "201": {
                        "description": "Created",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "system PAT / missing comments:write",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "anchor block not found in parse revision",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "idempotency key reused with different body",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "413": {
                        "description": "body / request over limit",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{paper_id}/images": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Requires paper_access.enabled and papers:read for every identity. Original producer-relative names, SHA and authenticated revision/member URLs come from a verified manifest, never old image buckets. No-pin unready content may return202; only content GET can trigger lazy parsing. Metadata registry listings are separate from this gated content inventory.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "List source/revision-pinned paper images",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "exact source; mutually exclusive with version",
                        "name": "source_id",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "exact semantic arXiv vN",
                        "name": "version",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "complete immutable revision",
                        "name": "revision",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{paper_id,source_id,revision,files:[{key,name,size,sha256,url}],truncated:false,assets}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "202": {
                        "description": "source-specific parse pending",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        },
                        "headers": {
                            "Operation-Location": {
                                "type": "string",
                                "description": "pure readiness poll URL"
                            },
                            "Retry-After": {
                                "type": "string",
                                "description": "poll delay in seconds"
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "disabled or exact pins missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "Conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "Unprocessable Entity",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{paper_id}/parses": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Parse revisions of a paper: each row is one immutable\nparse artifact (schema + schema_version + artifact\nsha256) bound to the source it was parsed from. A\nre-parse inserts a new revision and flips the\nis_current pointer (current_revision_id); old artifacts\nkeep serving unchanged bytes. Requires login or\npapers:read.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "BlockComments"
                ],
                "summary": "List parse revisions (block comments)",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{paper_id, parses:[{revision_id,source_id,schema,schema_version,artifact_sha256,is_current,created_at,json_endpoint,blocks_endpoint}], current_revision_id}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "no such paper",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "registry unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{paper_id}/parses/{revision}/blocks": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Top-level blocks of one immutable parse revision, in\nstable (page_idx, block index) order. Block indexes are\nthe parse's own public 1-based numbers and may be\nnon-contiguous (1,2,5,...); gaps are real — never\nrenumbered. page_idx is the original 0-based page index\n(page_no in each item is the public 1-based number).\ncursor is the opaque next_cursor token (keyset on the\nlast emitted block); per_page defaults to 20, max 100.\nRequires paper_access.enabled plus login/papers:read, including historical revisions and qa_ identities; disabled is 404.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "BlockComments"
                ],
                "summary": "List parse blocks (keyset)",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "parse revision id",
                        "name": "revision",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "integer",
                        "description": "filter: exact 0-based page index",
                        "name": "page_idx",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "keyset cursor from next_cursor",
                        "name": "cursor",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "page size (default 20, max 100)",
                        "name": "per_page",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{paper_id,revision_id,schema,schema_version,blocks:[{page_idx,block_index,page_no,type,content,bbox,has_bbox,raw}],next_cursor}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "bad query parameter",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "no such revision / bytes missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "artifact fails a supported original Middle profile (DocVortex 2.0 or native pdf_info-v1)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "registry or store unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{paper_id}/parses/{revision}/blocks/{page_idx}/{block_index}": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Combined read of ONE block: source (paper + source PDF\nidentity + parse revision identity) + anchor (the\npermanent comment anchor: canonical qa_, source_id,\nparse_revision, schema, page_idx, block_index) +\ncontent (the block's parsed value, original JSON under\nraw) + discussions. Block lookup is EXACT on\n(page_idx, block index): a non-contiguous index gap or a\npage the parse lacks answers 404 — never a\nnearest-block fallback. The same visual text under two\nparses of one PDF is TWO distinct anchors; comments\nnever migrate between revisions. Original block content requires\npaper_access.enabled plus login/papers:read for all IDs; comment\nmetadata does not provide an original-content gate bypass.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "BlockComments"
                ],
                "summary": "Read one block (combined)",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "parse revision id",
                        "name": "revision",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "integer",
                        "description": "0-based page index",
                        "name": "page_idx",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "integer",
                        "description": "1-based public block index (exact)",
                        "name": "block_index",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{source, anchor, content, discussions, discussions_ready, next_cursor}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "malformed block path",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "no such revision or block (exact-index semantics)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "artifact fails a supported original Middle profile (DocVortex 2.0 or native pdf_info-v1)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "registry or store unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{paper_id}/parses/{revision}/blocks/{page_idx}/{block_index}/image": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "A PNG crop of the ORIGINAL source PDF page rendered and\nclipped to the block's [0,1]-normalized bbox — a real\npage region, never a redraw of the parsed text. Honest\nfailures, never a fabricated image: the block carries\nno bbox → 404 reason=no_bbox; the source PDF bytes are\nmissing → 404 reason=source_missing; the page is beyond\nthe source PDF → 404 reason=page_out_of_range; no PDF\nrasterizer installed on the server → 503\nreason=renderer_unavailable (operators install\npoppler-utils or set paper_access.block_image_command;\nresolution via paper_access.block_image_dpi, default\n150). ETag is deterministic over (source sha, page,\nbbox, dpi). Requires paper_access.enabled plus login/papers:read, including historical sources; Range/aliases do not bypass the gate.",
                "produces": [
                    "image/png"
                ],
                "tags": [
                    "BlockComments"
                ],
                "summary": "Get block image (original-page crop)",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "parse revision id",
                        "name": "revision",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "integer",
                        "description": "0-based page index",
                        "name": "page_idx",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "integer",
                        "description": "1-based public block index (exact)",
                        "name": "block_index",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "PNG crop of the rendered original page",
                        "schema": {
                            "type": "file"
                        }
                    },
                    "400": {
                        "description": "malformed block path",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "no_bbox | source_missing | page_out_of_range | no such revision/block",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "artifact fails a supported original Middle profile (DocVortex 2.0 or native pdf_info-v1)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "renderer_unavailable | registry or store unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{paper_id}/parses/{revision}/files/{path}": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Requires paper_access.enabled and papers:read; serves the exact original relative member path, including nested directories and all JSON/Markdown/images/unknown valid files. No renaming, flattening or JSON reserialization. Verifies the complete published manifest/source/members and the selected member SHA before bytes; exact pins never fall back. Unknown/untrusted types use attachment+nosniff, allowlisted raster images may be inline. Generated manifest lives separately from producer files. No raw S3 presigns.",
                "produces": [
                    "application/octet-stream"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Get a producer-original immutable bundle member",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id or resolvable alias",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "immutable complete parse revision",
                        "name": "revision",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "original producer relative member path, may contain /; as listed by manifest",
                        "name": "path",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "optional exact source consistency pin",
                        "name": "source_id",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "byte-exact original member; ETag is member SHA-256",
                        "schema": {
                            "type": "file"
                        }
                    },
                    "400": {
                        "description": "unsafe member path",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "disabled, pins missing or member not in manifest",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "source/revision conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "bundle/source/member integrity failure",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "backend unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{paper_id}/parses/{revision}/json": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "The ORIGINAL Middle JSON artifact bytes of one parse\nrevision — not a re-serialization. ETag /\nX-QAtlas-Sha256 carry the artifact sha256 the revision\nrow pins; the server verifies the stored bytes against\nit before serving (a mismatch is a 500, never silent).\nRevisions are immutable: the bytes for a revision_id\nnever change. Requires paper_access.enabled plus login/papers:read. Historical pinned Middle remains available for old comments, but is not evidence that a new complete bundle is ready; use /read for the derived view or /manifest and /files/{path} for all producer originals.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "BlockComments"
                ],
                "summary": "Get parse JSON bytes",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "parse revision id",
                        "name": "revision",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Middle JSON bytes (application/json)",
                        "schema": {
                            "type": "file"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "no such revision / bytes missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "500": {
                        "description": "stored bytes fail the pinned sha256",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "registry or store unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{paper_id}/parses/{revision}/manifest": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Requires paper_access.enabled and papers:read; only published complete new bundles are accepted. Verifies exact frozen source, manifest and every original member before serving. Manifest is generated separately from originals: version,paper_id,source_id,revision_id,source_pdf_sha256,middle_path,markdown_path,files[{path,size_bytes,sha256}]. Old historical Middle alone is not bundle readiness. Never repairs or falls back to current.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Get immutable complete-bundle manifest",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id or resolvable alias",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "immutable complete parse revision",
                        "name": "revision",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "optional exact source consistency pin",
                        "name": "source_id",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "generated manifest JSON bytes, ETag is manifest SHA-256",
                        "schema": {
                            "type": "file"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "disabled or exact published bundle missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "source/revision conflict",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "422": {
                        "description": "bundle/source integrity failure",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "backend unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{paper_id}/sources": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Source PDF assets of a paper for the block-comments\nsurface: each row pins one immutable byte-identity\n(origin label like arxiv:v2 / upload, sha256, size).\nThe current_source_id / is_current pointer is derived\nfrom the paper's current parse revision (paper_sources\nrows are append-only). The paper_id path segment may be\na qa_ surrogate, an arXiv id, or a DOI — all resolve to\nthe canonical qa_ (merged papers follow merged_into;\nthe request alias is echoed via X-QAtlas-Requested-Id /\nX-QAtlas-Resolved-Id). Requires login or the\npapers:read scope.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "BlockComments"
                ],
                "summary": "List paper sources (block comments)",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{paper_id, sources:[{source_id,origin,sha256,size_bytes,is_current,created_at,pdf_endpoint,source_url?,retrieved_url?,retrieved_at?}], current_source_id}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "unrecognized id form",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "no such paper",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "registry unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/papers/{paper_id}/sources/{source_id}/pdf": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "The ORIGINAL PDF bytes of one source, hash-verifiable\nvia ETag / X-QAtlas-Sha256 (= the row's sha256). Range\nrequests are honoured with the SAME authentication as\nfull GETs. ?version=vN is a pin, not a hint: on mismatch\nwith the source's own origin the answer is 404 — the\nserver never substitutes a newer version or the journal\nedition. Requires paper_access.enabled even for historical\nsources; /api/papers/{id}/pdf is the direct alias-aware source-pin entry point.",
                "produces": [
                    "application/pdf"
                ],
                "tags": [
                    "BlockComments"
                ],
                "summary": "Get source PDF bytes",
                "parameters": [
                    {
                        "type": "string",
                        "description": "paper id: qa_... | arXiv id | DOI",
                        "name": "paper_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "source id from the sources list",
                        "name": "source_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "pin: arXiv version, e.g. v2 — mismatch 404s, never substitutes",
                        "name": "version",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "PDF bytes (Range → 206)",
                        "schema": {
                            "type": "file"
                        }
                    },
                    "206": {
                        "description": "partial content",
                        "schema": {
                            "type": "file"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "no such source / version pin mismatch / bytes missing",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "registry or store unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/pat": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "PAT"
                ],
                "summary": "List PATs",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "array",
                            "items": {
                                "$ref": "#/definitions/routes.patSummary"
                            }
                        }
                    }
                }
            },
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Mints a personal access token. Plaintext is returned exactly\nonce. Requires a PocketBase session token (PAT auth is\nrefused here, to stop a leaked PAT from self-replicating).",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "PAT"
                ],
                "summary": "Create PAT",
                "parameters": [
                    {
                        "description": "token spec",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/routes.patCreateRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/routes.patCreateResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/pat/scopes": {
            "get": {
                "description": "Lists the available scopes and their descriptions so clients\ncan render the create form without hardcoding the vocabulary.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "PAT"
                ],
                "summary": "PAT scopes",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/api/pat/{id}": {
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "PAT"
                ],
                "summary": "Delete PAT",
                "parameters": [
                    {
                        "type": "string",
                        "description": "PAT record id",
                        "name": "id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "boolean"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/rag/evidence": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Same relay contract as /api/rag/retrieve but targeting\nqatlas-rag's POST /v1/evidence: body forwarded verbatim,\nreply streamed back as-is, 64 KiB cap, upstream errors\nforwarded with their own status. Requires the papers:read\nscope.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "RAG"
                ],
                "summary": "RAG evidence (proxied)",
                "parameters": [
                    {
                        "description": "evidence query (qatlas-rag /v1/evidence schema)",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "the microservice's reply, relayed verbatim",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "body read failed",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "413": {
                        "description": "body exceeds 64 KiB",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "rag.remote disabled / microservice unreachable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/rag/retrieve": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Forwards the request body verbatim to qatlas-rag's\nPOST /v1/retrieve (the retrieve request/response schema\nbelongs to the qatlas-rag repository — qatlasd only\nauthenticates the caller and relays, so schema changes\nnever need a qatlasd release) and streams the reply back\nas-is. Bodies are capped at 64 KiB. rag.remote disabled →\n503 \"rag service not configured\"; microservice\nunreachable → 503 \"rag unreachable: …\"; a non-2xx\nupstream reply is forwarded with its own status + body so\nclients see the rag error schema. Requires the\npapers:read scope.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "RAG"
                ],
                "summary": "RAG retrieve (proxied)",
                "parameters": [
                    {
                        "description": "retrieve query (qatlas-rag /v1/retrieve schema)",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "the microservice's reply, relayed verbatim",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "body read failed",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "413": {
                        "description": "body exceeds 64 KiB",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "rag.remote disabled / microservice unreachable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/search": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Fans one search entry out to the configured providers\n(catalog / arxiv / openalex / remote), merges hits by paper\nidentity (DOI \u003e arXiv \u003e title hash) and resolve-or-mints each\nidentity-anchored hit against the paper registry. Newly minted\npapers carry created=true as metadata only: search does not\ndownload papers or submit conversion tasks. Select identifiers\nexplicitly via POST /api/downloader/fetch. Title-only hits return as un-minted\ncandidates. The entry may carry an identity (arxiv_id / doi)\ninstead of free text — identity fields are forwarded to the\nremote provider for identity-aware lookups; an entry with\nnone of text / title / arxiv_id / doi is a 400. Each minted\nresult also carries its hosting summary: has_md / has_pdf /\nstatus from the registry default asset (omitted when the\nregistry is unavailable). Requires the papers:read scope.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Search"
                ],
                "summary": "Search papers",
                "parameters": [
                    {
                        "description": "search entry {text?, title?, doi?, arxiv_id?, max_results?, required_phrases?}",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{results:[{paper_id,hit,created,has_md?,has_pdf?,status?}], candidates:[...]}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "invalid JSON or empty search entry",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "registry unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/search/agentic": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "One multi-source search through the agentic backend\n(remote qatlas-search microservice or the local runner),\noptionally with an LLM conclusion ({\"agent\": false} skips\nit). The body is a search entry plus \"agent\" and \"sources\"\nextensions; identity fields (arxiv_id / doi / title) are\nforwarded so identity-only entries run identity lookups\ninstead of an empty query — an entry with none of text /\ntitle / arxiv_id / doi is a 400 (before metering). Every\ncall is metered per user per day (usage block); a failed\nupstream is refunded. Results mirror POST /api/search,\nincluding the has_md / has_pdf / status hosting summary\non minted results. Anchoring is metadata-only; downloads require\nexplicit POST /api/downloader/fetch. Requires the papers:read scope plus a\nuser-bound credential (system PATs get 403).",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Search"
                ],
                "summary": "Agentic search (metered)",
                "parameters": [
                    {
                        "description": "{text?, title?, doi?, arxiv_id?, max_results?, agent?, sources?}",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{results:[{paper_id,hit,created,has_md?,has_pdf?,status?}], candidates:[...], conclusion, usage, errors}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "invalid JSON or empty search entry",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "system PAT (no user to meter)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "429": {
                        "description": "daily agentic limit reached",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "502": {
                        "description": "upstream failed (quota refunded)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "no backend configured / registry unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/search/backends": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "The backend catalog for the search page's backend picker:\nthe static table merged with the qatlas-search\nmicroservice's live availability (server_ready) and the\ncaller's stored API keys (key_configured). selectable =\nserver_ready || (user_key \u0026\u0026 key_configured) — key-requiring\nbackends without a stored key render disabled. Session-only\n(key_configured is user-private).",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Search"
                ],
                "summary": "List search backends",
                "responses": {
                    "200": {
                        "description": "{remote, keys_enabled, backends: [{name, label, category, requires_key, user_key, server_ready, key_configured, selectable}]}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/search/multi": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Proxies one mode=\"multi\" call to the qatlas-search\nmicroservice: every requested backend returns its own raw\nhit list (the source's own order), with NO cross-backend\nmerge or ranking. The caller's stored third-party API keys\n(configured in the dashboard) are decrypted and forwarded so\nkey-requiring backends run under the user's credentials.\nProvide text or doi (at least one nonblank). A DOI-only request\nforwards doi with an empty query for identity-aware lookup;\nnonempty text retains text-search semantics when both are supplied.\nIdentity-anchored hits (DOI / arXiv id) are resolve-or-\nminted as metadata only before the response, without downloads\nor conversion tasks, and carry the server-side\nenrichment paper_id / created / has_md / status (the\nhosting summary is omitted when the registry is\nunavailable). Title-only hits — no DOI, no arXiv id — are\nnever minted and carry none of these fields.\nRequires the papers:read scope; 503 when search.remote is\ndisabled; 502 when the microservice call fails.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Search"
                ],
                "summary": "Multi-backend search (per-platform raw results)",
                "parameters": [
                    {
                        "description": "{text?, doi?, max_results?, sources: [backend names]}; text or doi is required",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{results: {backend: [{title, authors?, year?, doi?, arxiv_id?, url?, venue?, citations?, source, score, paper_id?, created?, has_md?, status?}]}, usage, errors: {backend: msg}, remote: true}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "502": {
                        "description": "upstream failed",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "search.remote disabled",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/search/ranked": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Search anchors metadata only; downloading selected identifiers requires POST /api/downloader/fetch.\nRemote-only fused search with ranking=scorer and agent=false. Requires papers:read. Recompiles the supplied program on every request. No LLM calls. Preserves one globally ordered hit list including title-only candidates and custom score explanations; only returned identity-anchored hits are resolve-or-minted. Scores are neither normalized nor probabilities.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Search"
                ],
                "summary": "Search using a confirmed custom scorer",
                "parameters": [
                    {
                        "description": "{text:string,sources:[string],max_results?:1..100,scorer:{language:qatlas-expr-v1,filter?:string,score:string},explain?:boolean}",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{hits:[...],ranking,usage,errors,remote:true}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "413": {
                        "description": "Request Entity Too Large",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "422": {
                        "description": "Unprocessable Entity",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "429": {
                        "description": "Too Many Requests",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "502": {
                        "description": "Bad Gateway",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/api/search/scoring/capabilities": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Remote-only DSL features, limits and generation_available. Browser session required.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Search"
                ],
                "summary": "Scoring language capabilities",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/api/search/scoring/generate": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Requires papers:read and a user-bound credential. Generates and compiles a qatlas-expr-v1 scorer without searching or ingesting. One successful generation consumes one shared Agentic daily unit; failures refund the unit while recording known token usage. At most one internal repair.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Search"
                ],
                "summary": "Generate a restricted scorer from natural language (metered)",
                "parameters": [
                    {
                        "description": "{query:string, requirements:string}; each 1-4000 Unicode characters",
                        "name": "body",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "{scorer,summary,warnings,scorer_hash,feature_version,usage:{today,limit,llm_tokens}}",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "413": {
                        "description": "Request Entity Too Large",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "422": {
                        "description": "Unprocessable Entity",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "429": {
                        "description": "Too Many Requests",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "502": {
                        "description": "Bad Gateway",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "504": {
                        "description": "Gateway Timeout",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/api/search/survey": {
            "post": {
                "description": "Results are anchored as metadata only, without downloads or conversion tasks; selected identifiers require POST /api/downloader/fetch.\nKeywords, author/year/venue/citation rules and agentic planning are owned by qatlas-search. Rules filter a bounded retrieved set, not an exhaustive corpus. Per-user backend keys are injected by qatlasd and cannot be supplied by callers.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "search"
                ],
                "summary": "Plan and execute a bounded academic survey search",
                "parameters": [
                    {
                        "description": "{goal, queries?, rules?: {authors?, venues?, year_from?, year_to?, min_citations?, sort?}, sources, max_results?, agentic?}; agentic=true uses existing daily quota",
                        "name": "request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "Service Unavailable",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/server/info": {
            "get": {
                "description": "Capability discovery: mode / version / engine plus a\ncapabilities block — paper_access, markdown_delivery,\npdf_delivery (available when paper_access is enabled;\nstored PDF delivery requires no MinerU token), agentic_search, and a nested mineru\nobject (enabled / on_demand). Privacy mirrors /api/health:\nanonymous callers see the booleans only; authenticated\ncallers (system PAT or session) additionally get\nmineru.daily_cap and mineru.converted_today from the\nbatch-scheduler snapshot. Note the quota semantics:\ndaily_cap bounds explicit operator/manual batch runs;\nthere is no automatic nightly, boot, PDF-ingest or bulk\nconversion. Only explicit content access triggers lazy\nsource-bound inference; stored PDF reads are token-independent.\nManual and on-demand work still share upstream token quotas.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "System"
                ],
                "summary": "Server info",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/api/v1/papers/{arxiv_id}/mineru-lease": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Acquires the same MinerU processing lease returned by the claim path. The response body uses claim_id as the lease identifier. With paper_access enabled, pdf_url is a same-origin authenticated exact-source locator, source_id/pdf_requires_auth:true and verified PDF SHA are returned; never forward the user's auth to an external parser. Download exact bytes with auth, then use provider upload. With the gate off, only the external arXiv URL and grant catalog SHA are returned; no hosted PDF bytes.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Papers"
                ],
                "summary": "Acquire MinerU processing lease",
                "parameters": [
                    {
                        "type": "string",
                        "description": "arXiv identifier",
                        "name": "arxiv_id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "201": {
                        "description": "lease granted (body is the lease record; claim_id identifies the lease)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "400": {
                        "description": "invalid arxiv_id",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "not claimable (no PDF in catalog, or markdown already exists)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "already leased by someone else (body includes existing lease details)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "500": {
                        "description": "internal error",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "catalog unavailable (PostgreSQL unreachable)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/papers/{arxiv_id}/mineru-lease/{claim_id}": {
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Releases a MinerU processing lease by claim_id.",
                "tags": [
                    "Papers"
                ],
                "summary": "Release MinerU lease",
                "parameters": [
                    {
                        "type": "string",
                        "description": "arXiv identifier",
                        "name": "arxiv_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "claim id",
                        "name": "claim_id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "204": {
                        "description": "lease released (empty body)"
                    },
                    "400": {
                        "description": "invalid arxiv_id",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "409": {
                        "description": "claim_id does not match the active lease",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "500": {
                        "description": "internal error",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "503": {
                        "description": "catalog unavailable (PostgreSQL unreachable)",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/plugins": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Plugins"
                ],
                "summary": "List plugins",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/plugins/{id}/disable": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Plugins"
                ],
                "summary": "Disable plugin",
                "parameters": [
                    {
                        "type": "string",
                        "description": "plugin id",
                        "name": "id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/plugins/{id}/enable": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Plugins"
                ],
                "summary": "Enable plugin",
                "parameters": [
                    {
                        "type": "string",
                        "description": "plugin id",
                        "name": "id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "type": "object",
                            "additionalProperties": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        },
        "/install-qatlasd.sh": {
            "get": {
                "description": "Returns a POSIX sh script (text/x-shellscript) that downloads\nthe latest qatlasd release binary.",
                "produces": [
                    "text/plain"
                ],
                "tags": [
                    "System"
                ],
                "summary": "Installer script",
                "responses": {
                    "200": {
                        "description": "shell script",
                        "schema": {
                            "type": "string"
                        }
                    }
                }
            }
        }
    },
    "definitions": {
        "routes.externalSourceRequest": {
            "type": "object",
            "required": [
                "authors",
                "source_url",
                "title",
                "year"
            ],
            "properties": {
                "authors": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "source_url": {
                    "type": "string"
                },
                "title": {
                    "type": "string"
                },
                "year": {
                    "type": "integer",
                    "maximum": 9999,
                    "minimum": 1
                }
            }
        },
        "routes.meUsageResponse": {
            "type": "object",
            "properties": {
                "limit": {
                    "type": "integer"
                },
                "llm_tokens": {
                    "type": "integer"
                },
                "metric": {
                    "type": "string"
                },
                "today": {
                    "type": "integer"
                }
            }
        },
        "routes.oauthDeviceCodeRequest": {
            "type": "object",
            "properties": {
                "description": {
                    "type": "string"
                },
                "expires_in_days": {
                    "type": "integer"
                },
                "name": {
                    "type": "string"
                },
                "scopes": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                }
            }
        },
        "routes.oauthDeviceCodeResponse": {
            "type": "object",
            "properties": {
                "device_code": {
                    "type": "string"
                },
                "expires_in": {
                    "type": "integer"
                },
                "interval": {
                    "type": "integer"
                },
                "user_code": {
                    "type": "string"
                },
                "verification_uri": {
                    "type": "string"
                },
                "verification_uri_complete": {
                    "type": "string"
                }
            }
        },
        "routes.oauthDeviceLookupResponse": {
            "type": "object",
            "properties": {
                "available_scopes": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "description": {
                    "type": "string"
                },
                "expires_in_days": {
                    "type": "integer"
                },
                "max_expiry_days": {
                    "type": "integer"
                },
                "name": {
                    "type": "string"
                },
                "scope_descriptions": {
                    "type": "object",
                    "additionalProperties": {
                        "type": "string"
                    }
                },
                "scopes": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "status": {
                    "type": "string"
                },
                "user_code": {
                    "type": "string"
                }
            }
        },
        "routes.oauthDeviceTokenError": {
            "type": "object",
            "properties": {
                "error": {
                    "type": "string"
                }
            }
        },
        "routes.oauthDeviceTokenRequest": {
            "type": "object",
            "properties": {
                "device_code": {
                    "type": "string"
                }
            }
        },
        "routes.oauthDeviceTokenResponse": {
            "type": "object",
            "properties": {
                "expires_at": {
                    "type": "string"
                },
                "name": {
                    "type": "string"
                },
                "pat_id": {
                    "type": "string"
                },
                "plaintext": {
                    "type": "string"
                },
                "prefix": {
                    "type": "string"
                },
                "scopes": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                }
            }
        },
        "routes.oauthDeviceUserCodeBody": {
            "type": "object",
            "properties": {
                "user_code": {
                    "type": "string"
                }
            }
        },
        "routes.patCreateRequest": {
            "type": "object",
            "properties": {
                "description": {
                    "type": "string"
                },
                "expires_in_days": {
                    "description": "REQUIRED: 1..MaxPATExpiryDays",
                    "type": "integer"
                },
                "name": {
                    "type": "string"
                },
                "scopes": {
                    "description": "REQUIRED: may be empty for \"no permissions\"",
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                }
            }
        },
        "routes.patCreateResponse": {
            "type": "object",
            "properties": {
                "created": {
                    "type": "string"
                },
                "description": {
                    "type": "string"
                },
                "expires_at": {
                    "type": "string"
                },
                "id": {
                    "type": "string"
                },
                "name": {
                    "type": "string"
                },
                "plaintext": {
                    "type": "string"
                },
                "prefix": {
                    "type": "string"
                },
                "scopes": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                }
            }
        },
        "routes.patSummary": {
            "type": "object",
            "properties": {
                "created": {
                    "type": "string"
                },
                "description": {
                    "type": "string"
                },
                "expires_at": {
                    "type": "string"
                },
                "id": {
                    "type": "string"
                },
                "last_used_at": {
                    "type": "string"
                },
                "name": {
                    "type": "string"
                },
                "prefix": {
                    "type": "string"
                },
                "scopes": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                }
            }
        }
    },
    "securityDefinitions": {
        "BearerAuth": {
            "description": "\"Bearer \u003ctoken\u003e\" — two credential shapes\naccepted: a Personal Access Token\n(`Authorization: Bearer qat_...`, minted at\n`/pat` after GitHub OAuth login), or the\nconfig-loaded system PAT (set\n`system_pat.token` in config.yaml on the\nserver, send the plaintext as\n`Authorization: Bearer \u003cvalue\u003e`).\nBrowser callers are authenticated through\npb.authStore (no copy step) — only non-browser\ncallers need an explicit bearer.",
            "type": "apiKey",
            "name": "Authorization",
            "in": "header"
        }
    }
}