{
  "openapi": "3.1.0",
  "info": {
    "title": "SocialCutter API",
    "description": "SocialCutter turns one source image into correctly sized assets for every social\nplatform (Instagram, Facebook, X/Twitter, LinkedIn, YouTube, TikTok) in a single\ncall. Outputs are stored in GridFS and returned as public URLs.\n\n## Base URL\n\n```\nhttps://api.socialcutter.theboomer.dev\n```\n\nMachine-readable specification: [`/openapi.json`](/) · human reference: `/` · AI index: `/llms.txt`.\n\n## Authentication\n\nEvery protected endpoint accepts **either** credential below — pick whichever\nfits your client:\n\n1. **Clerk session JWT (dashboard users).** Send the Clerk session token of the\n   signed-in user as `Authorization: Bearer <clerk_session_jwt>`. Tokens are\n   RS256, verified against the instance JWKS, and `exp` / `nbf` / `iss` /\n   `azp` are enforced.\n2. **API key (direct programmatic use).** Create a key once in the dashboard\n   (or with `POST /api/v1/apikeys`) and send it either as the `X-API-Key`\n   header **or** as `Authorization: Bearer <api_key>` — both are accepted.\n\n```bash\ncurl -X POST https://api.socialcutter.theboomer.dev/api/v1/images/process \\\n  -H \"X-API-Key: sc_live_xxxxxxxxxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"source\":{\"type\":\"url\",\"value\":\"https://example.com/photo.jpg\"},\n       \"destinations\":[{\"platform\":\"instagram\",\"format\":\"post\"}]}'\n```\n\n**One active API key per user.** Creating a key while another one is active\nreturns `400`; revoke the current key first with\n`DELETE /api/v1/apikeys/{key_id}`.\n\n## Metering\n\nProcessing is metered in **uses**. One use is charged per *unique destination*\n(`platform` + `format`) per request — repeating a destination in the same\nrequest is not charged twice. Daily quotas come from the user's plan\n(free 3/day, basic 10/day, pro 30/day, agency 100/day); extra uses earned with\ncoupons or bought as credit packs do not expire. Going over quota returns\n`429` with `detail.code = \"quota_exceeded\"`; failed processing is refunded.\nSend an `Idempotency-Key` header to make retries safe.\n\n## MCP\n\nAn MCP server is available for agent/LLM use at `https://mcp.socialcutter.theboomer.dev/mcp`.\n",
    "version": "1.0.0",
    "contact": {
      "url": "https://socialcutter.theboomer.dev"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "paths": {
    "/api/v1/images/process": {
      "post": {
        "tags": [
          "process"
        ],
        "summary": "Process Image",
        "description": "Process image to social media formats. Stores output in GridFS.",
        "operationId": "process_image_api_v1_images_process_post",
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Idempotency-Key"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProcessRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProcessResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        }
      }
    },
    "/api/v1/images/process/upload": {
      "post": {
        "tags": [
          "process"
        ],
        "summary": "Process Uploaded Image File",
        "description": "Process a multipart-uploaded image file (bytes, no base64 round-trip).",
        "operationId": "process_uploaded_image_file_api_v1_images_process_upload_post",
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Idempotency-Key"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/Body_process_uploaded_image_file_api_v1_images_process_upload_post"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProcessResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        }
      }
    },
    "/api/v1/images/upload": {
      "post": {
        "tags": [
          "process"
        ],
        "summary": "Upload And Process",
        "description": "Upload base64 image and process to social formats.",
        "operationId": "upload_and_process_api_v1_images_upload_post",
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Idempotency-Key"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UploadRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProcessResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        }
      }
    },
    "/api/v1/images/upload/file": {
      "post": {
        "tags": [
          "process"
        ],
        "summary": "Upload File And Process",
        "description": "Upload image file (multipart) and process to social formats.",
        "operationId": "upload_file_and_process_api_v1_images_upload_file_post",
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Idempotency-Key"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/Body_upload_file_and_process_api_v1_images_upload_file_post"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProcessResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        }
      }
    },
    "/api/v1/images/batch": {
      "post": {
        "tags": [
          "process"
        ],
        "summary": "Batch Process",
        "description": "Process batch of images. Charges one use per unique destination per image.",
        "operationId": "batch_process_api_v1_images_batch_post",
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Idempotency-Key"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchImageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        }
      }
    },
    "/api/v1/images/{image_id}": {
      "get": {
        "tags": [
          "process"
        ],
        "summary": "Get Image Status",
        "description": "Get image processing details.\n\nAcepta las dos formas de id que expone el propio API: el id público\n(`img_...`, el que devuelven `POST /images/process*`) y el `ObjectId` de Mongo\n(el que `GET /history` publica como `id`).",
        "operationId": "get_image_status_api_v1_images__image_id__get",
        "parameters": [
          {
            "name": "image_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Image Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": []
      }
    },
    "/api/v1/platforms": {
      "get": {
        "tags": [
          "process"
        ],
        "summary": "List Platforms",
        "description": "List supported platforms.",
        "operationId": "list_platforms_api_v1_platforms_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": []
      }
    },
    "/api/v1/formats": {
      "get": {
        "tags": [
          "process"
        ],
        "summary": "List Formats",
        "description": "List supported output formats.",
        "operationId": "list_formats_api_v1_formats_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": []
      }
    },
    "/api/v1/fit-modes": {
      "get": {
        "tags": [
          "process"
        ],
        "summary": "List Fit Modes",
        "description": "List supported fit modes.",
        "operationId": "list_fit_modes_api_v1_fit_modes_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": []
      }
    },
    "/api/v1/health": {
      "get": {
        "tags": [
          "health"
        ],
        "summary": "Health Check",
        "description": "Health check with MongoDB status.",
        "operationId": "health_check_api_v1_health_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": []
      }
    },
    "/api/v1/auth/me": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "Get Me",
        "description": "Get current user info from Clerk session.",
        "operationId": "get_me_api_v1_auth_me_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/auth/sync": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Sync User",
        "description": "Sync Clerk user data.",
        "operationId": "sync_user_api_v1_auth_sync_post",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/apikeys": {
      "get": {
        "tags": [
          "apikeys"
        ],
        "summary": "List active API keys",
        "description": "Lists the API keys of the caller. Returns metadata only — the secret is shown exactly once, in the creation response. At most one key can be active per user.\n\nReturn the active API keys of the authenticated user, without the secret.",
        "operationId": "list_keys_api_v1_apikeys_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      },
      "post": {
        "tags": [
          "apikeys"
        ],
        "summary": "Create the API key",
        "description": "Creates an API key for the caller and returns the secret **once**; store it securely. **Only one active API key per user is allowed** — if a key is already active the call fails with `400`. Revoke it first with `DELETE /api/v1/apikeys/{key_id}`. The key can then be sent as `X-API-Key` or as `Authorization: Bearer <api_key>`.\n\nMint the user's single active API key. The secret is returned once here; `GET /api/v1/apikeys` only exposes a masked preview. Returns 409 `api_key_exists` when the user already owns an active key.",
        "operationId": "create_key_api_v1_apikeys_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateKeyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateKeyResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/apikeys/{key_id}": {
      "delete": {
        "tags": [
          "apikeys"
        ],
        "summary": "Revoke an API key",
        "description": "Revokes the key, permanently. After revocation requests using that key return `401`, and a new key can be created.\n\nDeactivate a key so it stops authenticating. Frees the slot for a new one.",
        "operationId": "revoke_key_api_v1_apikeys__key_id__delete",
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "key_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Key Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        }
      }
    },
    "/api/v1/credits": {
      "get": {
        "tags": [
          "credits"
        ],
        "summary": "Get Credits",
        "description": "Get current user's wallet summary.\n\nFormato compatible con el front legacy: ``remaining`` = total_remaining\n(cuota diaria restante + bolsa extra ganada + bolsa comprada) y ``total``\n= daily_limit + extra_balance. El resumen es de solo lectura (no escribe\nsaldos).",
        "operationId": "get_credits_api_v1_credits_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/history": {
      "get": {
        "tags": [
          "history"
        ],
        "summary": "Get History",
        "description": "Get processing history for the current user.",
        "operationId": "get_history_api_v1_history_get",
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 100,
              "minimum": 1,
              "default": 50,
              "title": "Limit"
            }
          },
          {
            "name": "skip",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0,
              "title": "Skip"
            }
          },
          {
            "name": "origin",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "pattern": "^(browser|api)$"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Origin"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        }
      }
    },
    "/api/v1/wallet": {
      "get": {
        "tags": [
          "wallet"
        ],
        "summary": "Get Wallet",
        "description": "Resumen del monedero del usuario autenticado.",
        "operationId": "get_wallet_api_v1_wallet_get",
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "service",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "socialcutter",
              "title": "Service"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        }
      }
    },
    "/api/v1/coupons": {
      "post": {
        "tags": [
          "coupons"
        ],
        "summary": "Create Coupon",
        "description": "Crea un cupón de usos propiedad del usuario autenticado.",
        "operationId": "create_coupon_api_v1_coupons_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCouponRequest",
                "default": {
                  "ttl_days": 90
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/coupons/mine": {
      "get": {
        "tags": [
          "coupons"
        ],
        "summary": "List My Coupons",
        "description": "Mis cupones con su registro de canjes (sin PII del que canjea).",
        "operationId": "list_my_coupons_api_v1_coupons_mine_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/coupons/redeem": {
      "post": {
        "tags": [
          "coupons"
        ],
        "summary": "Redeem Coupon",
        "description": "Canjea un cupón: recompensa simétrica e inmediata para ambos lados.",
        "operationId": "redeem_coupon_api_v1_coupons_redeem_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RedeemCouponRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/coupons/usage": {
      "get": {
        "tags": [
          "coupons"
        ],
        "summary": "List Coupon Usage",
        "description": "Uso de mis cupones: canjes paginados (mismo auth que /coupons/mine).",
        "operationId": "list_coupon_usage_api_v1_coupons_usage_get",
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1,
              "title": "Page"
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20,
              "title": "Per Page"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        }
      }
    },
    "/api/v1/coupons/{code}": {
      "get": {
        "tags": [
          "coupons"
        ],
        "summary": "Get Coupon",
        "description": "Valida un código y describe su recompensa sin canjearlo.",
        "operationId": "get_coupon_api_v1_coupons__code__get",
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Code"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        }
      }
    },
    "/api/v1/billing/pricing-plans": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "Pricing Plans",
        "description": "Catálogo público de planes con precios Stripe y límites del monedero.",
        "operationId": "pricing_plans_api_v1_billing_pricing_plans_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": []
      }
    },
    "/api/v1/billing/credit-packs": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "Credit Packs",
        "description": "Packs de usos one-time (compra esporadica, sin caducidad, sin cap).",
        "operationId": "credit_packs_api_v1_billing_credit_packs_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": []
      }
    },
    "/api/v1/billing/summary": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "Summary",
        "description": "Estado de la suscripción leído desde Stripe + saldo extra del monedero.",
        "operationId": "summary_api_v1_billing_summary_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/billing/invoices": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "Invoices",
        "description": "Historial: facturas de suscripción (Stripe) + compras de packs (monedero).",
        "operationId": "invoices_api_v1_billing_invoices_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/billing/create-checkout-session": {
      "post": {
        "tags": [
          "billing"
        ],
        "summary": "Create Checkout Session",
        "description": "Abre Checkout para un plan. 409 si ya hay suscripción viva (invariante).",
        "operationId": "create_checkout_session_api_v1_billing_create_checkout_session_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/billing/buy-credits": {
      "post": {
        "tags": [
          "billing"
        ],
        "summary": "Buy Credits",
        "description": "Abre Checkout one-time para un pack de usos (no cambia el plan).",
        "operationId": "buy_credits_api_v1_billing_buy_credits_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BuyCreditsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/billing/create-portal-session": {
      "post": {
        "tags": [
          "billing"
        ],
        "summary": "Create Portal Session",
        "description": "Portal de autogestión de Stripe para el cliente del usuario.",
        "operationId": "create_portal_session_api_v1_billing_create_portal_session_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortalRequest",
                "default": {
                  "return_url": ""
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/billing/create-user-profile": {
      "post": {
        "tags": [
          "billing"
        ],
        "summary": "Create User Profile",
        "description": "Asegura el cliente Stripe del usuario y su espejo en user_profiles.",
        "operationId": "create_user_profile_api_v1_billing_create_user_profile_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateProfileRequest",
                "default": {
                  "email": ""
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": [
          {
            "clerkJwt": []
          },
          {
            "apiKeyHeader": []
          },
          {
            "apiKeyBearer": []
          }
        ]
      }
    },
    "/api/v1/billing/webhook": {
      "post": {
        "tags": [
          "billing"
        ],
        "summary": "Stripe Webhook",
        "description": "Stripe webhook. Authenticated by the `stripe-signature` header (HMAC of the raw body), **not** by JWT or API key. Idempotent per Stripe `event.id`.\n\nWebhook de Stripe: firma obligatoria e idempotencia por event.id.",
        "operationId": "stripe_webhook_api_v1_billing_webhook_post",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": []
      }
    },
    "/api/v1/storage/{file_id}": {
      "get": {
        "tags": [
          "storage"
        ],
        "summary": "Serve Storage",
        "description": "Serve a file stored in GridFS by its ID.",
        "operationId": "serve_storage_api_v1_storage__file_id__get",
        "parameters": [
          {
            "name": "file_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "File Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": []
      }
    },
    "/": {
      "get": {
        "tags": [
          "root"
        ],
        "summary": "Read Root",
        "operationId": "read_root__get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": []
      }
    },
    "/health": {
      "get": {
        "tags": [
          "root"
        ],
        "summary": "Health Root",
        "description": "Liveness del contenedor, sin dependencias.\n\nDeliberadamente NO toca la base de datos: si Mongo falla, un /health que\ndevuelva error haria que Coolify/Docker reinicie un contenedor sano. El\nestado real (con `db.ping()`) esta en `/api/v1/health`.\nCoolify genera el healthcheck con `wget`, de ahi que la imagen lo instale.",
        "operationId": "health_root_health_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        }
      }
    },
    "/{path}": {
      "get": {
        "tags": [
          "root"
        ],
        "summary": "Serve Frontend",
        "description": "Catch-all SPA: solo las rutas que el front no resuelve localmente.\n\nUn 200 con el JSON de bienvenida sobre una ruta inexistente es indistinguible\nde un éxito para cualquier cliente, así que sin `frontend_dist` la respuesta\nes un 404 real.",
        "operationId": "serve_frontend__path__get",
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Path"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or expired credentials."
          }
        },
        "security": []
      }
    }
  },
  "components": {
    "schemas": {
      "BatchImageRequest": {
        "properties": {
          "images": {
            "items": {
              "$ref": "#/components/schemas/ProcessRequest"
            },
            "type": "array",
            "title": "Images"
          }
        },
        "type": "object",
        "required": [
          "images"
        ],
        "title": "BatchImageRequest",
        "description": "Batch image processing request."
      },
      "Body_process_uploaded_image_file_api_v1_images_process_upload_post": {
        "properties": {
          "file": {
            "type": "string",
            "contentMediaType": "application/octet-stream",
            "title": "File"
          },
          "destinations": {
            "type": "string",
            "title": "Destinations"
          },
          "options": {
            "type": "string",
            "title": "Options"
          }
        },
        "type": "object",
        "required": [
          "file",
          "destinations"
        ],
        "title": "Body_process_uploaded_image_file_api_v1_images_process_upload_post"
      },
      "Body_upload_file_and_process_api_v1_images_upload_file_post": {
        "properties": {
          "file": {
            "type": "string",
            "contentMediaType": "application/octet-stream",
            "title": "File"
          },
          "destinations": {
            "type": "string",
            "title": "Destinations"
          },
          "options": {
            "type": "string",
            "title": "Options",
            "default": "{}"
          }
        },
        "type": "object",
        "required": [
          "file",
          "destinations"
        ],
        "title": "Body_upload_file_and_process_api_v1_images_upload_file_post"
      },
      "BuyCreditsRequest": {
        "properties": {
          "pack_id": {
            "type": "string",
            "title": "Pack Id"
          },
          "success_url": {
            "type": "string",
            "title": "Success Url"
          },
          "cancel_url": {
            "type": "string",
            "title": "Cancel Url"
          }
        },
        "type": "object",
        "required": [
          "pack_id",
          "success_url",
          "cancel_url"
        ],
        "title": "BuyCreditsRequest"
      },
      "CheckoutRequest": {
        "properties": {
          "price_id": {
            "type": "string",
            "title": "Price Id"
          },
          "success_url": {
            "type": "string",
            "title": "Success Url"
          },
          "cancel_url": {
            "type": "string",
            "title": "Cancel Url"
          },
          "coupon": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Coupon"
          }
        },
        "type": "object",
        "required": [
          "price_id",
          "success_url",
          "cancel_url"
        ],
        "title": "CheckoutRequest"
      },
      "CreateCouponRequest": {
        "properties": {
          "ttl_days": {
            "type": "integer",
            "maximum": 365.0,
            "minimum": 1.0,
            "title": "Ttl Days",
            "default": 90
          },
          "max_redemptions": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 1000.0,
                "minimum": 1.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Max Redemptions"
          }
        },
        "type": "object",
        "title": "CreateCouponRequest"
      },
      "CreateKeyRequest": {
        "properties": {
          "name": {
            "type": "string",
            "title": "Name",
            "default": "Default"
          }
        },
        "type": "object",
        "title": "CreateKeyRequest"
      },
      "CreateKeyResponse": {
        "properties": {
          "key": {
            "type": "string",
            "title": "Key"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "message": {
            "type": "string",
            "title": "Message"
          }
        },
        "type": "object",
        "required": [
          "key",
          "name",
          "message"
        ],
        "title": "CreateKeyResponse"
      },
      "CreateProfileRequest": {
        "properties": {
          "email": {
            "type": "string",
            "title": "Email",
            "default": ""
          }
        },
        "type": "object",
        "title": "CreateProfileRequest"
      },
      "Destination": {
        "properties": {
          "platform": {
            "type": "string",
            "title": "Platform",
            "description": "Social platform: instagram, facebook, twitter, linkedin, youtube, tiktok"
          },
          "format": {
            "type": "string",
            "title": "Format",
            "description": "Format type: post, story, cover, etc."
          },
          "fit_mode": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Fit Mode",
            "description": "Fit mode: cover, contain, fill, stretch",
            "default": "cover"
          }
        },
        "type": "object",
        "required": [
          "platform",
          "format"
        ],
        "title": "Destination",
        "description": "Destination platform and format."
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
          }
        },
        "type": "object",
        "title": "HTTPValidationError"
      },
      "ImageOutput": {
        "properties": {
          "platform": {
            "type": "string",
            "title": "Platform"
          },
          "format": {
            "type": "string",
            "title": "Format"
          },
          "url": {
            "type": "string",
            "title": "Url"
          },
          "width": {
            "type": "integer",
            "title": "Width"
          },
          "height": {
            "type": "integer",
            "title": "Height"
          },
          "size_bytes": {
            "type": "integer",
            "title": "Size Bytes"
          }
        },
        "type": "object",
        "required": [
          "platform",
          "format",
          "url",
          "width",
          "height",
          "size_bytes"
        ],
        "title": "ImageOutput",
        "description": "Processed image output."
      },
      "ImageSource": {
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "url",
              "base64"
            ],
            "title": "Type",
            "description": "Source type: url or base64"
          },
          "value": {
            "type": "string",
            "title": "Value",
            "description": "URL or base64 encoded image data"
          }
        },
        "type": "object",
        "required": [
          "type",
          "value"
        ],
        "title": "ImageSource",
        "description": "Image source with type and value."
      },
      "PortalRequest": {
        "properties": {
          "return_url": {
            "type": "string",
            "title": "Return Url",
            "default": ""
          }
        },
        "type": "object",
        "title": "PortalRequest"
      },
      "ProcessMetadata": {
        "properties": {
          "processing_time_ms": {
            "type": "integer",
            "title": "Processing Time Ms"
          },
          "created_at": {
            "type": "string",
            "title": "Created At"
          }
        },
        "type": "object",
        "required": [
          "processing_time_ms",
          "created_at"
        ],
        "title": "ProcessMetadata",
        "description": "Processing metadata."
      },
      "ProcessOptions": {
        "properties": {
          "quality": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 100.0,
                "minimum": 1.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Quality",
            "description": "Output quality 1-100",
            "default": 85
          },
          "format": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Format",
            "description": "Output format: png, jpg, webp",
            "default": "webp"
          },
          "background_color": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Background Color",
            "description": "Background color for contain mode",
            "default": "#000000"
          }
        },
        "type": "object",
        "title": "ProcessOptions",
        "description": "Processing options."
      },
      "ProcessRequest": {
        "properties": {
          "source": {
            "$ref": "#/components/schemas/ImageSource"
          },
          "destinations": {
            "items": {
              "$ref": "#/components/schemas/Destination"
            },
            "type": "array",
            "title": "Destinations"
          },
          "options": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ProcessOptions"
              },
              {
                "type": "null"
              }
            ],
            "default": {
              "quality": 85,
              "format": "webp",
              "background_color": "#000000"
            }
          }
        },
        "type": "object",
        "required": [
          "source",
          "destinations"
        ],
        "title": "ProcessRequest",
        "description": "Process image request."
      },
      "ProcessResponse": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "status": {
            "type": "string",
            "title": "Status"
          },
          "outputs": {
            "items": {
              "$ref": "#/components/schemas/ImageOutput"
            },
            "type": "array",
            "title": "Outputs"
          },
          "metadata": {
            "$ref": "#/components/schemas/ProcessMetadata"
          }
        },
        "type": "object",
        "required": [
          "id",
          "status",
          "outputs",
          "metadata"
        ],
        "title": "ProcessResponse",
        "description": "Process image response."
      },
      "RedeemCouponRequest": {
        "properties": {
          "code": {
            "type": "string",
            "maxLength": 16,
            "minLength": 3,
            "title": "Code"
          }
        },
        "type": "object",
        "required": [
          "code"
        ],
        "title": "RedeemCouponRequest"
      },
      "UploadRequest": {
        "properties": {
          "image": {
            "type": "string",
            "title": "Image",
            "description": "Base64 encoded image data"
          },
          "destinations": {
            "items": {
              "$ref": "#/components/schemas/Destination"
            },
            "type": "array",
            "title": "Destinations"
          },
          "options": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ProcessOptions"
              },
              {
                "type": "null"
              }
            ],
            "default": {
              "quality": 85,
              "format": "webp",
              "background_color": "#000000"
            }
          }
        },
        "type": "object",
        "required": [
          "image",
          "destinations"
        ],
        "title": "UploadRequest",
        "description": "Upload base64 image request."
      },
      "ValidationError": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          },
          "input": {
            "title": "Input"
          },
          "ctx": {
            "type": "object",
            "title": "Context"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      }
    },
    "securitySchemes": {
      "APIKeyHeader": {
        "type": "apiKey",
        "description": "SocialCutter API key (`sc_...`) created in the dashboard. Only one active key per user.",
        "in": "header",
        "name": "X-API-Key"
      },
      "clerkJwt": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Clerk session token (RS256) sent as `Authorization: Bearer <jwt>`. Used by the dashboard. Signature is verified against the instance JWKS; `exp`, `nbf`, `iss` and (when configured) `azp` are enforced."
      },
      "apiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "API key sent in the `X-API-Key` header. Create it with `POST /api/v1/apikeys`; only one key can be active per user."
      },
      "apiKeyBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "The same API key, sent as `Authorization: Bearer <api_key>`. Accepted as an alias of `X-API-Key` for clients that can only set an Authorization header."
      }
    }
  },
  "servers": [
    {
      "url": "https://api.socialcutter.theboomer.dev",
      "description": "Production"
    },
    {
      "url": "http://localhost:8002",
      "description": "Local development"
    }
  ],
  "tags": [
    {
      "name": "process",
      "description": "Image processing: single, batch and multipart upload. Metered in uses."
    },
    {
      "name": "health",
      "description": "Liveness / readiness including MongoDB connectivity. Public."
    },
    {
      "name": "auth",
      "description": "Identity of the authenticated caller (Clerk)."
    },
    {
      "name": "apikeys",
      "description": "API key lifecycle. At most ONE active key per user."
    },
    {
      "name": "credits",
      "description": "Wallet summary in the legacy shape used by the dashboard."
    },
    {
      "name": "wallet",
      "description": "Wallet summary: daily quota + bonus bag + purchased balance."
    },
    {
      "name": "coupons",
      "description": "Create, inspect and redeem use-coupons (symmetric reward)."
    },
    {
      "name": "billing",
      "description": "Stripe subscriptions, one-time credit packs, portal and webhook."
    },
    {
      "name": "history",
      "description": "Previously processed images for the authenticated user."
    },
    {
      "name": "storage",
      "description": "Public GridFS file serving for processed outputs."
    },
    {
      "name": "root",
      "description": "Frontend shell; unknown paths fall through to the SPA index."
    }
  ],
  "security": [
    {
      "clerkJwt": []
    },
    {
      "apiKeyHeader": []
    },
    {
      "apiKeyBearer": []
    }
  ]
}
