{
  "openapi": "3.1.0",
  "info": {
    "title": "SRD Framework API",
    "version": "1.0.0",
    "description": "Seeded random-table rolls behind a friends-only license gate (demo tables public). Random results are plain and reproducible: same seed, same result. Site index for agents: /llms.txt. Canonical paths carry a trailing slash (bare forms 308-redirect)"
  },
  "servers": [
    {
      "url": "https://srd-framework.vercel.app"
    }
  ],
  "components": {
    "securitySchemes": {
      "gatePassword": {
        "type": "http",
        "scheme": "bearer",
        "description": "The shared gate password, sent as a bearer token. Issued by the site owner. Protects LLM-backed endpoints (cost control)."
      }
    },
    "schemas": {
      "RollNode": {
        "type": "object",
        "description": "A roll and everything it cascaded into. Recursive.",
        "properties": {
          "label": {
            "type": "string"
          },
          "die": {
            "type": "string"
          },
          "rolled": {
            "type": "integer"
          },
          "text": {
            "type": "string"
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RollNode"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/api/roll/": {
      "get": {
        "operationId": "rollTable",
        "summary": "Roll on a random table (or list all tables with ?list=1)",
        "security": [
          {
            "gatePassword": []
          }
        ],
        "description": "Gated (the table data is licensed WWN content — friends-only, not cost), EXCEPT tables with the demo/ id prefix, which are public and exist so agents can validate the contract without credentials (try table=demo/omen). Unauthenticated ?list=1 shows demo tables only. Use ?list=1 first to discover table ids, kinds, and valid row ids. matrix and row-die tables require &row=. Same seed reproduces the same result; omit seed and the response reports the one it chose. n>1 derives per-roll seeds as <seed>-<i>.",
        "parameters": [
          {
            "name": "list",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            },
            "description": "Return the table catalog instead of rolling."
          },
          {
            "name": "table",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Table id, e.g. wwn/site-treasure. Required unless list=1."
          },
          {
            "name": "row",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Row id for matrix / row-die tables, e.g. bandit-cache."
          },
          {
            "name": "seed",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Reproducibility seed. Same seed, same result."
          },
          {
            "name": "n",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 1
            },
            "description": "Number of rolls."
          }
        ],
        "responses": {
          "200": {
            "description": "Roll results (or table catalog when list=1).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "engine": {
                      "type": "string"
                    },
                    "table": {
                      "type": "string"
                    },
                    "row": {
                      "type": "string"
                    },
                    "seed": {
                      "type": "string"
                    },
                    "n": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "seed": {
                            "type": "string"
                          },
                          "tree": {
                            "$ref": "#/components/schemas/RollNode"
                          },
                          "text": {
                            "type": "string",
                            "description": "Plain-text rendering, identical to the UI output."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Actionable error — the message says what to fix (e.g. missing ?table=, invalid row)."
          }
        }
      }
    }
  }
}