{
  "openapi": "3.1.0",
  "info": {
    "title": "Social Card Studio API",
    "description": "OG image generation API for Ghost/WordPress bloggers. Always returns image/png — use X-SCS-Reason header to determine outcome.",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://your-site.com",
      "description": "Production"
    }
  ],
  "paths": {
    "/api/v1/og": {
      "get": {
        "summary": "Generate social card PNG",
        "description": "Generates a 1200x630 PNG social card for a blog post. ALWAYS returns image/png — never 4xx or 5xx. Check X-SCS-Reason header for outcome.",
        "operationId": "generateSocialCard",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "description": "API token with scs_live_ prefix, linked to a brand kit",
            "schema": {
              "type": "string",
              "pattern": "^scs_live_"
            }
          },
          {
            "name": "title",
            "in": "query",
            "required": true,
            "description": "Post title — displayed on the card",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "template",
            "in": "query",
            "required": false,
            "description": "Template name — defaults to brand kit's default_template setting",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Social card PNG. Always returned — check X-SCS-Reason for outcome.",
            "headers": {
              "X-SCS-Reason": {
                "description": "Outcome of the render request",
                "schema": {
                  "type": "string",
                  "enum": [
                    "ok",
                    "over-quota",
                    "subscription-required",
                    "token-revoked",
                    "kit-paused",
                    "rate-limited",
                    "template-unknown",
                    "render-error",
                    "title-rejected",
                    "origin-blocked"
                  ]
                }
              },
              "X-RateLimit-Limit": {
                "description": "Maximum requests per window for this IP",
                "schema": { "type": "integer" }
              },
              "X-RateLimit-Remaining": {
                "description": "Remaining requests in current window",
                "schema": { "type": "integer" }
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        }
      }
    }
    },
    "/api/fetch-og": {
      "get": {
        "summary": "Scrape OG metadata from a URL",
        "description": "Public endpoint that fetches Open Graph metadata from a given URL. Used by the preview page to populate the try-it card. SSRF-protected: private/reserved IPs are blocked. Rate-limited per IP (10 req/60s).",
        "operationId": "fetchOGMetadata",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "URL to scrape OG metadata from",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OG metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "title": { "type": ["string", "null"] },
                    "description": { "type": ["string", "null"] },
                    "image": { "type": ["string", "null"] },
                    "url": { "type": ["string", "null"] }
                  }
                }
              }
            }
          },
          "400": {
            "description": "url required, unreachable or private, or fetch failed"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/api/v1/og/preview": {
      "get": {
        "summary": "Generate watermarked try-it preview PNG",
        "description": "Unauthenticated endpoint that renders a watermarked OG card for the try-it flow. No token required. Always returns image/png. Bot requests silently get a 1×1 fallback.",
        "operationId": "generatePreview",
        "parameters": [
          {
            "name": "title",
            "in": "query",
            "required": false,
            "description": "Post title (max 200 chars)",
            "schema": { "type": "string", "maxLength": 200 }
          },
          {
            "name": "site_name",
            "in": "query",
            "required": false,
            "description": "Site name (max 150 chars)",
            "schema": { "type": "string", "maxLength": 150 }
          },
          {
            "name": "author",
            "in": "query",
            "required": false,
            "description": "Author name (max 150 chars)",
            "schema": { "type": "string", "maxLength": 150 }
          },
          {
            "name": "template",
            "in": "query",
            "required": false,
            "description": "Template name — defaults to verso",
            "schema": { "type": "string", "enum": ["verso", "dispatch", "margin"] }
          }
        ],
        "responses": {
          "200": {
            "description": "Watermarked preview PNG (or 1×1 fallback on error/bot)",
            "content": {
              "image/png": {
                "schema": { "type": "string", "format": "binary" }
              }
            }
          }
        }
      }
    }
  }