{
  "openapi": "3.1.0",
  "info": {
    "title": "Photo AI Studio Agent API",
    "version": "1.0.0",
    "summary": "Generate professional AI photos, edit images, and create video from a selfie — programmatically.",
    "description": "Machine-readable API surface for AI agents. Public discovery endpoints (capabilities, themes, products) require no authentication; generation and account endpoints use a Bearer token plus an X-Buyer-Email header identifying the end user. Also reachable over the Model Context Protocol at /api/mcp (manifest at /.well-known/mcp) and, for interactive checkout, the agentic-commerce endpoints under /api/commerce. Credits are the unit of spend (100 credits = 1 photo); prices and packages are in the product feed and at /pricing.\n\nVersioning: this is v1. Send an optional `API-Version` request header (date- or number-based, e.g. `2026-08-25` or `1`) to pin behavior; without it, the current stable version is used. Breaking changes ship under a new version value and, where structural, a new `/vN/` path.\n\nDeprecation & sunset policy: a version is supported for at least 6 months after its successor ships. When an endpoint or version is deprecated it returns a `Deprecation: true` header and a `Sunset: <HTTP-date>` header (RFC 8594) giving the earliest removal date, plus a `Link` header with `rel=\"deprecation\"` pointing to migration docs. Removal never happens before the advertised Sunset date.",
    "x-sunset-policy": {
      "minSupportWindowMonths": 6,
      "deprecationHeader": "Deprecation",
      "sunsetHeader": "Sunset",
      "docs": "https://www.photoaistudio.com/for-developers"
    },
    "contact": {
      "name": "Photo AI Studio Support",
      "email": "contact@photoaistudio.com",
      "url": "https://www.photoaistudio.com/contact"
    },
    "termsOfService": "https://www.photoaistudio.com/terms-conditions",
    "license": {
      "name": "Proprietary",
      "url": "https://www.photoaistudio.com/terms-conditions"
    }
  },
  "servers": [
    {
      "url": "https://www.photoaistudio.com",
      "description": "Production (v1)"
    }
  ],
  "externalDocs": {
    "description": "Developer guide, llms.txt, and MCP server",
    "url": "https://www.photoaistudio.com/for-developers"
  },
  "tags": [
    {
      "name": "Discovery",
      "description": "Unauthenticated read endpoints for capability, theme, and product discovery."
    },
    {
      "name": "Generation",
      "description": "Authenticated endpoints that spend credits to create or edit media."
    },
    {
      "name": "Account",
      "description": "Authenticated account and prediction-status endpoints."
    },
    {
      "name": "Commerce",
      "description": "Agentic-commerce product feed and checkout (ACP / UCP)."
    },
    {
      "name": "MCP",
      "description": "Hosted Model Context Protocol server and its OAuth 2.1 authorization endpoints."
    }
  ],
  "paths": {
    "/api/agent/capabilities": {
      "get": {
        "tags": [
          "Discovery"
        ],
        "operationId": "getCapabilities",
        "summary": "List every operation, parameter, and credit cost",
        "description": "Unauthenticated. Returns the full machine-readable capability descriptor for the agent API.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "Capability descriptor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Capabilities"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/agent/themes": {
      "get": {
        "tags": [
          "Discovery"
        ],
        "operationId": "listThemes",
        "summary": "List available photo theme categories",
        "description": "Unauthenticated. Returns the live theme catalog.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          },
          {
            "name": "gender",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "male",
                "female",
                "unisex"
              ]
            }
          },
          {
            "name": "tag",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Theme catalog",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThemeList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/commerce/products": {
      "get": {
        "tags": [
          "Discovery",
          "Commerce"
        ],
        "operationId": "listProducts",
        "summary": "List buyable credit packages with live prices",
        "description": "Unauthenticated. Product feed for agentic commerce (ACP / UCP).",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "Product feed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductFeed"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/agent/account": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getAccount",
        "summary": "Get the buyer's account and credit balance",
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "Account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "photos:read",
              "photos:write"
            ]
          },
          {
            "bearerAuth": [],
            "buyerEmail": []
          }
        ]
      }
    },
    "/api/agent/upload": {
      "post": {
        "tags": [
          "Generation"
        ],
        "operationId": "uploadImage",
        "summary": "Upload a source selfie/image",
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "image"
                ],
                "properties": {
                  "image": {
                    "type": "string",
                    "description": "Publicly reachable image URL or base64 data URI."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Uploaded image reference",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "photos:read",
              "photos:write"
            ]
          },
          {
            "bearerAuth": [],
            "buyerEmail": []
          }
        ]
      }
    },
    "/api/agent/generate": {
      "post": {
        "tags": [
          "Generation"
        ],
        "operationId": "generatePhotos",
        "summary": "Generate themed or custom AI photos from a selfie",
        "description": "Spends credits (100 credits per image). Returns a prediction to poll via /api/agent/predictions/{id}.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "images"
                ],
                "properties": {
                  "images": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Uploaded image URL(s). Each must be a Photo AI Studio CDN URL returned by POST /api/agent/upload or by an earlier generation; external image hosts are deprecated and will be refused."
                  },
                  "theme": {
                    "type": "string",
                    "description": "Theme category slug, e.g. professional-headshot."
                  },
                  "prompt": {
                    "type": "string",
                    "description": "Custom prompt when not using a preset theme."
                  },
                  "num_images": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 8,
                    "default": 4
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Prediction created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Prediction"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "photos:read",
              "photos:write"
            ]
          },
          {
            "bearerAuth": [],
            "buyerEmail": []
          }
        ]
      }
    },
    "/api/agent/edit": {
      "post": {
        "tags": [
          "Generation"
        ],
        "operationId": "editImage",
        "summary": "AI-edit an image (background removal, retouch, upscale, face swap, and more)",
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "image",
                  "operation"
                ],
                "properties": {
                  "image": {
                    "type": "string"
                  },
                  "operation": {
                    "type": "string",
                    "description": "One of the 19 edit operations (e.g. remove-background, upscale, retouch, face-swap, clothes-try-on)."
                  },
                  "params": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Prediction created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Prediction"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "photos:read",
              "photos:write"
            ]
          },
          {
            "bearerAuth": [],
            "buyerEmail": []
          }
        ]
      }
    },
    "/api/agent/video": {
      "post": {
        "tags": [
          "Generation"
        ],
        "operationId": "generateVideo",
        "summary": "Create video (image-to-video, UGC, product-holder)",
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type"
                ],
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "image-to-video",
                      "ugc",
                      "product-holder"
                    ]
                  },
                  "image": {
                    "type": "string"
                  },
                  "prompt": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Prediction created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Prediction"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "photos:read",
              "photos:write"
            ]
          },
          {
            "bearerAuth": [],
            "buyerEmail": []
          }
        ]
      }
    },
    "/api/agent/predictions": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "listPredictions",
        "summary": "List the buyer's recent predictions",
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "Predictions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PredictionList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "photos:read",
              "photos:write"
            ]
          },
          {
            "bearerAuth": [],
            "buyerEmail": []
          }
        ]
      }
    },
    "/api/agent/predictions/{id}": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getPrediction",
        "summary": "Poll a prediction until state = succeeded",
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Prediction status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Prediction"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "photos:read",
              "photos:write"
            ]
          },
          {
            "bearerAuth": [],
            "buyerEmail": []
          }
        ]
      }
    },
    "/api/mcp": {
      "post": {
        "tags": [
          "MCP"
        ],
        "operationId": "mcpJsonRpc",
        "summary": "Hosted MCP server (Streamable HTTP)",
        "description": "JSON-RPC 2.0 endpoint implementing the Model Context Protocol over Streamable HTTP. Stateless: every POST carries a complete request and receives a single JSON response. Anonymous callers can list tools and use the four discovery tools. Tools that touch an account answer 401 with a WWW-Authenticate header naming /.well-known/oauth-protected-resource, which starts the OAuth 2.1 flow. A per-user API key works as a bearer token as well.",
        "security": [
          {},
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "photos:read",
              "photos:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "enum": [
                      "2.0"
                    ]
                  },
                  "id": {
                    "description": "Request id. Omit for notifications."
                  },
                  "method": {
                    "type": "string",
                    "description": "MCP method, for example initialize, tools/list, or tools/call."
                  },
                  "params": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              },
              "example": {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "tools/list",
                "params": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jsonrpc": {
                      "type": "string"
                    },
                    "id": {},
                    "result": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "integer"
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The tool needs a connected account. The WWW-Authenticate header names the protected resource metadata document.",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                },
                "description": "Bearer resource_metadata=\"https://www.photoaistudio.com/.well-known/oauth-protected-resource\""
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope this tool requires."
          },
          "429": {
            "description": "Rate limit exceeded. The body is a JSON-RPC error; Retry-After gives the wait in seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jsonrpc": {
                      "type": "string"
                    },
                    "id": {},
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "integer"
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "405": {
            "description": "Only POST carries JSON-RPC. GET and DELETE return this because the server is stateless."
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource": {
      "get": {
        "tags": [
          "MCP"
        ],
        "operationId": "oauthProtectedResourceMetadata",
        "summary": "OAuth 2.0 Protected Resource Metadata (RFC 9728)",
        "description": "Tells an MCP client which authorization server guards /api/mcp. Also served at /.well-known/oauth-protected-resource/api/mcp.",
        "security": [
          {}
        ],
        "responses": {
          "200": {
            "description": "Protected resource metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/oauth-authorization-server": {
      "get": {
        "tags": [
          "MCP"
        ],
        "operationId": "oauthAuthorizationServerMetadata",
        "summary": "OAuth 2.0 Authorization Server Metadata (RFC 8414)",
        "security": [
          {}
        ],
        "responses": {
          "200": {
            "description": "Authorization server metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/oauth/register": {
      "post": {
        "tags": [
          "MCP"
        ],
        "operationId": "oauthRegisterClient",
        "summary": "Dynamic Client Registration (RFC 7591)",
        "description": "Registers a public client and returns a client_id. No credential required, as the RFC specifies. Registration grants no access on its own.",
        "security": [
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "redirect_uris"
                ],
                "properties": {
                  "redirect_uris": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "client_name": {
                    "type": "string"
                  },
                  "client_uri": {
                    "type": "string"
                  },
                  "scope": {
                    "type": "string"
                  },
                  "grant_types": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered client",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid client metadata or redirect URI"
          },
          "429": {
            "description": "Too many registrations from this address"
          }
        }
      }
    },
    "/api/oauth/token": {
      "post": {
        "tags": [
          "MCP"
        ],
        "operationId": "oauthToken",
        "summary": "Exchange an authorization code or refresh an access token",
        "description": "OAuth 2.1 token endpoint. Public clients only: PKCE with S256 replaces the client secret, and refresh tokens rotate on every use.",
        "security": [
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "grant_type"
                ],
                "properties": {
                  "grant_type": {
                    "type": "string",
                    "enum": [
                      "authorization_code",
                      "refresh_token"
                    ]
                  },
                  "code": {
                    "type": "string"
                  },
                  "code_verifier": {
                    "type": "string"
                  },
                  "redirect_uri": {
                    "type": "string"
                  },
                  "refresh_token": {
                    "type": "string"
                  },
                  "client_id": {
                    "type": "string"
                  },
                  "scope": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access_token": {
                      "type": "string"
                    },
                    "token_type": {
                      "type": "string"
                    },
                    "expires_in": {
                      "type": "integer"
                    },
                    "refresh_token": {
                      "type": "string"
                    },
                    "scope": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "invalid_grant, invalid_request, or unsupported_grant_type"
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "ApiVersion": {
        "name": "API-Version",
        "in": "header",
        "required": false,
        "description": "Optional API version to pin (date-based e.g. 2026-08-25, or number e.g. 1). Omit to use the current stable version.",
        "schema": {
          "type": "string",
          "default": "1"
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Bearer credential. Use a per-user API key (pa_sk_) created at https://www.photoaistudio.com/api-keys, an OAuth 2.1 access token (pa_at_) from /api/oauth/token, or a shared partner token together with X-Buyer-Email. Send as: Authorization: Bearer <token>."
      },
      "buyerEmail": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Buyer-Email",
        "description": "Email of the end user the agent is acting for. Users are auto-created."
      },
      "oauth2": {
        "type": "oauth2",
        "description": "OAuth 2.1 authorization code flow with PKCE. Used by remote MCP clients. Discover the endpoints at /.well-known/oauth-authorization-server and the protected resource at /.well-known/oauth-protected-resource.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://www.photoaistudio.com/api/oauth/authorize",
            "tokenUrl": "https://www.photoaistudio.com/api/oauth/token",
            "refreshUrl": "https://www.photoaistudio.com/api/oauth/token",
            "scopes": {
              "photos:read": "Read the account, credit balance, themes, and past generations.",
              "photos:write": "Upload images and spend credits on photos, edits, and videos."
            }
          }
        }
      }
    },
    "schemas": {
      "Capabilities": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string"
          },
          "service": {
            "type": "string"
          },
          "auth": {
            "type": "object",
            "additionalProperties": true
          },
          "operations": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "Theme": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "ThemeList": {
        "type": "object",
        "properties": {
          "themes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Theme"
            }
          }
        }
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "unit_amount": {
            "type": "integer",
            "description": "Price in the smallest currency unit."
          },
          "currency": {
            "type": "string"
          },
          "credits": {
            "type": "integer"
          },
          "photos_equivalent": {
            "type": "integer"
          },
          "availability_status": {
            "type": "string"
          }
        }
      },
      "ProductFeed": {
        "type": "object",
        "properties": {
          "merchant": {
            "type": "object",
            "additionalProperties": true
          },
          "products": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Product"
            }
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "credits": {
            "type": "integer"
          }
        }
      },
      "UploadResult": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "Prediction": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "starting",
              "processing",
              "succeeded",
              "failed"
            ]
          },
          "result_url": {
            "type": "string",
            "format": "uri"
          },
          "credits_spent": {
            "type": "integer"
          }
        }
      },
      "PredictionList": {
        "type": "object",
        "properties": {
          "predictions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Prediction"
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Structured JSON error. Agents should branch on `type` and surface `message`; `resolution` hints how to recover.",
        "required": [
          "type",
          "message"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "Stable machine-readable error code (e.g. invalid_request, unauthorized, insufficient_credits, not_found)."
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation."
          },
          "resolution": {
            "type": "string",
            "description": "How to fix or recover from the error."
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code."
          }
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Unexpected error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid Authorization header",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InsufficientCredits": {
        "description": "Not enough credits to complete the operation",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded. Paid operations (generate, edit, video) are never rate limited — credits are the only cap on how fast you can spend. Limits apply to work that costs you nothing: reads, uploads, public endpoints, requests that fail authentication, and generation requests that consume no credits. The body names the bucket in `scope` and gives `limit`, `window_seconds` and `retry_after`; the RateLimit-* / X-RateLimit-* headers carry the live policy, and a 429 also sends Retry-After.",
        "headers": {
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            },
            "description": "Requests allowed in the current window."
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            },
            "description": "Requests remaining in the current window."
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds until the window resets."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The credential is valid but lacks the required scope, or the account is unavailable",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "bearerAuth": [],
      "buyerEmail": []
    }
  ]
}
