{"version":1,"resources":["https://stablekey.dev/api/process","https://stablekey.dev/api/jobs","https://stablekey.dev/api/jobs/:jobId","DELETE https://stablekey.dev/api/jobs/:jobId"],"mppResources":["https://stablekey.dev/api/process"],"description":"VFX-grade green-screen unmixing via Corridor Digital's CorridorKey neural keyer. Submit a green-screen plate from StableUpload, get back alpha + straight foreground + premultiplied RGBA mp4s ready for compositing. Operated at cost-plus-defensive-buffer; surplus reinvested. Model © Corridor Digital — corridordigital.com.","instructions":"# StableKey API\n\nVFX-grade green-screen unmixing via Corridor Digital's CorridorKey neural\nkeyer. Stablekey runs CorridorKey on Modal NVIDIA B200 GPUs (with L40S fallback for capacity), takes a green-screen\nplate from StableUpload, and returns clean alpha + un-multiplied straight\nforeground + premultiplied RGBA mp4s ready for compositing.\n\nBase URL: `https://stablekey.dev`\n\nOperated at cost-plus-defensive-buffer (2× modeled cost). Surplus is\nreinvested into capacity, not extracted as profit. Model © Corridor Digital.\nStar their repo: https://github.com/nikopueringer/CorridorKey\n\n## What you get back\n\nFor every job, three mp4s land at agent-provided StableUpload public URLs:\n\n- **alpha** — the predicted alpha matte (linear). Grayscale mp4, sharp at\n  hair / motion-blur / translucency edges where most \"AI roto\" tools give\n  you a binary mask.\n- **fg** — the recovered straight (un-multiplied) foreground in sRGB. The\n  green spill has been mathematically undone, not just suppressed. To\n  composite, convert to linear before multiplying by alpha.\n- **processed** — the production output: linear premultiplied RGBA\n  encoded as RGB-on-black mp4 for easy preview. Drop into Premiere/Resolve\n  on top of a new background and it just works.\n\n## Required Workflow\n\nStableKey does not accept raw file uploads. Per D8 in the architecture:\nthe agent reserves every storage slot itself on stableupload.dev. There\nis no cross-app billing.\n\n```\n1. POST stableupload.dev /api/upload          # input plate\n   → upload bytes to returned uploadUrl\n2. POST stableupload.dev /api/upload × 3      # output reservations\n   → DO NOT upload bytes; forward the URLs to stablekey\n3. POST stablekey.dev /api/process            # paid: GPU compute only\n   → returns { jobId }\n4. GET  stablekey.dev /api/jobs/{jobId}       # poll every 5-10s\n```\n\nFor \"gnarly\" shots (see below), insert a step between 1 and 2:\n`POST stablesam.dev /api/segment` with prompt \"person\", upload the resulting\nmask mp4 to stableupload, and pass that URL as `hintUrl` on /api/process.\n\n## Hint generator (default: inline chroma)\n\nStablekey generates the coarse alpha hint inline using a fast chroma\nthreshold + dilate + blur. **For clean studio plates this is empirically\nequivalent in quality to SAM-3.1-generated hints.** No setup, no extra\ncalls, no extra cost — just submit `/api/process` without `hintUrl`.\n\n**Bring your own `hintUrl`** (typically by calling\n`stablesam.dev /api/segment` first) only for gnarly shots:\n\n- subject is wearing green clothing (chroma threshold will erase it)\n- complex non-green BG props (lighting equipment, etc.) you don't want\n  keyed in\n- multiple subjects in frame, you only want some of them\n- translucent FG objects (smoke, glass, sheer fabric) where green shows\n  through partly\n- visible tracking-dot crosses on the green wall (chroma will pick them up)\n\nThe added cost is ~$0.05/clip for stablesam plus an extra StableUpload\ntier purchase for the mask mp4.\n\n## Internal model resolution: always 2048×2048\n\nCorridorKey resizes every input plate to a 2048×2048 working grid for\ninference, then resizes outputs back to your declared\n`resolutionWidth × resolutionHeight`. Two practical consequences:\n\n- **Cost is sub-linear in input resolution.** Empirical (L40S\n  compile=default B=1, 250-frame 1080p smoke test): 4K is ~1.7× the\n  per-frame cost of 1080p, not 4×. **Don't downscale your input to save\n  money** — submit at the resolution your output needs.\n- **Aspect ratio is preserved by default.** Non-square inputs are\n  letterboxed to 2048² and cropped back.\n- **There is no quality benefit to inputs above 2048².** Anything larger\n  gets downscaled before inference.\n\n## Color space contract\n\nCorridorKey expects the input plate as sRGB by default; declare\n`colorSpace: \"linear\"` if your source is linear (e.g. EXR sequences\nre-encoded as mp4 in linear gamma). Get this wrong and the output looks\nwashed out / crushed.\n\n**No refund on validation mismatch (D7).** Modal validates declared\n`frameCount`, `resolutionWidth`, `resolutionHeight` against ffprobe BEFORE\nrunning inference. Compute locally:\n\n```bash\nffprobe -v error -count_packets \\\n  -show_entries stream=nb_read_packets,width,height \\\n  -of csv=p=0 your_plate.mp4\n```\n\n## Idempotency\n\nPass an opaque `clientRequestId` (UUID, ≤128 chars) on every retry. Same\n`(walletAddress, clientRequestId)` returns the existing job WITHOUT\nre-billing.\n\n## /api/process — $0.05+ per video job\n\n```json\n{\n  \"type\": \"stablekey-video\",\n  \"rgbUrl\": \"https://f.stableupload.dev/abc123/plate.mp4\",\n  \"declared\": {\n    \"frameCount\":      250,\n    \"resolutionWidth\": 1920,\n    \"resolutionHeight\":1080,\n    \"colorSpace\":      \"srgb\"\n  },\n  \"outputs\": {\n    \"alpha\":     {\"uploadUrl\": \"...\", \"publicUrl\": \"...\"},\n    \"fg\":        {\"uploadUrl\": \"...\", \"publicUrl\": \"...\"},\n    \"processed\": {\"uploadUrl\": \"...\", \"publicUrl\": \"...\"}\n  },\n  \"clientRequestId\": \"ck_2026_04_26_abc\"\n}\n```\n\nOptional: `hintUrl` (gnarly shots), `options` (CorridorKey knobs).\n\nPricing: `max(0.05, frames × (0.4 + max(0, mp - 2) × 0.085) / 3600 × $1.95 × 2)`.\n250-frame 1080p ≈ $0.10. 250-frame 4K ≈ $0.15.\n\n## /api/jobs/{jobId} — SIWX-authed status\n\n`{status: \"queued\"|\"processing\"|\"complete\"|\"failed\", result?: {outputs, metrics}, error?}`.\nPoll every 5-10s. Typical wall-time: ~2 min for a 250-frame 1080p clip.\n\nIf `error.code` is `ValidationMismatch`, declared frameCount/resolution\ndidn't match ffprobe; correct + retry with fresh `clientRequestId`.\n\n## /api/jobs — SIWX-authed list\n\nPagination via `?cursor=…&limit=50`.\n\n## DELETE /api/jobs/{jobId} — soft-delete a finished job\n"}