{
  "openapi": "3.1.0",
  "info": {
    "title": "Pixelmuse API",
    "version": "1.0.0",
    "description": "Generate AI images programmatically with Pixelmuse. Same models, same quality as the web app — accessible via a simple REST API.\n\n## Quick Start\n\n### 1. Get an API key\n\nCreate an API key from your [settings](https://www.pixelmuse.studio/settings/api-keys). The key starts with `pm_live_` and is shown once — save it somewhere safe.\n\n### 2. Generate your first image\n\n```bash\ncurl -X POST https://www.pixelmuse.studio/api/v1/images \\\n  -H \"Authorization: Bearer pm_live_YOUR_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"prompt\": \"a cyberpunk cityscape at night, neon lights reflecting in rain puddles\",\n    \"model\": \"flux-schnell\"\n  }'\n```\n\nThe response includes an image object. If the image is ready immediately, status will be `succeeded` with output URLs. For async models, you'll get `processing` — poll `GET /api/v1/images/{id}` until it completes.\n\n### 3. Wait for completion (optional)\n\nAdd the `Prefer: wait=30` header to have the API wait up to 30 seconds for the image to complete before responding:\n\n```bash\ncurl -X POST https://www.pixelmuse.studio/api/v1/images \\\n  -H \"Authorization: Bearer pm_live_YOUR_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Prefer: wait=30\" \\\n  -d '{\"prompt\": \"a mountain at sunset\", \"model\": \"nano-banana-pro\"}'\n```\n\n## Authentication\n\nAll authenticated endpoints require an `Authorization` header:\n\n```\nAuthorization: Bearer pm_live_your_api_key_here\n```\n\nAPI keys are scoped to your account. Every API call consumes credits from the same balance as the web app.\n\n## Idempotency\n\nFor safe retries, include an `Idempotency-Key` header (string, max 255 characters) on `POST /api/v1/images` requests:\n\n```bash\ncurl -X POST https://www.pixelmuse.studio/api/v1/images \\\n  -H \"Authorization: Bearer pm_live_YOUR_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: my-unique-request-id-123\" \\\n  -d '{\"prompt\": \"a cat\", \"model\": \"flux-schnell\"}'\n```\n\n- **Same key + same body** within 24 hours: returns the original response with `X-Idempotent-Replayed: true` header. No credits are charged again.\n- **Same key + different body**: returns `422 idempotency_conflict` error.\n- **No key**: request proceeds normally (no replay protection).\n\nThis is useful for retry logic in unreliable networks — you can safely retry without risking duplicate generations.\n\n## Models\n\n| Model | Credits | Best For |\n|-------|---------|----------|\n| `flux-schnell` | 1 | Quick mockups, ideation |\n| `imagen-3` | 1 | Realistic photos, complex compositions |\n| `nano-banana-pro` | 4 | Text rendering, real-time info, multi-image editing |\n| `recraft-v4` | 1 | Typography, design, composition |\n| `recraft-v4-pro` | 7 | High-res design, art direction |\n\nUse `GET /api/v1/models` to fetch the full list with supported aspect ratios and capabilities.\n\n## Rate Limits\n\nRequests are limited per API key based on your plan:\n\n| Plan | Requests/min |\n|------|-------------|\n| Free | 10 |\n| Starter | 30 |\n| Pro | 60 |\n| Scale | 120 |\n\nEvery response includes rate limit headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`. On `429`, a `Retry-After` header tells you when to retry.\n\n## Errors\n\nAll errors follow a consistent format:\n\n```json\n{\n  \"error\": {\n    \"code\": \"insufficient_credits\",\n    \"message\": \"Insufficient credits. This model requires 3 credits.\"\n  },\n  \"request_id\": \"req_a1b2c3d4\"\n}\n```\n\nEvery response includes an `X-Request-ID` header for debugging.\n\n| Status | Code | Meaning |\n|--------|------|---------|\n| 400 | `invalid_request_error` | Bad parameters |\n| 401 | `authentication_error` | Invalid API key |\n| 402 | `insufficient_credits` | Buy more credits |\n| 422 | `content_policy_violation` | Prompt blocked by moderation |\n| 422 | `idempotency_conflict` | Idempotency key reused with different body |\n| 429 | `rate_limit_error` | Slow down |\n| 500 | `api_error` | Internal error |\n| 503 | `service_unavailable` | Provider is down |\n\n## Machine Payments Protocol (MPP)\n\nAI agents can generate images **without a Pixelmuse account** using the [Machine Payments Protocol](https://docs.stripe.com/payments/machine/mpp). Agents pay per generation via Stripe's Shared Payment Tokens (SPT).\n\n### How it works\n\n1. `GET /api/v1/pricing` — discover per-model pricing\n2. `POST /api/v1/images/generate` — send a generation request (no auth header)\n3. Receive a `402` response with an MPP payment challenge\n4. Authorize payment via your agent's SPT\n5. Retry the request with the MPP credential\n6. Receive the generated image + `Payment-Receipt` header\n\n```bash\n# 1. Check pricing\ncurl https://www.pixelmuse.studio/api/v1/pricing\n\n# 2. Generate (agent handles 402 challenge/credential automatically)\ncurl -X POST https://www.pixelmuse.studio/api/v1/images/generate \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"prompt\": \"a logo for an AI startup\", \"model\": \"nano-banana-pro\"}'\n```\n\n**Pricing:** Each model has a fixed USD price (e.g., `flux-schnell` = $0.10). See `GET /api/v1/pricing` for the full list.\n\n**Refunds:** If generation fails after payment, the charge is automatically refunded.\n\n**Rate limits:** 30 requests/min per IP, 5 concurrent generations globally.\n\nThis endpoint also accepts `Authorization: Bearer pm_live_...` API keys — in that case it behaves identically to `POST /api/v1/images`.\n\n## Credits\n\nEvery generation costs credits. Check your balance with `GET /api/v1/account`. When you run low, purchase more via the API:\n\n| Package | Credits | Price |\n|---------|---------|-------|\n| `credit_pack` | 100 | $10.00 |\n\nOr subscribe for monthly credits: Starter ($12/mo, 150 credits), Pro ($39/mo, 500 credits), Scale ($99/mo, 1500 credits).\n\n### Purchase flow\n\n1. `GET /api/v1/billing/packages` — list available packages\n2. `POST /api/v1/billing/checkout` — get a Stripe checkout URL\n3. Open the URL in a browser to complete payment\n4. Credits are added automatically — verify with `GET /api/v1/account`\n\n```bash\n# Get a checkout URL\ncurl -X POST https://www.pixelmuse.studio/api/v1/billing/checkout \\\n  -H \"Authorization: Bearer pm_live_YOUR_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"package\": \"credit_pack\"}'\n```\n\n## Pagination\n\nList endpoints use cursor-based pagination. The response includes:\n\n- `has_more`: whether more results exist\n- `next_cursor`: pass this as the `cursor` query parameter to get the next page\n\n```bash\n# First page\ncurl \"https://www.pixelmuse.studio/api/v1/images?limit=10\" \\\n  -H \"Authorization: Bearer pm_live_YOUR_KEY\"\n\n# Next page (use next_cursor from previous response)\ncurl \"https://www.pixelmuse.studio/api/v1/images?limit=10&cursor=2026-02-21T18:00:00.000Z\" \\\n  -H \"Authorization: Bearer pm_live_YOUR_KEY\"\n```\n\n---\n\n## CLI\n\nGenerate images from the terminal with the [Pixelmuse CLI](https://www.npmjs.com/package/pixelmuse). Same models, same credits, same API key.\n\n### Install\n\n```bash\nnpm install -g pixelmuse\npixelmuse login\npixelmuse \"a cat floating through space\"\n```\n\nRequires Node.js 20+. For terminal image previews, install [chafa](https://hpjansson.org/chafa/).\n\n### Commands\n\n| Command | Description |\n|---------|-------------|\n| `pixelmuse \"prompt\"` | Generate an image (default) |\n| `pixelmuse models` | List available models with costs |\n| `pixelmuse account` | Account balance and usage stats |\n| `pixelmuse history` | Recent generations table |\n| `pixelmuse open <id>` | Open a generation in system viewer |\n| `pixelmuse login` | Authenticate with API key |\n| `pixelmuse logout` | Remove stored credentials |\n| `pixelmuse template <cmd>` | Manage prompt templates |\n| `pixelmuse ui` | Launch interactive TUI |\n\n### Flags\n\n| Flag | Description |\n|------|-------------|\n| `-m, --model` | Model ID (default: `nano-banana-pro`) |\n| `-a, --aspect-ratio` | Aspect ratio (default: `1:1`) |\n| `-o, --output` | Output file path |\n| `--json` | Machine-readable JSON output |\n| `--no-preview` | Skip terminal image preview |\n| `--open` | Open result in system viewer |\n| `--clipboard` | Copy image to clipboard |\n| `--watch <file>` | Watch prompt file, regenerate on save |\n\n### Scripting & Pipes\n\n```bash\n# Pipe prompt from stdin\necho \"hero banner for SaaS landing page\" | pixelmuse -o hero.png\n\n# JSON output for scripting\npixelmuse --json \"logo concept\" | jq .output_path\n\n# Watch mode — regenerates when prompt file changes\npixelmuse --watch prompt.txt -o output.png\n```\n\n## MCP Server\n\nThe MCP server lets AI agents (Claude Code, Cursor, Windsurf) generate images, list models, and check your balance.\n\n### Claude Code\n\nAdd to `~/.claude/.mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"pixelmuse\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"pixelmuse-mcp\"],\n      \"env\": {\n        \"PIXELMUSE_API_KEY\": \"pm_live_your_key_here\"\n      }\n    }\n  }\n}\n```\n\n### Cursor / Windsurf\n\nAdd to your MCP settings:\n\n```json\n{\n  \"pixelmuse\": {\n    \"command\": \"npx\",\n    \"args\": [\"-y\", \"pixelmuse-mcp\"],\n    \"env\": {\n      \"PIXELMUSE_API_KEY\": \"pm_live_your_key_here\"\n    }\n  }\n}\n```\n\n### Available Tools\n\n| Tool | Description |\n|------|-------------|\n| `generate_image` | Generate an image from a prompt with model, aspect ratio, style, and output path |\n| `list_models` | List all available models with credit costs |\n| `check_balance` | Check your credit balance and plan info |\n\n## Prompt Templates\n\nSave reusable prompts as YAML files with variables and default settings.\n\n```bash\n# Scaffold a new template\npixelmuse template init product-shot\n\n# Generate with a template\npixelmuse template use blog-thumbnail --var subject=\"React hooks guide\"\n\n# List all templates\npixelmuse template list\n```\n\nTemplate format (`~/.config/pixelmuse-cli/prompts/`):\n\n```yaml\nname: Blog Thumbnail\ndescription: Dark-themed blog post thumbnail\nprompt: >\n  A cinematic {{subject}} on a dark gradient background,\n  dramatic lighting, 8K resolution\ndefaults:\n  model: nano-banana-pro\n  aspect_ratio: \"16:9\"\nvariables:\n  subject: \"code editor with syntax highlighting\"\ntags: [blog, thumbnail, dark]\n```\n\n## Which Interface?\n\nPixelmuse ships four interfaces. They all use the same API and credentials.\n\n| | **CLI** | **TUI** | **MCP Server** | **Claude Skill** |\n|---|---|---|---|---|\n| **What it is** | Command-line tool | Interactive terminal UI | AI agent tool server | Claude Code prompt template |\n| **Launch** | `pixelmuse \"prompt\"` | `pixelmuse ui` | Auto-starts with Claude/Cursor | Auto-triggers on keywords |\n| **Best for** | Scripting, automation, CI/CD | Visual browsing, exploring models | Letting AI agents generate images | Generating images mid-conversation |\n| **Input** | Flags, stdin, pipe, watch mode | Guided wizard with menus | AI decides params from language | Natural language to Claude |\n\n**Rule of thumb:** You type the prompt → CLI or TUI. AI types the prompt → MCP Server or Claude Skill.\n\nFor more details, visit the [CLI documentation](https://www.pixelmuse.studio/developers/cli).",
    "contact": {
      "name": "Pixelmuse",
      "url": "https://www.pixelmuse.studio"
    }
  },
  "servers": [
    {
      "url": "https://www.pixelmuse.studio",
      "description": "Production"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key in the format: pm_live_..."
      }
    },
    "schemas": {
      "GenerateImageRequest": {
        "type": "object",
        "properties": {
          "prompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 10000,
            "description": "Text prompt describing the image to generate.",
            "example": "A serene mountain lake at sunset"
          },
          "model": {
            "type": "string",
            "enum": [
              "flux-schnell",
              "recraft-v4",
              "recraft-v4-pro",
              "imagen-3",
              "nano-banana-pro"
            ],
            "description": "Model identifier to use for generation."
          },
          "aspect_ratio": {
            "type": "string",
            "enum": [
              "1:1",
              "16:9",
              "21:9",
              "3:2",
              "2:3",
              "4:5",
              "5:4",
              "3:4",
              "4:3",
              "9:16",
              "9:21"
            ],
            "default": "1:1",
            "description": "Aspect ratio for the output image."
          },
          "negative_prompt": {
            "type": "string",
            "maxLength": 5000,
            "description": "Things to exclude from the generated image."
          },
          "image_input": {
            "type": "string",
            "description": "Base64 data URL or URL of an input image for img2img. Maximum ~10MB."
          },
          "visibility": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ],
            "default": "private",
            "description": "Image visibility. Defaults to private."
          }
        },
        "required": [
          "prompt",
          "model"
        ]
      },
      "ImageObject": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "img_abc123"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "succeeded",
              "failed"
            ]
          },
          "model": {
            "type": "string",
            "example": "flux-schnell"
          },
          "prompt": {
            "type": "string"
          },
          "output": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "CDN URLs of generated images. Null while pending."
          },
          "credits_charged": {
            "type": "number",
            "example": 1
          },
          "visibility": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ],
            "description": "Image visibility setting."
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO timestamp when the generation completed successfully. Null while pending or processing."
          }
        },
        "required": [
          "id",
          "status",
          "model",
          "prompt",
          "output",
          "credits_charged",
          "visibility",
          "error",
          "created_at",
          "completed_at"
        ]
      },
      "UpdateImageRequest": {
        "type": "object",
        "properties": {
          "visibility": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ],
            "description": "New visibility setting for the image."
          }
        },
        "required": [
          "visibility"
        ]
      },
      "ListImagesQuery": {
        "type": "object",
        "properties": {
          "cursor": {
            "type": "string",
            "description": "ISO datetime pagination cursor from a previous response's next_cursor."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 20,
            "description": "Number of results to return (1-100)."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "succeeded",
              "failed"
            ],
            "description": "Filter by generation status."
          }
        }
      },
      "ListImagesResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ImageObject"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ]
      },
      "ModelObject": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "flux-schnell"
          },
          "name": {
            "type": "string",
            "example": "Flux Schnell"
          },
          "description": {
            "type": "string"
          },
          "credit_cost": {
            "type": "number",
            "example": 1
          },
          "supported_aspect_ratios": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "is_pro": {
            "type": "boolean"
          },
          "supports_image_input": {
            "type": "boolean"
          },
          "strengths": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "weaknesses": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "credit_cost",
          "supported_aspect_ratios",
          "is_pro",
          "supports_image_input",
          "strengths",
          "weaknesses"
        ]
      },
      "AccountResponse": {
        "type": "object",
        "properties": {
          "user_id": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "plan": {
            "type": "string",
            "example": "pro"
          },
          "credits": {
            "type": "object",
            "properties": {
              "total": {
                "type": "number"
              },
              "subscription": {
                "type": "number"
              },
              "purchased": {
                "type": "number"
              }
            },
            "required": [
              "total",
              "subscription",
              "purchased"
            ]
          },
          "rate_limit": {
            "type": "object",
            "properties": {
              "requests_per_minute": {
                "type": "number"
              }
            },
            "required": [
              "requests_per_minute"
            ]
          }
        },
        "required": [
          "user_id",
          "email",
          "plan",
          "credits",
          "rate_limit"
        ]
      },
      "UsageResponse": {
        "type": "object",
        "properties": {
          "period": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "format": "date-time"
              },
              "end": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "start",
              "end"
            ]
          },
          "generations_count": {
            "type": "number"
          },
          "credits_used": {
            "type": "number"
          },
          "by_model": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "model": {
                  "type": "string"
                },
                "count": {
                  "type": "number"
                },
                "credits": {
                  "type": "number"
                }
              },
              "required": [
                "model",
                "count",
                "credits"
              ]
            }
          }
        },
        "required": [
          "period",
          "generations_count",
          "credits_used",
          "by_model"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "example": "invalid_request_error"
              },
              "message": {
                "type": "string",
                "example": "Prompt is required."
              }
            },
            "required": [
              "code",
              "message"
            ]
          },
          "request_id": {
            "type": "string",
            "description": "Unique request identifier for debugging.",
            "example": "req_a1b2c3d4"
          }
        },
        "required": [
          "error",
          "request_id"
        ]
      },
      "MppImageObject": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "gen_abc123"
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded",
              "failed"
            ]
          },
          "model": {
            "type": "string",
            "example": "nano-banana-pro"
          },
          "prompt": {
            "type": "string"
          },
          "output": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "CDN URLs of generated images. Null if generation failed."
          },
          "visibility": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ]
          },
          "price_usd": {
            "type": "number",
            "description": "Amount charged in USD.",
            "example": 0.1
          },
          "price_cents": {
            "type": "number",
            "description": "Amount charged in cents.",
            "example": 10
          },
          "payment_intent_id": {
            "type": "string",
            "description": "Stripe PaymentIntent ID for the charge.",
            "example": "pi_3abc123"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "status",
          "model",
          "prompt",
          "output",
          "visibility",
          "price_usd",
          "price_cents",
          "payment_intent_id",
          "error",
          "created_at",
          "completed_at"
        ]
      },
      "MppChallengeResponse": {
        "type": "object",
        "properties": {},
        "description": "HTTP 402 response with MPP payment challenge headers. The client agent should authorize payment and retry with the credential."
      },
      "ModelPricingObject": {
        "type": "object",
        "properties": {
          "model": {
            "type": "string",
            "example": "flux-schnell"
          },
          "name": {
            "type": "string",
            "example": "Flux Schnell"
          },
          "description": {
            "type": "string"
          },
          "credit_cost": {
            "type": "number",
            "example": 1
          },
          "price_usd": {
            "type": "number",
            "example": 0.1
          },
          "price_cents": {
            "type": "number",
            "example": 10
          },
          "is_pro": {
            "type": "boolean"
          },
          "supports_image_input": {
            "type": "boolean"
          },
          "supported_aspect_ratios": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "strengths": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "weaknesses": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "model",
          "name",
          "description",
          "credit_cost",
          "price_usd",
          "price_cents",
          "is_pro",
          "supports_image_input",
          "supported_aspect_ratios",
          "strengths",
          "weaknesses"
        ]
      },
      "PricingResponse": {
        "type": "object",
        "properties": {
          "usd_per_credit": {
            "type": "number",
            "example": 0.1
          },
          "models": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ModelPricingObject"
            }
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "usd_per_credit",
          "models",
          "updated_at"
        ]
      },
      "PackageObject": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "example": "starter"
          },
          "label": {
            "type": "string",
            "example": "Starter Pack"
          },
          "credits": {
            "type": "number",
            "example": 50
          },
          "bonus_credits": {
            "type": "number",
            "example": 0
          },
          "price_usd": {
            "type": "number",
            "example": 5
          },
          "price_cents": {
            "type": "number",
            "example": 500
          }
        },
        "required": [
          "name",
          "label",
          "credits",
          "bonus_credits",
          "price_usd",
          "price_cents"
        ]
      },
      "ListPackagesResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PackageObject"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "CheckoutRequest": {
        "type": "object",
        "properties": {
          "package": {
            "type": "string",
            "enum": [
              "credit_pack"
            ],
            "description": "Credit package identifier."
          }
        },
        "required": [
          "package"
        ]
      },
      "CheckoutResponse": {
        "type": "object",
        "properties": {
          "checkout_url": {
            "type": "string",
            "format": "uri",
            "example": "https://checkout.stripe.com/c/pay/..."
          },
          "session_id": {
            "type": "string",
            "example": "cs_live_..."
          },
          "package": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "label": {
                "type": "string"
              },
              "credits": {
                "type": "number"
              },
              "bonus_credits": {
                "type": "number"
              },
              "price_usd": {
                "type": "number"
              }
            },
            "required": [
              "name",
              "label",
              "credits",
              "bonus_credits",
              "price_usd"
            ]
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "checkout_url",
          "session_id",
          "package",
          "expires_at"
        ]
      }
    },
    "parameters": {}
  },
  "paths": {
    "/api/v1/images": {
      "post": {
        "summary": "Generate an image",
        "description": "Create a new image generation. The response includes a pending image object. Poll `GET /api/v1/images/{id}` until the status is `succeeded` or `failed`.\n\n**Idempotency:** Send an `Idempotency-Key` header (string, max 255 chars) to safely retry requests. If the same key is reused with the same body within 24 hours, the original response is replayed with an `X-Idempotent-Replayed: true` header. Using the same key with a different body returns a `422 idempotency_conflict` error.",
        "tags": [
          "Images"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateImageRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Image generation completed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImageObject"
                }
              }
            }
          },
          "202": {
            "description": "Image generation started (processing).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImageObject"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key."
          },
          "402": {
            "description": "Insufficient credits."
          },
          "422": {
            "description": "Content policy violation or idempotency conflict."
          },
          "429": {
            "description": "Rate limit exceeded."
          }
        }
      },
      "get": {
        "summary": "List images",
        "description": "List your generated images, ordered by creation date (newest first). Supports cursor-based pagination.",
        "tags": [
          "Images"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "ISO datetime pagination cursor from a previous response's next_cursor."
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "Number of results to return (1-100)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "succeeded",
                "failed"
              ],
              "description": "Filter by generation status."
            },
            "required": false,
            "name": "status",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of images.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListImagesResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key."
          }
        }
      }
    },
    "/api/v1/images/{id}": {
      "get": {
        "summary": "Get an image",
        "description": "Retrieve a single image generation by ID. Use this to poll for completion.",
        "tags": [
          "Images"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Image generation ID."
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The image object.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImageObject"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key."
          },
          "404": {
            "description": "Image not found."
          }
        }
      },
      "patch": {
        "summary": "Update an image",
        "description": "Update properties of an image. Currently supports changing visibility.",
        "tags": [
          "Images"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Image generation ID."
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateImageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated image object.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImageObject"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body."
          },
          "401": {
            "description": "Missing or invalid API key."
          },
          "404": {
            "description": "Image not found."
          }
        }
      },
      "delete": {
        "summary": "Delete an image",
        "description": "Soft-delete an image generation. The image will no longer appear in listings or be retrievable by ID.",
        "tags": [
          "Images"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Image generation ID."
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Image deleted."
          },
          "401": {
            "description": "Missing or invalid API key."
          },
          "404": {
            "description": "Image not found."
          }
        }
      }
    },
    "/api/v1/models": {
      "get": {
        "summary": "List models",
        "description": "List all available image generation models and their capabilities.",
        "tags": [
          "Models"
        ],
        "responses": {
          "200": {
            "description": "A list of available models.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ModelObject"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/models/{id}": {
      "get": {
        "summary": "Get a model",
        "description": "Retrieve details for a specific model.",
        "tags": [
          "Models"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Model identifier."
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The model object.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelObject"
                }
              }
            }
          },
          "404": {
            "description": "Model not found."
          }
        }
      }
    },
    "/api/v1/account": {
      "get": {
        "summary": "Get account info",
        "description": "Retrieve your account details including plan, credits, and rate limits.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Account information.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key."
          }
        }
      }
    },
    "/api/v1/account/usage": {
      "get": {
        "summary": "Get usage stats",
        "description": "Retrieve your usage statistics for the current billing period, broken down by model.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Usage statistics.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key."
          }
        }
      }
    },
    "/api/v1/billing/packages": {
      "get": {
        "summary": "List credit packages",
        "description": "List all available credit packages with pricing. No authentication required.",
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "description": "Available credit packages.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListPackagesResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/checkout": {
      "post": {
        "summary": "Create checkout session",
        "description": "Create a Stripe Checkout session for purchasing credits. Returns a URL to redirect the user to complete payment. The checkout session expires after 24 hours.",
        "tags": [
          "Billing"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Checkout session created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid package name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key."
          }
        }
      }
    },
    "/api/v1/images/generate": {
      "post": {
        "summary": "Generate an image (MPP / API key)",
        "description": "Dual-auth image generation endpoint. Supports two authentication methods:\n\n**1. API Key (Bearer token)** — Same behavior as `POST /api/v1/images`. The request is forwarded internally.\n\n**2. Machine Payments Protocol (MPP)** — Pay-per-generation with no account required. Send a request with no auth or with an MPP credential:\n\n- **No credential / first request:** Returns `402` with an MPP payment challenge. Your agent should authorize payment via its Shared Payment Token (SPT) and retry with the credential.\n- **With valid credential:** Payment is charged, the image is generated, and the response includes a `Payment-Receipt` header.\n\nIf generation fails after payment, the charge is automatically refunded.\n\n### MPP Flow\n\n```\nAgent → POST /api/v1/images/generate (no auth)\nServer → 402 + payment challenge headers\nAgent → authorizes payment via SPT\nAgent → POST /api/v1/images/generate (with MPP credential)\nServer → charges payment, generates image\nServer → 201 + image response + Payment-Receipt header\n```\n\n### Rate Limits (MPP)\n\n| Limit | Value |\n|-------|-------|\n| Requests per minute (per IP) | 30 |\n| Max concurrent generations (global) | 5 |\n\n### Content Moderation\n\nPrompts are moderated **before** payment is charged. If content is flagged, a `422` is returned with no charge.",
        "tags": [
          "Machine Payments (MPP)"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateImageRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Image generated successfully. Includes `Payment-Receipt` header for MPP payments.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MppImageObject"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "MPP payment challenge. Authorize payment and retry with credential. For API key auth, this means insufficient credits."
          },
          "422": {
            "description": "Content policy violation."
          },
          "429": {
            "description": "Rate limit exceeded (IP-based for MPP, key-based for API key auth)."
          },
          "500": {
            "description": "Generation failed. Payment is automatically refunded for MPP."
          },
          "504": {
            "description": "Generation timed out. Payment is automatically refunded for MPP."
          }
        }
      }
    },
    "/api/v1/pricing": {
      "get": {
        "summary": "Get model pricing",
        "description": "Returns per-model pricing in USD and cents. No authentication required. Useful for agents to discover costs before making generation requests.\n\nResponse is cached for 1 hour.",
        "tags": [
          "Machine Payments (MPP)"
        ],
        "responses": {
          "200": {
            "description": "Current pricing for all models.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PricingResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {}
}