{"openapi":"3.1.0","info":{"title":"LittleDemo API","version":"1.0.0","description":"AI-powered product demo video generator. Turn any product URL into a polished demo video that can be embedded anywhere and refreshed over time.","contact":{"email":"support@littledemo.com","url":"https://littledemo.com"}},"servers":[{"url":"https://littledemo.com","description":"Production"}],"security":[],"tags":[{"name":"Projects","description":"Manage projects (groups of demos for a single site)"},{"name":"Demos","description":"Create, manage, and refresh demo videos"},{"name":"Media","description":"Serve video, GIF, and thumbnail assets"},{"name":"API Keys","description":"Manage API keys for programmatic access"},{"name":"Usage","description":"Check plan and usage information"},{"name":"Credits","description":"Credit packs, top-off settings, and transaction history"},{"name":"Backgrounds","description":"Presentation background catalog and uploads"},{"name":"Embeds","description":"Dynamic embed generation and SDK integration"},{"name":"Progress","description":"Generation progress over WebSocket or HTTP polling"},{"name":"Webhooks","description":"CI/CD integration for auto-refresh"},{"name":"Device Auth","description":"Browser-based CLI login (device authorization flow)"},{"name":"Health","description":"Service health check"}],"components":{"schemas":{"ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"OkResponse":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]},"Project":{"type":"object","properties":{"id":{"type":"string"},"user_id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"viewport":{"type":"string"},"refresh_schedule":{"type":"string"},"webhook_secret":{"type":["string","null"]},"signing_secret":{"type":"string","description":"Returned only on project creation. Store it server-side and rotate if exposed."},"created_at":{"type":"string"},"updated_at":{"type":"string"},"demo_count":{"type":"number"}},"required":["id","user_id","name","url","viewport","refresh_schedule","webhook_secret","created_at","updated_at"]},"CreateProjectBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"url":{"type":"string","format":"uri"},"viewport":{"type":"string","enum":["desktop","mobile","tablet"],"default":"desktop"}},"required":["name","url"]},"ProjectList":{"type":"object","properties":{"projects":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/Project"},{"type":"object","properties":{},"required":["demo_count"]}]}}},"required":["projects"]},"ProjectDetail":{"allOf":[{"$ref":"#/components/schemas/Project"},{"type":"object","properties":{"demos":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":["string","null"]},"prompt":{"type":"string"},"status":{"type":"string"},"thumbnail_key":{"type":["string","null"]},"snapshots":{"type":["string","null"]},"video_key":{"type":["string","null"]},"gif_key":{"type":["string","null"]},"duration_ms":{"type":["number","null"]},"format":{"type":"string"},"version":{"type":"number"},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","title","prompt","status","thumbnail_key","snapshots","video_key","gif_key","duration_ms","format","version","created_at","updated_at"]}}},"required":["demos"]}]},"ProjectSigningSecret":{"type":"object","properties":{"signing_secret":{"type":"string"}},"required":["signing_secret"]},"UpdateProjectBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"url":{"type":"string","format":"uri"},"viewport":{"type":"string","enum":["desktop","mobile","tablet"],"default":"desktop"},"refresh_schedule":{"type":"string","enum":["manual","daily","weekly","on-deploy"],"default":"manual"}}},"ProjectRefreshResult":{"type":"object","properties":{"queued":{"type":"number"},"jobs":{"type":"array","items":{"type":"string"}}},"required":["queued","jobs"]},"DemoCreated":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending"]},"jobId":{"type":"string"},"pollUrl":{"type":"string"},"wsUrl":{"type":"string"}},"required":["id","status","jobId","pollUrl","wsUrl"]},"CreateDemoBody":{"type":"object","properties":{"project_id":{"type":"string","minLength":1},"prompt":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["mp4","webm"],"default":"mp4"},"resolution":{"type":"string","enum":["720p","1080p"],"default":"1080p"},"duration_target":{"type":"string","enum":["5s","10s","30s"],"default":"10s"},"presentation":{"$ref":"#/components/schemas/Presentation"}},"required":["project_id","prompt"]},"Presentation":{"type":"object","properties":{"background":{"anyOf":[{"type":"string"},{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"angle":{"type":"number"}},"required":["from","to"]},{"type":"object","properties":{"imageUrl":{"type":"string","maxLength":2048,"format":"uri"}},"required":["imageUrl"]}]},"mockup":{"type":"string","enum":["macos-light","macos-dark","minimal","none"]},"zoom":{"type":"object","properties":{"enabled":{"type":"boolean"},"intensity":{"type":"number","minimum":1,"maximum":3},"transitionMs":{"type":"number","minimum":100,"maximum":2000}},"required":["enabled"]},"padding":{"type":"number","minimum":0,"maximum":0.3},"cursor":{"$ref":"#/components/schemas/Cursor"},"clickEffect":{"$ref":"#/components/schemas/ClickEffect"},"fade":{"$ref":"#/components/schemas/Fade"},"windowAnimation":{"$ref":"#/components/schemas/WindowAnimation"},"recordingTemplate":{"$ref":"#/components/schemas/RecordingTemplate"},"debugHud":{"type":"boolean"}}},"Cursor":{"type":"object","properties":{"style":{"type":"string","enum":["dot","arrow","crosshair","circle","none"]},"color":{"type":"string","maxLength":50},"size":{"type":"number","minimum":8,"maximum":64}}},"ClickEffect":{"type":"object","properties":{"style":{"type":"string","enum":["pulse","ripple","glow","none"]},"color":{"type":"string","maxLength":50},"scale":{"type":"number","minimum":0.2,"maximum":3}}},"Fade":{"type":"object","properties":{"inMs":{"type":"number","minimum":0,"maximum":3000},"outMs":{"type":"number","minimum":0,"maximum":3000},"color":{"type":"string","enum":["black","white"]}}},"WindowAnimation":{"type":"object","properties":{"intro":{"type":"boolean"},"outro":{"type":"boolean"},"introDurationMs":{"type":"number","minimum":200,"maximum":2000},"outroDurationMs":{"type":"number","minimum":200,"maximum":2000}}},"RecordingTemplate":{"type":"object","properties":{"id":{"type":"string","enum":["auto","cinematic","guided-tour","plain-scroll","section-focus","hero-spotlight","feature-sweep","conversion-flow","pricing-focus","proof-scroll"]},"sectionHint":{"type":"string","minLength":1,"maxLength":120},"effectiveId":{"type":"string","enum":["cinematic","guided-tour","plain-scroll","section-focus","hero-spotlight","feature-sweep","conversion-flow","pricing-focus","proof-scroll"]},"autoSelection":{"type":"object","properties":{"selectedId":{"type":"string","enum":["cinematic","guided-tour","plain-scroll","section-focus","hero-spotlight","feature-sweep","conversion-flow","pricing-focus","proof-scroll"]},"sectionHint":{"type":"string","minLength":1,"maxLength":120},"confidence":{"type":"number","minimum":0,"maximum":1},"reason":{"type":"string","minLength":1,"maxLength":500},"source":{"type":"string","enum":["heuristic","ai","fallback"]},"signals":{"type":"array","items":{"type":"string","minLength":1,"maxLength":240},"maxItems":8}},"required":["selectedId","confidence","reason","source"]}},"required":["id"]},"DemoList":{"type":"object","properties":{"demos":{"type":"array","items":{"$ref":"#/components/schemas/Demo"}}},"required":["demos"]},"Demo":{"type":"object","properties":{"id":{"type":"string"},"project_id":{"type":"string"},"prompt":{"type":"string"},"status":{"type":"string"},"title":{"type":["string","null"]},"action_script":{"type":["string","null"],"description":"JSON-encoded recorder script. Present after planning succeeds."},"ai_plan":{"type":["string","null"],"description":"JSON-encoded action plan generated by AI."},"snapshots":{"type":["string","null"],"description":"JSON-encoded snapshot metadata array."},"video_key":{"type":["string","null"]},"gif_key":{"type":["string","null"]},"thumbnail_key":{"type":["string","null"]},"duration_ms":{"type":["number","null"]},"recording_log":{"type":["string","null"],"description":"JSON-encoded per-step recorder execution log."},"format":{"type":"string"},"resolution":{"type":"string"},"duration_target":{"type":["string","null"]},"context":{"type":["string","null"]},"context_hash":{"type":["string","null"]},"presentation":{"type":["string","null"],"description":"JSON-encoded presentation config, including recordingTemplate metadata."},"recorder_variants":{"type":["string","null"],"description":"JSON-encoded recorder variant status map."},"version":{"type":"number"},"error":{"type":["string","null"]},"created_at":{"type":"string"},"updated_at":{"type":"string"},"refreshed_at":{"type":["string","null"]}},"required":["id","project_id","prompt","status","title","action_script","ai_plan","snapshots","video_key","gif_key","thumbnail_key","duration_ms","recording_log","format","resolution","duration_target","context","presentation","version","error","created_at","updated_at"]},"DemoDetail":{"allOf":[{"$ref":"#/components/schemas/Demo"},{"type":"object","properties":{"latestJob":{"type":["object","null"],"properties":{"id":{"type":"string"},"status":{"type":"string"},"step":{"type":["string","null"]},"started_at":{"type":["string","null"]},"completed_at":{"type":["string","null"]},"error":{"type":["string","null"]}},"required":["id","status","step","started_at","completed_at","error"]},"refreshHistory":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"triggered_by":{"type":"string"},"result":{"type":"string"},"old_version":{"type":["number","null"]},"new_version":{"type":["number","null"]},"error":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","triggered_by","result","old_version","new_version","error","created_at"]}}},"required":["latestJob","refreshHistory"]}]},"UpdateDemoBody":{"type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":500},"context":{"type":"string","maxLength":2000}}},"DemoRefreshResult":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["refreshing","recording"]},"jobId":{"type":"string"}},"required":["id","status","jobId"]},"RestyleDemoBody":{"type":"object","properties":{"presentation":{"$ref":"#/components/schemas/Presentation"}},"required":["presentation"]},"ApiKeyCreated":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"prefix":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["live","publishable"]},"project_id":{"type":["string","null"]},"allowed_domains":{"type":["array","null"],"items":{"type":"string"}}},"required":["id","key","prefix","name","type","project_id","allowed_domains"]},"CreateApiKeyBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"type":{"type":"string","enum":["live","publishable"],"default":"live"},"project_id":{"type":"string"},"allowed_domains":{"type":"array","items":{"type":"string","maxLength":253},"maxItems":20}}},"ApiKeyList":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}},"required":["keys"]},"ApiKey":{"type":"object","properties":{"id":{"type":"string"},"key_prefix":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["live","publishable"]},"project_id":{"type":["string","null"]},"allowed_domains":{"type":["string","null"],"description":"JSON-encoded domain allowlist for publishable keys.","example":"[\"example.com\",\"docs.example.com\"]"},"last_used_at":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","key_prefix","name","type","project_id","allowed_domains","last_used_at","created_at"]},"Usage":{"type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"monthlyDemos":{"type":"number"},"maxDurationSec":{"type":"number"},"resolution":{"type":"string"},"watermark":{"type":"boolean"},"price":{"type":"number"}},"required":["id","name","monthlyDemos","maxDurationSec","resolution","watermark","price"]},"usage":{"type":"object","properties":{"demosThisMonth":{"type":"number"},"demosLimit":{"type":"number"},"demosRemaining":{"type":"number"}},"required":["demosThisMonth","demosLimit","demosRemaining"]},"creditBalance":{"type":"number"},"autoTopoff":{"$ref":"#/components/schemas/AutoTopoff"},"creditPacks":{"type":"array","items":{"$ref":"#/components/schemas/CreditPack"}},"totalRemaining":{"type":"number"},"demoCount":{"type":"number"}},"required":["plan","usage","creditBalance","autoTopoff","creditPacks","totalRemaining","demoCount"]},"AutoTopoff":{"type":"object","properties":{"enabled":{"type":"boolean"},"packId":{"type":"string","enum":["5","20","50","100"]}},"required":["enabled","packId"]},"CreditPack":{"type":"object","properties":{"id":{"type":"string","enum":["5","20","50","100"]},"credits":{"type":"number"},"price":{"type":"number"},"perCredit":{"type":"number"},"savings":{"type":"number"},"label":{"type":"string"},"popular":{"type":"boolean"}},"required":["id","credits","price","perCredit","savings","label"]},"CreditsResponse":{"type":"object","properties":{"balance":{"type":"number"},"packs":{"type":"array","items":{"$ref":"#/components/schemas/CreditPack"}},"autoTopoff":{"$ref":"#/components/schemas/AutoTopoff"},"recentTransactions":{"type":"array","items":{"$ref":"#/components/schemas/CreditTransaction"}}},"required":["balance","packs","autoTopoff","recentTransactions"]},"CreditTransaction":{"type":"object","properties":{"id":{"type":"string"},"user_id":{"type":"string"},"type":{"type":"string","enum":["purchase","auto_topoff","deduct","refund","bonus"]},"amount":{"type":"number"},"balance_after":{"type":"number"},"pack_id":{"type":["string","null"]},"description":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","user_id","type","amount","balance_after","pack_id","description","created_at"]},"PurchaseCreditsResponse":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"creditsAdded":{"type":"number"},"balance":{"type":"number"},"transactionId":{"type":"string"},"pack":{"$ref":"#/components/schemas/CreditPack"}},"required":["success","creditsAdded","balance","transactionId","pack"]},"PurchaseCredits":{"type":"object","properties":{"packId":{"type":"string","enum":["5","20","50","100"],"description":"Credit pack ID"}},"required":["packId"]},"CreditTransactionsResponse":{"type":"object","properties":{"transactions":{"type":"array","items":{"$ref":"#/components/schemas/CreditTransaction"}}},"required":["transactions"]},"BackgroundCatalog":{"type":"object","properties":{"packs":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/BackgroundAsset"}}},"total":{"type":"number"}},"required":["packs","total"]},"BackgroundAsset":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"theme":{"type":"string"},"imageUrl":{"type":"string"},"thumbnailUrl":{"type":"string"}},"required":["id","name","slug","theme","imageUrl","thumbnailUrl"]},"BackgroundUpload":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"imageUrl":{"type":"string"}},"required":["id","key","imageUrl"]},"EmbedGenerateResponse":{"type":"object","properties":{"demoId":{"type":"string"},"status":{"type":"string"},"embedUrl":{"type":"string"},"cached":{"type":"boolean"}},"required":["demoId","status","embedUrl","cached"]},"EmbedGenerateBody":{"type":"object","properties":{"project_id":{"type":"string"},"prompt":{"type":"string","minLength":1,"maxLength":500},"template":{"type":"string","description":"Optional recording template hint such as auto, guided-tour, pricing-focus, or proof-scroll."},"section":{"type":"string","minLength":1,"maxLength":120,"description":"Optional section hint for section-focused or auto-selected recordings."},"presentation":{"type":"object","additionalProperties":{},"description":"Optional presentation config override, including recordingTemplate."},"context":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"title":{"type":"string","maxLength":200},"headings":{"type":"array","items":{"type":"string","maxLength":200},"maxItems":20},"description":{"type":"string","maxLength":500}}}}},"ProgressState":{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/ProgressEvent"}},"startedAt":{"type":["number","null"]}},"required":["events","startedAt"]},"ProgressEvent":{"type":"object","properties":{"step":{"type":"string"},"message":{"type":"string"},"detail":{"type":"string"},"progress":{"type":"number"},"elapsed":{"type":"number"},"videoUrl":{"type":"string"},"timestamp":{"type":"number"}},"required":["step","message","timestamp"]},"WebhookRefreshResponse":{"type":"object","properties":{"refreshed":{"type":"number"},"jobs":{"type":"array","items":{"type":"object","properties":{"demoId":{"type":"string"},"jobId":{"type":"string"}},"required":["demoId","jobId"]}}},"required":["refreshed","jobs"]},"DeviceCodeResponse":{"type":"object","properties":{"device_code":{"type":"string"},"user_code":{"type":"string"},"verification_uri":{"type":"string"},"verification_uri_complete":{"type":"string"},"expires_in":{"type":"number"},"interval":{"type":"number"}},"required":["device_code","user_code","verification_uri","verification_uri_complete","expires_in","interval"]},"CreateDeviceCodeBody":{"type":"object","properties":{"client_name":{"type":"string","minLength":1,"maxLength":100},"scope":{"type":"string","enum":["write"]}}},"DeviceTokenResponse":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","enum":["bearer"]}},"required":["access_token","token_type"]},"DeviceTokenBody":{"type":"object","properties":{"device_code":{"type":"string","minLength":1,"maxLength":200}},"required":["device_code"]},"DeviceAuthorizeResponse":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"client_name":{"type":"string"}},"required":["ok","client_name"]},"DeviceAuthorizeBody":{"type":"object","properties":{"user_code":{"type":"string","minLength":1,"maxLength":20}},"required":["user_code"]}},"parameters":{},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Live API key, for example `Authorization: Bearer ld_live_...`."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Live or publishable API key, for example `ld_live_...` or `ld_pub_...`."},"cookieAuth":{"type":"apiKey","in":"cookie","name":"better-auth.session_token","description":"Dashboard session cookie."},"webhookSecret":{"type":"apiKey","in":"header","name":"X-Webhook-Secret","description":"Project webhook secret for deploy-triggered refreshes."}}},"paths":{"/api/v1/projects":{"post":{"operationId":"createProject","summary":"Create project","tags":["Projects"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectBody"}}}},"responses":{"201":{"description":"Project created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"operationId":"listProjects","summary":"List projects","tags":["Projects"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Project list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectList"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/projects/{id}":{"get":{"operationId":"getProject","summary":"Get project","tags":["Projects"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Project detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDetail"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateProject","summary":"Update project","tags":["Projects"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProjectBody"}}}},"responses":{"200":{"description":"Project updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteProject","summary":"Delete project","tags":["Projects"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/projects/{id}/signing-secret":{"get":{"operationId":"revealProjectSigningSecret","summary":"Reveal project signing secret","description":"Returns the encrypted-at-rest signing secret used to create trusted runtime embed URLs. Keep this value server-side.","tags":["Projects"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Project signing secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectSigningSecret"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/projects/{id}/signing-secret/rotate":{"post":{"operationId":"rotateProjectSigningSecret","summary":"Rotate project signing secret","description":"Creates a new project signing secret. Existing signed runtime URLs stop working.","tags":["Projects"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"New project signing secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectSigningSecret"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/projects/{id}/refresh":{"post":{"operationId":"refreshProjectDemos","summary":"Refresh all demos in project","tags":["Projects"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Refresh queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectRefreshResult"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/demos":{"post":{"operationId":"createDemo","summary":"Create demo (triggers generation)","tags":["Demos"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDemoBody"}}}},"responses":{"202":{"description":"Demo generation started","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoCreated"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit or quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"operationId":"listDemos","summary":"List demos","tags":["Demos"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"project_id","in":"query"},{"schema":{"type":"string","enum":["pending","recording","refreshing","ready","failed"]},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"Demo list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoList"}}}}}}},"/api/v1/demos/{id}":{"get":{"operationId":"getDemo","summary":"Get demo details","tags":["Demos"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Demo detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoDetail"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateDemo","summary":"Update demo","tags":["Demos"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDemoBody"}}}},"responses":{"200":{"description":"Demo updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Demo"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteDemo","summary":"Delete demo","tags":["Demos"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/demos/{id}/refresh":{"post":{"operationId":"refreshDemo","summary":"Refresh demo","tags":["Demos"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"202":{"description":"Refresh started","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoRefreshResult"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/demos/{id}/restyle":{"post":{"operationId":"restyleDemo","summary":"Re-record demo with updated presentation","description":"Uses the existing action script and records the demo again with a new presentation config. This is useful for changing background, mockup, cursor, click effects, fade, or template metadata without re-planning the whole demo.","tags":["Demos"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestyleDemoBody"}}}},"responses":{"202":{"description":"Restyle recording started","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoRefreshResult"}}}},"400":{"description":"Demo has no recorded script or validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/demos/{id}/video":{"get":{"operationId":"streamDemoVideo","summary":"Stream demo video","tags":["Media"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"variant","in":"query"}],"responses":{"200":{"description":"Video stream (supports Range requests)"},"206":{"description":"Partial video stream for Range requests"},"304":{"description":"Not modified"},"404":{"description":"Not found"}}}},"/api/v1/demos/{id}/gif":{"get":{"operationId":"getDemoGif","summary":"Get demo GIF","tags":["Media"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"variant","in":"query"}],"responses":{"200":{"description":"GIF image"},"404":{"description":"Not found"}}}},"/api/v1/demos/{id}/thumbnail":{"get":{"operationId":"getDemoThumbnail","summary":"Get demo thumbnail","tags":["Media"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"variant","in":"query"}],"responses":{"200":{"description":"Thumbnail PNG"},"404":{"description":"Not found"}}}},"/api/v1/demos/{id}/snapshots/{index}":{"get":{"operationId":"getDemoSnapshot","summary":"Get AI exploration snapshot","description":"Returns a PNG screenshot captured during site analysis. Snapshot metadata is stored in the demo's snapshots field.","tags":["Media"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","pattern":"^\\d+$"},"required":true,"name":"index","in":"path"}],"responses":{"200":{"description":"Snapshot PNG"},"404":{"description":"Not found"}}}},"/api/v1/demos/{id}/debug-stills/{index}":{"get":{"operationId":"getDemoDebugStill","summary":"Get recorder debug still","description":"Returns a JPEG debug still for a recorder variant. Primarily used by the dashboard storyboard/debug view.","tags":["Media"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","pattern":"^\\d+$"},"required":true,"name":"index","in":"path"},{"schema":{"type":"string"},"required":false,"name":"variant","in":"query"}],"responses":{"200":{"description":"Debug still JPEG"},"404":{"description":"Not found"}}}},"/api/v1/api-keys":{"post":{"operationId":"createApiKey","summary":"Create API key","tags":["API Keys"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyBody"}}}},"responses":{"201":{"description":"API key created (plaintext returned only once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreated"}}}}}},"get":{"operationId":"listApiKeys","summary":"List API keys","tags":["API Keys"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"API key list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyList"}}}}}}},"/api/v1/api-keys/{id}":{"delete":{"operationId":"revokeApiKey","summary":"Revoke API key","tags":["API Keys"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Key revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/usage":{"get":{"operationId":"getUsage","summary":"Get current usage and plan info","tags":["Usage"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Usage info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Usage"}}}}}}},"/api/v1/credits":{"get":{"operationId":"getCredits","summary":"Get credit balance, packs, and recent transactions","tags":["Credits"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Credit account summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/credits/purchase":{"post":{"operationId":"purchaseCredits","summary":"Purchase a credit pack","description":"Development implementation directly adds credits. Production checkout can use the same response shape after payment confirmation.","tags":["Credits"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseCredits"}}}},"responses":{"200":{"description":"Credits added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseCreditsResponse"}}}},"400":{"description":"Invalid pack","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/credits/auto-topoff":{"get":{"operationId":"getCreditAutoTopoff","summary":"Get auto top-off settings","tags":["Credits"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Auto top-off config","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoTopoff"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"operationId":"updateCreditAutoTopoff","summary":"Update auto top-off settings","tags":["Credits"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoTopoff"}}}},"responses":{"200":{"description":"Updated auto top-off config","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoTopoff"}}}},"400":{"description":"Invalid pack","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/credits/transactions":{"get":{"operationId":"listCreditTransactions","summary":"List credit transactions","tags":["Credits"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Credit transaction history","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditTransactionsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/backgrounds":{"get":{"operationId":"listBackgrounds","summary":"List available presentation backgrounds","tags":["Backgrounds"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Background catalog grouped by pack","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackgroundCatalog"}}}},"502":{"description":"Upstream background catalog failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/backgrounds/upload":{"post":{"operationId":"uploadBackground","summary":"Upload a custom presentation background","tags":["Backgrounds"],"security":[{"bearerAuth":[]},{"cookieAuth":[]}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}},"responses":{"201":{"description":"Background uploaded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackgroundUpload"}}}},"400":{"description":"Invalid or missing file","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/backgrounds/custom/{key}":{"get":{"operationId":"getCustomBackground","summary":"Serve an uploaded custom background","description":"The key may contain slashes and should be URL-encoded when used from clients.","tags":["Backgrounds"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"key","in":"path"}],"responses":{"200":{"description":"Background image"},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/embed/generate":{"post":{"operationId":"generateEmbedDemo","summary":"Generate or reuse a demo for a dynamic embed","description":"Used by sdk.js with a publishable key. A live API key is also accepted for server-side integrations.","tags":["Embeds"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbedGenerateBody"}}}},"responses":{"200":{"description":"Existing demo reused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbedGenerateResponse"}}}},"202":{"description":"Demo generation started","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbedGenerateResponse"}}}},"400":{"description":"Missing project_id or invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"API key required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Quota or rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/embed/runtime":{"get":{"operationId":"renderSignedRuntimeEmbed","summary":"Render a signed runtime embed","description":"Trusted integration endpoint for server-generated signed iframe URLs. The payload may reference an existing demo or request generation using url, prompt, template, section, and player props.","tags":["Embeds"],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"project_id","in":"query"},{"schema":{"type":"string"},"required":true,"name":"expires","in":"query"},{"schema":{"type":"string"},"required":true,"name":"signature","in":"query"},{"schema":{"type":"string"},"required":false,"name":"payload","in":"query"}],"responses":{"200":{"description":"Embeddable HTML player, loading state, or recoverable error"},"202":{"description":"Demo generation started and loading embed returned"},"400":{"description":"Invalid signed payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing, invalid, or expired signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Project or demo not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Quota or rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/progress/{demoId}":{"get":{"operationId":"getGenerationProgress","summary":"Poll generation progress","description":"HTTP fallback for generation progress. The dashboard normally uses /api/ws/{demoId}; this endpoint returns the current generation state.","tags":["Progress"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"demoId","in":"path"}],"responses":{"200":{"description":"Progress state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgressState"}}}}}}},"/api/ws/{demoId}":{"get":{"operationId":"subscribeGenerationProgress","summary":"Subscribe to generation progress","description":"WebSocket endpoint. Send `{ \"type\": \"ping\" }` to receive a pong; progress messages have `type: \"progress\"` and state replay messages have `type: \"state\"`.","tags":["Progress"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"demoId","in":"path"}],"responses":{"101":{"description":"WebSocket upgrade"},"426":{"description":"Expected WebSocket upgrade","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/webhooks/projects/{id}/refresh":{"post":{"operationId":"refreshProjectDemosWebhook","summary":"CI/CD webhook: refresh all demos in project","tags":["Webhooks"],"security":[{"bearerAuth":[]},{"webhookSecret":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Refresh queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookRefreshResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden (plan restriction)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/device/code":{"post":{"operationId":"createDeviceCode","summary":"Start device authorization flow (CLI login step 1)","description":"Returns a short-lived device code and a user code. The CLI opens the verification URL with the user code pre-filled and polls /device/token until the user authorizes in their browser.","tags":["Device Auth"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDeviceCodeBody"}}}},"responses":{"200":{"description":"Device + user code issued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceCodeResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/device/token":{"post":{"operationId":"pollDeviceToken","summary":"Poll for CLI access token (CLI login step 2)","description":"CLI polls this endpoint after starting a device flow. Returns 400 with error=authorization_pending until the user authorizes in the browser, then returns the access token (single-use — the device code is deleted on retrieval).","tags":["Device Auth"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceTokenBody"}}}},"responses":{"200":{"description":"Access token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceTokenResponse"}}}},"400":{"description":"authorization_pending | expired_token | access_denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"slow_down (rate limited)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/device/authorize":{"post":{"operationId":"authorizeDeviceCode","summary":"Authorize a device code (browser /device page calls this)","tags":["Device Auth"],"security":[{"cookieAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthorizeBody"}}}},"responses":{"200":{"description":"Authorized — CLI will pick up the token on next poll","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthorizeResponse"}}}},"401":{"description":"Not signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Code not found or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Code already used","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/health":{"get":{"operationId":"getHealth","summary":"Health check","tags":["Health"],"responses":{"200":{"description":"Service status","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"service":{"type":"string"},"timestamp":{"type":"string"}},"required":["ok","service","timestamp"]}}}}}}}},"webhooks":{}}