# SocialCutter API > SocialCutter is an image processing API that turns a single source image into correctly > sized assets for every social platform (Instagram, Facebook, X/Twitter, LinkedIn, YouTube, > TikTok) in one request. Outputs are stored in GridFS and returned as public URLs. > Processing is metered in "uses": one use per unique destination (platform + format) per request. - Base URL: https://api.socialcutter.theboomer.dev - OpenAPI 3.1 (JSON): https://docs.socialcutter.theboomer.dev/openapi.json - OpenAPI 3.1 (YAML): https://docs.socialcutter.theboomer.dev/openapi.yaml - Interactive reference (Scalar): https://docs.socialcutter.theboomer.dev/ - MCP server: https://mcp.socialcutter.theboomer.dev/mcp - Product site: https://socialcutter.theboomer.dev - Dashboard: https://dash.socialcutter.theboomer.dev ## Authentication Every protected endpoint accepts either of two credential forms. Pick one; both identify the same user account. 1. Clerk session JWT (dashboard / browser clients) - Header: `Authorization: Bearer ` - RS256, verified against the Clerk instance JWKS. `exp`, `nbf`, `iss` and (when configured) `azp` are enforced. No `aud` claim is required. 2. API key (direct programmatic use) - Header: `X-API-Key: ` (preferred) - Alias: `Authorization: Bearer ` - Create keys with `POST /api/v1/apikeys`. The secret is returned exactly once. - Only ONE active API key per user: creating a second key while one is active returns HTTP 400. Revoke first with `DELETE /api/v1/apikeys/{key_id}`. - Revoked keys return HTTP 401. Public endpoints (no credentials required): `GET /api/v1/health`, `GET /api/v1/platforms`, `GET /api/v1/formats`, `GET /api/v1/fit-modes`, `GET /api/v1/billing/pricing-plans`, `GET /api/v1/billing/credit-packs`, `GET /api/v1/storage/{file_id}`. `POST /api/v1/billing/webhook` is authenticated by the Stripe signature header, not by JWT or API key. Example (API key): ```bash curl -X POST https://api.socialcutter.theboomer.dev/api/v1/images/process \ -H "X-API-Key: $SOCIALCUTTER_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 8f2c1d9e4b" \ -d '{ "source": { "type": "url", "value": "https://example.com/photo.jpg" }, "destinations": [ { "platform": "instagram", "format": "post" }, { "platform": "instagram", "format": "story" }, { "platform": "linkedin", "format": "post" } ], "options": { "format": "webp", "quality": 85 } }' ``` ## Endpoints ### Image processing (tag: process) — metered - `POST /api/v1/images/process` — Process an image given as a URL or base64 source. Returns one output per destination. Charges 1 use per unique destination. - `POST /api/v1/images/process/upload` — Same, but the image is sent as a real multipart file (no base64 round-trip) plus a `destinations` JSON string form field. Max 5 MB. - `POST /api/v1/images/upload` — Convenience form: raw base64 image string in the JSON body. - `POST /api/v1/images/upload/file` — Legacy multipart upload variant. Prefer `/images/process/upload`. - `POST /api/v1/images/batch` — Process many images in one call. Charges one use per unique destination per image; failed items are refunded and reported per index. - `GET /api/v1/images/{image_id}` — Retrieve the stored record of a previous processing job. Public. - `GET /api/v1/platforms` — Supported platforms with every format's width, height and aspect ratio. Public. - `GET /api/v1/formats` — Supported output formats. Public. - `GET /api/v1/fit-modes` — Supported fit modes with descriptions. Public. ### API keys (tag: apikeys) - `GET /api/v1/apikeys` — List the caller's API keys (metadata only: id, name, dates, preview). - `POST /api/v1/apikeys` — Create a key; the secret is returned once. One active key per user (HTTP 400 otherwise). Body: `{"name": "Default"}`. - `DELETE /api/v1/apikeys/{key_id}` — Revoke a key permanently. HTTP 404 if not found. ### Identity (tag: auth) - `GET /api/v1/auth/me` — Identity of the authenticated caller (id, email, name, clerk_id). - `POST /api/v1/auth/sync` — Synchronise the Clerk user into the account store. ### Wallet and credits (tags: wallet, credits) - `GET /api/v1/wallet` — Wallet summary: daily quota, daily used, daily remaining, extra (bonus) bag, purchased balance, plan, reset timestamp. - `GET /api/v1/credits` — Same data in the dashboard's legacy shape (`remaining`, `total`, `used`, `plan`, ...). ### Coupons (tag: coupons) - `POST /api/v1/coupons` — Create a use-coupon owned by the caller. Body: `{"ttl_days": 30, "max_redemptions": 5}`. - `GET /api/v1/coupons/mine` — List the caller's coupons with their redemption log. - `POST /api/v1/coupons/redeem` — Redeem a code. Body: `{"code": "ABC123"}`. Both sides are rewarded immediately. - `GET /api/v1/coupons/{code}` — Validate a code and describe its reward without redeeming it. ### Billing (tag: billing) - `GET /api/v1/billing/pricing-plans` — Public plan catalogue with prices and wallet limits. - `GET /api/v1/billing/credit-packs` — Public one-time credit packs (never expire, no cap). - `GET /api/v1/billing/summary` — Subscription state read from Stripe plus the wallet's extra balance. - `POST /api/v1/billing/create-checkout-session` — Open Stripe Checkout for a plan. HTTP 409 if a live subscription already exists. - `POST /api/v1/billing/buy-credits` — Open a one-time Stripe Checkout for a credit pack. - `POST /api/v1/billing/create-portal-session` — Open the Stripe self-service portal. - `POST /api/v1/billing/create-user-profile` — Ensure the user's Stripe customer exists. - `POST /api/v1/billing/webhook` — Stripe webhook; signature-authenticated, idempotent per `event.id`. ### History and storage (tags: history, storage) - `GET /api/v1/history` — Paginated list of the caller's processed images. `limit` 1-100 (default 50), `skip` >= 0. - `GET /api/v1/storage/{file_id}` — Serve a processed output from GridFS. Public, cacheable for one year. ### Health (tag: health) - `GET /api/v1/health` — Liveness plus MongoDB connectivity. Public. Returns `status`, `version`, `uptime`, `database`. ## Limits and quotas - Upload size: 5 MB (5,242,880 bytes) on multipart endpoints; larger files return HTTP 413. - Daily plan quotas (renewed every UTC day): free 3, basic 10, pro 30, agency 100 uses/day. - Coupon reward per side (symmetric): free 50, basic 50, pro 100, agency 150 uses. - Accumulated bonus-bag cap: free 150, basic 300, pro 600, agency 1500 uses. - Purchased credit packs never expire and have no cap. - Charge granularity: 1 use per unique `(platform, format)` pair per request; duplicates in the same request are free. - Failed processing is refunded automatically. - `Idempotency-Key` request header makes retries safe; without it a random key is generated per request. - `limit` on history: 1-100. `skip`: >= 0. - Coupons: `ttl_days` 1-365, `max_redemptions` 1-1000, code length 3-16 characters. - `options.quality` 1-100 (default 85). `options.format` one of `png`, `jpg`, `jpeg`, `webp`, `gif` (default `webp`). - Supported fit modes: `cover` (fill and crop), `contain` (fit with padding, uses `background_color`), `fill` (stretch), `stretch` (force exact dimensions). - Rate limit: no fixed request-per-second limit; usage is bounded by the wallet (HTTP 429 when the quota is exhausted). ## Platform and format reference | platform | format | size (px) | aspect ratio | |---|---|---|---| | instagram | post | 1080x1080 | 1:1 | | instagram | story | 1080x1920 | 9:16 | | instagram | landscape | 1080x566 | 1.91:1 | | facebook | post | 1200x630 | 1.91:1 | | facebook | story | 1080x1920 | 9:16 | | facebook | cover | 820x312 | 2.63:1 | | twitter | post | 1200x675 | 16:9 | | twitter | header | 1500x500 | 3:1 | | linkedin | post | 1200x627 | 1.91:1 | | linkedin | cover | 1128x191 | 5.9:1 | | youtube | thumbnail | 1280x720 | 16:9 | | youtube | banner | 2560x1440 | 16:9 | | tiktok | cover | 1080x1920 | 9:16 | Live equivalent: `GET /api/v1/platforms`. ## Errors - 400 — invalid payload: unknown platform/format/fit mode, bad JSON, or (on `POST /api/v1/apikeys`) an API key is already active. - 401 — missing, malformed, expired or revoked credentials. - 404 — unknown resource (image id, file id, key id). - 409 — billing conflict: a live subscription already exists. - 413 — uploaded file exceeds the 5 MB limit. - 422 — request validation error (`detail`, `errors`). - 429 — wallet quota exhausted: `detail.code = "quota_exceeded"` with `limit`, `used`, `extra_balance`, `purchased_balance`, `plan`, `upgrade_url`. - 500 — processing failure (the charged uses for that request are refunded). ## MCP server For agent and LLM clients, the same capabilities are exposed over Model Context Protocol: - Endpoint: https://mcp.socialcutter.theboomer.dev/mcp (streamable HTTP transport). ## Optional - Health probe: https://api.socialcutter.theboomer.dev/api/v1/health - Version header: every API response carries `X-Tentpole-Version` with the running build version. - Image optimiser (free, used to shrink oversized uploads): https://imageoptim.theboomer.dev/