{
  "openapi": "3.1.0",
  "info": {
    "title": "tfora LP Maker API",
    "version": "1.0.0",
    "description": "AI エージェントがロングLP（画像を縦に積んだランディングページ）を作る・直す・検品する・書き出すための API。コマンドの中身と使い方は /llms.txt。試し用キー（tf_test_）は画像を作らず仮の絵を返し、クレジットを引かない。"
  },
  "servers": [{ "url": "/" }],
  "components": {
    "securitySchemes": { "key": { "type": "http", "scheme": "bearer", "description": "tf_test_… または tf_live_…" } },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error", "code"],
        "properties": {
          "error": { "type": "string" },
          "code": { "type": "string", "examples": ["missing_key", "invalid_key", "insufficient_credits", "file_must_be_uploaded", "unknown_command", "bad_name", "test_limit", "command_failed"] },
          "hint": { "type": "string", "description": "次に何をすればよいか" }
        }
      },
      "RunRequest": {
        "type": "object",
        "required": ["command"],
        "properties": {
          "command": { "type": "string", "enum": ["list", "show", "part", "new", "fork", "outline", "style", "title", "refs", "ref-add", "ref-rm", "draw", "plan", "add", "set", "replace", "move", "copy", "rm", "gen", "clone", "crop", "stack", "paste", "put", "seams", "adopt", "discard", "get", "sheet", "check", "export", "reviews", "review", "help"] },
          "args": { "type": "array", "items": { "type": "string" }, "description": "位置引数。先頭は LP 名（英数字・_・-、「--」不可、60字まで）。ファイルは upload:<id>" },
          "options": { "type": "object", "additionalProperties": { "oneOf": [{ "type": "string" }, { "type": "boolean" }, { "type": "array", "items": { "type": "string" } }] }, "description": "--名前 の値。値を取らないものは true。ファイルを取るもの（file・outline・texts-file・screens-file・config・ref）は upload:<id>" },
          "estimate_id": { "type": "string", "description": "needs_confirmation で返った見積もりを確定して実行する（30分以内）" }
        }
      },
      "RunResult": {
        "type": "object",
        "properties": {
          "run_id": { "type": "string" },
          "mode": { "type": "string", "enum": ["test", "live"] },
          "status": { "type": "string", "enum": ["done", "failed"] },
          "output": { "type": "string", "description": "コマンドの表示。次の一手もここに出る" },
          "files": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "url": { "type": "string", "description": "24時間有効" } } } },
          "failed": { "type": "boolean" },
          "credits": { "type": "object", "properties": { "used": { "type": "integer" }, "left": { "type": "integer" }, "estimate": { "type": "integer" } } },
          "usage": { "type": "object", "properties": { "images": { "type": "integer" }, "claude_input_tokens": { "type": "integer" }, "claude_output_tokens": { "type": "integer" } } },
          "watermark": { "type": "boolean" }
        }
      },
      "NeedsConfirmation": {
        "type": "object",
        "properties": {
          "status": { "const": "needs_confirmation" },
          "estimate_id": { "type": "string" },
          "estimate": { "type": "integer", "description": "最大でこれだけ使う（実際は使った分だけ）" },
          "estimate_note": { "type": "string" },
          "balance": { "type": "integer" },
          "hint": { "type": "string" }
        }
      }
    }
  },
  "paths": {
    "/api/v1/signup": {
      "post": {
        "summary": "AI が自分で登録する。組織と試し用キーを作る",
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string" } } } } } },
        "responses": {
          "201": { "description": "登録した。キーの全文はこの1回だけ", "content": { "application/json": { "schema": { "type": "object", "properties": { "org": { "type": "object" }, "test_key": { "type": "string" }, "live_key": { "type": "string" }, "credits": { "type": "integer" }, "expires_at": { "type": "string" }, "next": { "type": "string" } } } } } },
          "429": { "description": "登録の回数の上限", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/uploads": {
      "post": {
        "summary": "run に渡すファイルを上げる",
        "security": [{ "key": [] }],
        "parameters": [{ "name": "filename", "in": "query", "schema": { "type": "string" } }],
        "requestBody": {
          "content": {
            "application/octet-stream": { "schema": { "type": "string", "format": "binary" }, "description": "3.5MBまで。ファイルそのもの" },
            "application/json": { "schema": { "type": "object", "properties": { "filename": { "type": "string" }, "size": { "type": "integer" } } }, "description": "大きいファイル。upload_url が返るので PUT で送る（25MBまで）" }
          }
        },
        "responses": { "200": { "description": "ref（upload:<id>）を run の引数に書く", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "string" }, "ref": { "type": "string" }, "upload_url": { "type": "string" } } } } } } }
      }
    },
    "/api/v1/run": {
      "post": {
        "summary": "LP Maker のコマンドを1つ動かす",
        "security": [{ "key": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RunRequest" } } } },
        "responses": {
          "200": { "description": "実行した", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RunResult" } } } },
          "202": { "description": "見積もりが大きいので確認待ち。estimate_id を付けて呼び直す", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NeedsConfirmation" } } } },
          "400": { "description": "引数の誤り・コマンドの失敗（RunResult の項目も付く）", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "キーが無い・使えない", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "402": { "description": "クレジットが足りない", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/me": {
      "get": {
        "summary": "組織・残高・単価・最近の実行",
        "security": [{ "key": [] }],
        "responses": { "200": { "description": "OK" } }
      }
    }
  }
}
