{
  "openapi": "3.0.3",
  "info": {
    "title": "SlotForge Studio API",
    "version": "1.0.0",
    "description": "Production API for generating, editing, searching and approving slot-symbol artwork at scale. SSO (Google) for the dashboard; API keys for automation. Per-workspace billing, signed audit trail, GLI-ready compliance reports.",
    "contact": { "name": "SlotForge support", "url": "https://kobowlotto.com/products/slotforge/", "email": "sales@kobowlotto.com" },
    "license": { "name": "Proprietary" }
  },
  "servers": [
    { "url": "https://kobowlotto.com", "description": "Production" }
  ],
  "security": [{ "ApiKey": [] }, { "SessionCookie": [] }],
  "tags": [
    { "name": "Auth", "description": "Google SSO + session helpers" },
    { "name": "Generate", "description": "Text-to-image, edit, upscale" },
    { "name": "Search", "description": "Semantic library search" },
    { "name": "Projects", "description": "Game projects (paytable, reels, math)" },
    { "name": "Brands", "description": "Brand definitions for re-skin workflows" },
    { "name": "Assets", "description": "Approval, versioning, status" },
    { "name": "Compliance", "description": "GLI/jurisdiction reports" },
    { "name": "Audit", "description": "Signed event log" },
    { "name": "Webhooks", "description": "Outbound event notifications" },
    { "name": "Billing", "description": "Stripe-backed plans, usage, invoices" },
    { "name": "Comments", "description": "Threaded asset discussion" },
    { "name": "Export", "description": "Sprite atlas, multi-res, project ZIP" },
    { "name": "Jobs", "description": "Async generation queue" },
    { "name": "Workspaces", "description": "Multi-tenant workspace admin" }
  ],
  "components": {
    "securitySchemes": {
      "ApiKey": { "type": "apiKey", "in": "header", "name": "X-API-Key" },
      "SessionCookie": { "type": "apiKey", "in": "cookie", "name": "sf_session" }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean", "example": false },
          "error": { "type": "string", "example": "missing_prompt" },
          "message": { "type": "string" }
        }
      },
      "Asset": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "file": { "type": "string", "example": "atlantis_mermaid_v1_wild.png" },
          "kind": { "type": "string", "enum": ["raw", "edit", "upscale", "revert", "regen"] },
          "size": { "type": "string", "example": "1024x1024" },
          "sha": { "type": "string", "example": "a1b2c3d4" },
          "provider": { "type": "string", "example": "openai_gpt_image_1" },
          "cost_cents": { "type": "integer" },
          "status": { "type": "string", "enum": ["draft", "review", "approved", "rejected", "cert_submitted", "production"] },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "GenRequest": {
        "type": "object",
        "required": ["prompt"],
        "properties": {
          "prompt": { "type": "string", "example": "a glowing mermaid coin, polished gold, transparent background" },
          "size": { "type": "string", "default": "1024x1024" },
          "brand_slug": { "type": "string", "example": "pollard" },
          "provider": { "type": "string", "example": "openai_gpt_image_1" },
          "format": { "type": "string", "example": "slot_symbol_square" },
          "project_id": { "type": "integer" }
        }
      },
      "GenResponse": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "asset_id": { "type": "integer" },
          "file": { "type": "string" },
          "url": { "type": "string" },
          "size": { "type": "string" },
          "sha": { "type": "string" },
          "provider": { "type": "string" },
          "cost_cents": { "type": "integer" },
          "duration_ms": { "type": "integer" }
        }
      },
      "Project": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "slug": { "type": "string" },
          "name": { "type": "string" },
          "theme": { "type": "string" },
          "status": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "Brand": {
        "type": "object",
        "properties": {
          "slug": { "type": "string" },
          "name": { "type": "string" },
          "palette": { "type": "object" },
          "style_summary": { "type": "string" }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "kind": { "type": "string", "enum": ["slack", "discord", "generic", "email"] },
          "events": { "type": "array", "items": { "type": "string" } },
          "enabled": { "type": "boolean" },
          "last_status": { "type": "integer", "nullable": true },
          "failure_count": { "type": "integer" }
        }
      },
      "Subscription": {
        "type": "object",
        "properties": {
          "plan_id": { "type": "string", "enum": ["free", "starter", "pro", "enterprise"] },
          "status": { "type": "string" },
          "monthly_quota_gens": { "type": "integer" },
          "monthly_used_gens": { "type": "integer" },
          "period_start": { "type": "string", "format": "date" },
          "period_end": { "type": "string", "format": "date" }
        }
      },
      "Comment": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "asset_id": { "type": "integer", "nullable": true },
          "project_id": { "type": "integer", "nullable": true },
          "parent_comment_id": { "type": "integer", "nullable": true },
          "author_id": { "type": "integer" },
          "author_email": { "type": "string" },
          "body": { "type": "string" },
          "mentions": { "type": "array", "items": { "type": "integer" } },
          "resolved": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      }
    }
  },
  "paths": {
    "/api/studio/health": {
      "get": {
        "tags": ["Auth"],
        "summary": "Service health check",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": { "application/json": { "example": { "ok": true, "formats": 68, "providers": 8 } } }
          }
        }
      }
    },
    "/api/auth/me": {
      "get": {
        "tags": ["Auth"],
        "summary": "Current user / session",
        "responses": {
          "200": {
            "description": "User profile",
            "content": { "application/json": { "example": { "authenticated": true, "user": { "id": 4, "email": "you@example.com", "role": "admin", "display_name": "Juliano" }, "workspace": { "id": 1, "slug": "kobow" }, "source": "session", "sso_enabled": true } } }
          }
        }
      }
    },
    "/api/auth/google": {
      "get": { "tags": ["Auth"], "summary": "Begin Google OAuth", "security": [], "responses": { "302": { "description": "Redirect to Google consent" } } }
    },
    "/api/auth/google/callback": {
      "get": { "tags": ["Auth"], "summary": "OAuth callback (Google → SlotForge)", "security": [], "responses": { "302": { "description": "Redirect to dashboard, sets sf_session cookie" } } }
    },
    "/api/auth/logout": {
      "post": { "tags": ["Auth"], "summary": "Revoke session", "responses": { "200": { "description": "OK" } } }
    },
    "/api/studio/generate_raw": {
      "post": {
        "tags": ["Generate"],
        "summary": "Generate image from text prompt",
        "description": "Decrements your monthly quota by 1. Free plan returns 402 when quota is exhausted; paid plans accrue overage at $0.06/gen.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenRequest" } } } },
        "responses": {
          "200": { "description": "Generated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenResponse" } } } },
          "402": { "description": "Quota exceeded — upgrade plan", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "400": { "description": "Bad input" }
        }
      }
    },
    "/api/studio/generate_with_refs": {
      "post": {
        "tags": ["Generate"],
        "summary": "Generate with reference image(s)",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/GenRequest" }, { "type": "object", "properties": { "reference_asset_ids": { "type": "array", "items": { "type": "integer" } } } }] } } } },
        "responses": { "200": { "description": "Generated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenResponse" } } } } }
      }
    },
    "/api/studio/edit_image": {
      "post": {
        "tags": ["Generate"],
        "summary": "Edit an existing asset",
        "requestBody": { "required": true, "content": { "application/json": { "example": { "asset_id": 4865, "prompt": "make the background more emerald and add bubbles" } } } },
        "responses": { "200": { "description": "Edited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenResponse" } } } } }
      }
    },
    "/api/studio/upscale": {
      "post": {
        "tags": ["Generate"],
        "summary": "Upscale an asset",
        "requestBody": { "required": true, "content": { "application/json": { "example": { "asset_id": 4865, "scale": 2 } } } },
        "responses": { "200": { "description": "Upscaled" } }
      }
    },
    "/api/studio/chat_edit_prompt": {
      "post": {
        "tags": ["Generate"],
        "summary": "Refine a prompt via Chat Studio",
        "requestBody": { "required": true, "content": { "application/json": { "example": { "prompt": "mermaid coin", "instruction": "make it ornate art-nouveau" } } } },
        "responses": { "200": { "description": "Refined prompt" } }
      }
    },
    "/api/studio/search_semantic": {
      "post": {
        "tags": ["Search"],
        "summary": "Vector search the asset library",
        "requestBody": { "required": true, "content": { "application/json": { "example": { "query": "mermaid gold coin", "limit": 20 } } } },
        "responses": { "200": { "description": "Search results" } }
      }
    },
    "/api/studio/projects": {
      "get": {
        "tags": ["Projects"],
        "summary": "List projects",
        "responses": { "200": { "description": "Array of projects", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Project" } } } } } }
      },
      "post": {
        "tags": ["Projects"],
        "summary": "Create a project (designer/approver/admin only)",
        "requestBody": { "required": true, "content": { "application/json": { "example": { "slug": "atlantis_mermaid_v2", "name": "Atlantis Mermaid v2", "theme": "mermaid" } } } },
        "responses": { "201": { "description": "Created" }, "403": { "description": "Role denied" } }
      }
    },
    "/api/studio/projects/{id}": {
      "get": {
        "tags": ["Projects"],
        "summary": "Get a project",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "responses": { "200": { "description": "Project", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Project" } } } } }
      }
    },
    "/api/studio/projects/{id}/assets": {
      "get": {
        "tags": ["Projects"],
        "summary": "List assets attached to a project",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "responses": { "200": { "description": "Project assets" } }
      }
    },
    "/api/studio/projects/{id}/symbols": {
      "get": { "tags": ["Projects"], "summary": "List symbols (paytable rows)", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }], "responses": { "200": { "description": "OK" } } },
      "post": { "tags": ["Projects"], "summary": "Upsert a symbol", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }], "requestBody": { "required": true, "content": { "application/json": {} } }, "responses": { "200": { "description": "Upserted" } } }
    },
    "/api/studio/projects/{id}/paytable": {
      "get": { "tags": ["Projects"], "summary": "Get paytable", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }], "responses": { "200": { "description": "OK" } } },
      "put": { "tags": ["Projects"], "summary": "Replace paytable", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }], "requestBody": { "required": true, "content": { "application/json": {} } }, "responses": { "200": { "description": "OK" } } }
    },
    "/api/studio/projects/{id}/simulate": {
      "post": { "tags": ["Projects"], "summary": "Run a Monte Carlo math simulation", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }], "requestBody": { "required": true, "content": { "application/json": { "example": { "spins": 10000 } } } }, "responses": { "200": { "description": "Simulation result (RTP, hit_frequency, volatility, biggest_win)" } } }
    },
    "/api/studio/projects/{id}/compliance_report": {
      "post": {
        "tags": ["Compliance"],
        "summary": "Generate a signed GLI compliance PDF",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "requestBody": { "required": true, "content": { "application/json": { "example": { "jurisdiction": "MN" } } } },
        "responses": { "200": { "description": "PDF binary (Content-Disposition: attachment)", "content": { "application/pdf": {} } } }
      }
    },
    "/api/studio/compliance/jurisdictions": {
      "get": { "tags": ["Compliance"], "summary": "List supported jurisdictions", "responses": { "200": { "description": "Array" } } }
    },
    "/api/studio/brands": {
      "get": { "tags": ["Brands"], "summary": "List brands", "responses": { "200": { "description": "Array", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Brand" } } } } } } },
      "post": { "tags": ["Brands"], "summary": "Create a brand (designer/approver/admin)", "requestBody": { "required": true, "content": { "application/json": {} } }, "responses": { "201": { "description": "Created" } } }
    },
    "/api/studio/brands/{slug}": {
      "get": { "tags": ["Brands"], "summary": "Get a brand", "parameters": [{ "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }], "responses": { "200": { "description": "Brand" } } }
    },
    "/api/studio/assets/{id}": {
      "get": { "tags": ["Assets"], "summary": "Get an asset", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }], "responses": { "200": { "description": "Asset", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Asset" } } } } } }
    },
    "/api/studio/assets/{id}/status": {
      "post": {
        "tags": ["Assets"],
        "summary": "Change asset status (approver/admin only)",
        "description": "Status transitions: draft → review → approved → cert_submitted → production. Each transition is signed with the workspace Ed25519 approver key.",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "requestBody": { "required": true, "content": { "application/json": { "example": { "status": "approved", "note": "looks great" } } } },
        "responses": { "200": { "description": "Updated" }, "403": { "description": "Role denied" } }
      }
    },
    "/api/studio/assets/{id}/versions": {
      "get": { "tags": ["Assets"], "summary": "Version timeline for an asset", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }], "responses": { "200": { "description": "Versions sorted desc" } } }
    },
    "/api/studio/assets/{id}/versions/{n}/diff": {
      "get": {
        "tags": ["Assets"], "summary": "Pixel diff vs another version",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } },
          { "name": "n", "in": "path", "required": true, "schema": { "type": "integer" } },
          { "name": "against", "in": "query", "required": true, "schema": { "type": "integer" } }
        ],
        "responses": { "200": { "description": "Diff PNG + stats" } }
      }
    },
    "/api/studio/assets/{id}/revert/{n}": {
      "post": { "tags": ["Assets"], "summary": "Revert to an earlier version (creates new HEAD)", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }, { "name": "n", "in": "path", "required": true, "schema": { "type": "integer" } }], "responses": { "200": { "description": "Reverted" } } }
    },
    "/api/studio/audit": {
      "get": { "tags": ["Audit"], "summary": "List audit events", "responses": { "200": { "description": "Events" } } }
    },
    "/api/studio/audit/pubkey": {
      "get": { "tags": ["Audit"], "summary": "Ed25519 public key for signature verification", "responses": { "200": { "description": "PEM public key" } } }
    },
    "/api/studio/webhooks": {
      "get": { "tags": ["Webhooks"], "summary": "List webhooks", "responses": { "200": { "description": "Array", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Webhook" } } } } } } },
      "post": { "tags": ["Webhooks"], "summary": "Create webhook", "requestBody": { "required": true, "content": { "application/json": {} } }, "responses": { "201": { "description": "Created" } } }
    },
    "/api/studio/webhooks/{id}/test": {
      "post": { "tags": ["Webhooks"], "summary": "Send ping payload to webhook", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }], "responses": { "200": { "description": "Delivery result" } } }
    },
    "/api/studio/webhooks/event_types": {
      "get": { "tags": ["Webhooks"], "summary": "List supported event types", "security": [], "responses": { "200": { "description": "Array of strings" } } }
    },
    "/api/studio/comments": {
      "get": { "tags": ["Comments"], "summary": "List comments on an asset or project", "parameters": [{ "name": "asset_id", "in": "query", "schema": { "type": "integer" } }, { "name": "project_id", "in": "query", "schema": { "type": "integer" } }], "responses": { "200": { "description": "Threaded comments" } } },
      "post": { "tags": ["Comments"], "summary": "Add a comment with optional @mentions", "requestBody": { "required": true, "content": { "application/json": { "example": { "asset_id": 4865, "body": "hair could be more emerald @julianomachado" } } } }, "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Comment" } } } } } }
    },
    "/api/studio/comments/stream": {
      "get": { "tags": ["Comments"], "summary": "Subscribe to comment events (Server-Sent Events)", "parameters": [{ "name": "asset_id", "in": "query", "schema": { "type": "integer" } }], "responses": { "200": { "description": "text/event-stream" } } }
    },
    "/api/billing/plans": {
      "get": { "tags": ["Billing"], "summary": "Plan catalog", "security": [], "responses": { "200": { "description": "Plans + stripe_configured flag" } } }
    },
    "/api/billing/me": {
      "get": { "tags": ["Billing"], "summary": "Current workspace subscription", "responses": { "200": { "description": "Subscription", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Subscription" } } } } } }
    },
    "/api/billing/checkout": {
      "post": { "tags": ["Billing"], "summary": "Begin Stripe Checkout session", "requestBody": { "required": true, "content": { "application/json": { "example": { "plan_id": "starter" } } } }, "responses": { "200": { "description": "Returns hosted Stripe URL" }, "503": { "description": "Stripe not configured" } } }
    },
    "/api/billing/portal": {
      "post": { "tags": ["Billing"], "summary": "Stripe Customer Portal link", "responses": { "200": { "description": "Hosted portal URL" } } }
    },
    "/api/billing/invoices": {
      "get": { "tags": ["Billing"], "summary": "Last 50 invoices", "responses": { "200": { "description": "Invoices" } } }
    },
    "/api/billing/webhook": {
      "post": { "tags": ["Billing"], "summary": "Stripe webhook receiver", "security": [], "responses": { "200": { "description": "Received" } } }
    },
    "/api/studio/export_sprite_atlas": {
      "post": { "tags": ["Export"], "summary": "Export project as one packed sprite atlas (PNG + JSON manifest)", "requestBody": { "required": true, "content": { "application/json": { "example": { "project_id": 1 } } } }, "responses": { "200": { "description": "URL to atlas" } } }
    },
    "/api/studio/export_multires": {
      "post": { "tags": ["Export"], "summary": "Export multiple resolution variants", "requestBody": { "required": true, "content": { "application/json": { "example": { "project_id": 1, "sizes": ["256", "512", "1024"] } } } }, "responses": { "200": { "description": "URL to ZIP" } } }
    },
    "/api/studio/export_project_zip": {
      "post": { "tags": ["Export"], "summary": "Export entire project (assets + manifest + audit)", "requestBody": { "required": true, "content": { "application/json": { "example": { "project_id": 1, "include_audit": true } } } }, "responses": { "200": { "description": "URL to ZIP" } } }
    },
    "/api/studio/costs": {
      "get": { "tags": ["Audit"], "summary": "Cost report (USD by day / provider / project)", "responses": { "200": { "description": "Aggregated costs" } } }
    },
    "/api/studio/api_keys": {
      "get": { "tags": ["Auth"], "summary": "List API keys (current user / workspace)", "responses": { "200": { "description": "Keys (last 4 chars only)" } } },
      "post": { "tags": ["Auth"], "summary": "Mint a new API key", "requestBody": { "required": true, "content": { "application/json": { "example": { "name": "ci-server", "scope": "rw" } } } }, "responses": { "201": { "description": "Created — key returned once in plaintext" } } }
    },
    "/api/studio/gen_queue": {
      "post": { "tags": ["Jobs"], "summary": "Enqueue an async generation job", "requestBody": { "required": true, "content": { "application/json": { "example": { "prompt": "wild symbol", "size": "1024x1024" } } } }, "responses": { "202": { "description": "Job accepted; check /api/studio/jobs/:id" } } }
    },
    "/api/studio/jobs/{id}": {
      "get": { "tags": ["Jobs"], "summary": "Job status", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }], "responses": { "200": { "description": "Job" } } }
    },
    "/api/studio/formats": {
      "get": { "tags": ["Generate"], "summary": "List canonical formats (sizes, aspect ratios)", "security": [], "responses": { "200": { "description": "Array of formats" } } }
    },
    "/api/studio/providers": {
      "get": { "tags": ["Generate"], "summary": "List image-gen providers", "responses": { "200": { "description": "Array of providers" } } }
    },
    "/api/studio/workspaces": {
      "get": { "tags": ["Workspaces"], "summary": "List workspaces the current user can access", "responses": { "200": { "description": "Array" } } }
    },
    "/api/studio/me": {
      "get": { "tags": ["Auth"], "summary": "Legacy current-user endpoint (use /api/auth/me)", "responses": { "200": { "description": "User" } } }
    }
  }
}
