{
  "openapi": "3.1.0",
  "info": {
    "title": "VentaKit HTTP API",
    "version": "0.1.0-draft",
    "description": "VentaKit HTTP routes used by the editor. Authenticate with the account Google session (cookie or OAuth access token). The server applies that account’s limits on generation, credits, and export claim. Pixel export is rendered in the client. Connect an assistant at https://ventakit.com/mcp. GET /api/me without a session returns 200 with authenticated:false.",
    "contact": {
      "name": "VentaKit support",
      "url": "https://ventakit.com/soporte"
    },
    "x-status": "beta",
    "x-agent-docs": "https://ventakit.com/agents.md"
  },
  "servers": [{ "url": "https://ventakit.com" }],
  "tags": [
    { "name": "account" },
    { "name": "generate" },
    { "name": "export" },
    { "name": "brand" },
    { "name": "media" }
  ],
  "paths": {
    "/api/me": {
      "get": {
        "tags": ["account"],
        "summary": "Current user, plan, credits, export eligibility",
        "operationId": "getMe",
        "parameters": [
          {
            "name": "lite",
            "in": "query",
            "schema": { "type": "string", "enum": ["1"] },
            "description": "Omit profile fields when set"
          }
        ],
        "responses": {
          "200": {
            "description": "Account snapshot. Without a session: authenticated false and account limit fields, no user id.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MeResponse" }
              }
            }
          }
        },
        "security": [{ "bearerAuth": [] }, {}]
      }
    },
    "/api/generate": {
      "post": {
        "tags": ["generate"],
        "summary": "Generate or iterate a full carousel",
        "operationId": "postGenerate",
        "parameters": [
          {
            "name": "x-vk-generate-key",
            "in": "header",
            "required": true,
            "schema": { "type": "string", "maxLength": 128 },
            "description": "Idempotency key per user. Same key + same JSON body replays the same job without extra debit."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/GenerateRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Synchronous carousel result",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GenerateSuccess" }
              }
            }
          },
          "202": {
            "description": "Job accepted — poll /api/generate/status",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GenerateAccepted" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ApiError" },
          "409": { "$ref": "#/components/responses/ApiError" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "502": { "$ref": "#/components/responses/ApiError" }
        },
        "security": [{ "bearerAuth": [] }]
      }
    },
    "/api/generate/status": {
      "get": {
        "tags": ["generate"],
        "summary": "Poll async generate job",
        "operationId": "getGenerateStatus",
        "parameters": [
          {
            "name": "jobId",
            "in": "query",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Job state",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GenerateStatus" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ApiError" }
        },
        "security": [{ "bearerAuth": [] }]
      }
    },
    "/api/generate/slide": {
      "post": {
        "tags": ["generate"],
        "summary": "Regenerate a single slide (1 credit)",
        "operationId": "postGenerateSlide",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/GenerateSlideRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated slide payload",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GenerateSlideSuccess" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ApiError" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        },
        "security": [{ "bearerAuth": [] }]
      }
    },
    "/api/export/claim": {
      "get": {
        "tags": ["export"],
        "summary": "Check whether export is allowed",
        "operationId": "getExportClaim",
        "responses": {
          "401": {
            "description": "No Google session",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiError" }
              }
            }
          },
          "200": {
            "description": "Eligibility",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ExportClaimGet" }
              }
            }
          }
        },
        "security": [{ "bearerAuth": [] }]
      },
      "post": {
        "tags": ["export"],
        "summary": "Consume export quota after client render",
        "operationId": "postExportClaim",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ExportClaimPost" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Claim result",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ExportClaimResult" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/ApiError" },
          "403": {
            "description": "Account limit. paywall true and upgrade_url when the account must change plan.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PaywallError" }
              }
            }
          }
        },
        "security": [{ "bearerAuth": [] }]
      }
    },
    "/api/agent/v1/export": {
      "post": {
        "tags": ["export"],
        "summary": "Agent export: 1 credit and a ZIP of PNGs",
        "description": "Costs 1 credit and the account export allowance. Same function as the MCP ventakit_export tool. Google session. Header x-vk-export-key replays the same body without a second charge. POST /api/export/claim does not debit a credit. The response is a short-lived download URL for a ZIP of one PNG per slide. GET /api/agent/v1/export/file needs no session: the query token alone is the credential, one use; a replay of the export key reissues a fresh link without a second charge. The editor download is unchanged.",
        "operationId": "postAgentExport",
        "parameters": [
          {
            "name": "x-vk-export-key",
            "in": "header",
            "required": true,
            "schema": { "type": "string", "maxLength": 80 },
            "description": "Idempotency key per account. Same key and same JSON body replay the claim. A different body returns 409."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/AgentExportRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Claim granted. Body includes download.url for a ZIP of PNGs. raster is true.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AgentExportResult" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ApiError" },
          "401": { "$ref": "#/components/responses/ApiError" },
          "403": {
            "description": "export_denied includes paywall true and upgrade_url. insufficient_scope does not.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PaywallError" }
              }
            }
          },
          "409": { "$ref": "#/components/responses/ApiError" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "503": { "$ref": "#/components/responses/ApiError" }
        },
        "security": [{ "bearerAuth": [] }]
      }
    },
    "/api/agent/v1/export/file": {
      "get": {
        "tags": ["export"],
        "summary": "Download an agent export ZIP",
        "description": "No session required: the query token is the only credential, so plain curl works. Single use: the link dies with its first download. Expired links return 410, consumed or unknown tokens 404. Replaying the export idempotency key reissues a fresh link without a second charge.",
        "operationId": "getAgentExportFile",
        "parameters": [
          { "name": "t", "in": "query", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "application/zip of PNG slides" },
          "404": { "$ref": "#/components/responses/ApiError" },
          "410": { "$ref": "#/components/responses/ApiError" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        },
        "security": []
      }
    },
    "/api/mcp": {
      "post": {
        "tags": ["account"],
        "summary": "MCP streamable HTTP",
        "description": "JSON-RPC for assistants. Google session, OAuth access token, or an existing account key with the tool scope. Same account limits as the editor. Discovery: /.well-known/oauth-protected-resource and /.well-known/mcp.",
        "operationId": "postMcp",
        "responses": {
          "200": { "description": "JSON-RPC result" },
          "401": { "$ref": "#/components/responses/ApiError" },
          "403": { "$ref": "#/components/responses/ApiError" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        },
        "security": [{ "bearerAuth": [] }]
      }
    },
    "/api/media-fetch": {
      "get": {
        "tags": ["media"],
        "summary": "SSRF-safe media proxy for the editor",
        "operationId": "getMediaFetch",
        "parameters": [
          { "name": "url", "in": "query", "required": true, "schema": { "type": "string", "format": "uri" } }
        ],
        "responses": {
          "200": { "description": "Binary body" },
          "400": { "$ref": "#/components/responses/ApiError" },
          "401": { "$ref": "#/components/responses/ApiError" }
        },
        "security": [{ "bearerAuth": [] }]
      }
    },
    "/api/brand": {
      "post": {
        "tags": ["brand"],
        "summary": "Upload or update brand kit (multipart)",
        "operationId": "postBrand",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": { "type": "object", "additionalProperties": true }
            }
          }
        },
        "responses": {
          "200": { "description": "Brand kit saved" },
          "401": { "$ref": "#/components/responses/ApiError" }
        },
        "security": [{ "bearerAuth": [] }]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "clerkSession": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Browser session cookie or session bearer. Same account limits as the editor."
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Google session cookie or OAuth access token for that account."
      }
    },
    "responses": {
      "ApiError": {
        "description": "Structured error",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ApiError" }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ApiError" }
          }
        },
        "headers": {
          "Retry-After": { "schema": { "type": "integer" } }
        }
      }
    },
    "schemas": {
      "PaywallError": {
        "type": "object",
        "required": ["error", "code", "paywall", "upgrade_url"],
        "properties": {
          "error": { "type": "string" },
          "code": { "type": "string" },
          "message": { "type": "string" },
          "paywall": { "type": "boolean" },
          "upgrade_url": { "type": "string" }
        }
      },
      "ApiError": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": { "type": "string" },
          "code": { "type": "string" },
          "message": { "type": "string" },
          "jobId": { "type": "string" },
          "retryAfterSec": { "type": "integer" }
        }
      },
      "MeResponse": {
        "type": "object",
        "additionalProperties": true
      },
      "GenerateRequest": {
        "type": "object",
        "properties": {
          "prompt": { "type": "string", "maxLength": 24000 },
          "instructions": { "type": "string", "maxLength": 24000 },
          "attachments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string" },
                "text": { "type": "string" }
              }
            }
          },
          "aspectRatioId": { "type": "string" },
          "brandVoice": { "type": "string" },
          "designContext": { "type": "object", "additionalProperties": true },
          "currentCarousel": { "type": "object", "additionalProperties": true }
        }
      },
      "GenerateSuccess": {
        "type": "object",
        "properties": {
          "carousel": { "type": "object", "additionalProperties": true },
          "creditsRemaining": { "type": "integer" }
        }
      },
      "GenerateAccepted": {
        "type": "object",
        "required": ["jobId"],
        "properties": { "jobId": { "type": "string" } }
      },
      "GenerateStatus": {
        "type": "object",
        "properties": {
          "status": { "type": "string", "enum": ["queued", "running", "done", "failed"] },
          "carousel": { "type": "object", "additionalProperties": true },
          "creditsRemaining": { "type": "integer" },
          "retryAfterSec": { "type": "integer" },
          "error": { "type": "string" },
          "code": { "type": "string" }
        }
      },
      "GenerateSlideRequest": {
        "type": "object",
        "additionalProperties": true
      },
      "GenerateSlideSuccess": {
        "type": "object",
        "additionalProperties": true
      },
      "ExportClaimGet": {
        "type": "object",
        "additionalProperties": true
      },
      "ExportClaimPost": {
        "type": "object",
        "properties": {
          "format": { "type": "string" }
        }
      },
      "ExportClaimResult": {
        "type": "object",
        "additionalProperties": true
      },
      "AgentExportRequest": {
        "type": "object",
        "required": ["format", "carousel"],
        "properties": {
          "format": { "type": "string", "enum": ["png", "zip", "pdf"] },
          "carousel": { "type": "object", "additionalProperties": true }
        }
      },
      "AgentExportResult": {
        "type": "object",
        "required": ["ok", "raster", "files"],
        "properties": {
          "ok": { "type": "boolean" },
          "granted": { "type": "string" },
          "exportsUsed": { "type": ["integer", "null"] },
          "format": { "type": "string" },
          "raster": { "type": "boolean" },
          "download": {
            "type": "object",
            "properties": {
              "url": { "type": "string" },
              "expiresAt": { "type": "string" },
              "ttlSec": { "type": "integer" }
            }
          },
          "replay": { "type": "boolean" },
          "delivery": { "type": "string" },
          "slideCount": { "type": "integer" },
          "files": { "type": "array", "items": {} },
          "carousel": { "type": "object", "additionalProperties": true },
          "message": { "type": "string" }
        }
      }
    }
  }
}
