{"openapi":"3.1.0","info":{"title":"ZeroDeploy API","version":"1.0.0","description":"REST API for ZeroDeploy - a zero-friction deployment platform for frontend SPAs","contact":{"name":"ZeroDeploy Support","url":"https://zerodeploy.dev/docs"},"license":{"name":"MIT","url":"https://opensource.org/licenses/MIT"}},"servers":[{"url":"https://api.zerodeploy.dev","description":"Production"},{"url":"http://localhost:8787","description":"Local development"}],"tags":[{"name":"Auth","description":"Authentication and user management"},{"name":"Organizations","description":"Organization management"},{"name":"Sites","description":"Site management within organizations"},{"name":"Deployments","description":"Deployment operations"},{"name":"Domains","description":"Custom domain management"},{"name":"Tokens","description":"Deploy token management for CI/CD"},{"name":"Forms","description":"Form submission management"},{"name":"Analytics","description":"Site traffic analytics"},{"name":"Drop","description":"Zero-auth instant deploy — one HTTP call, no login required"},{"name":"Health","description":"Health check endpoints"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token from GitHub OAuth login, or deploy token (zd_* prefix) for CI/CD"}},"schemas":{"HealthResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"version":{"type":"string"},"env":{"type":"string"}},"required":["status","version","env"]},"ReadyResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded"]},"version":{"type":"string"},"env":{"type":"string"},"latency":{"type":"number"},"checks":{"type":"object","properties":{"database":{"$ref":"#/components/schemas/HealthCheck"},"storage":{"$ref":"#/components/schemas/HealthCheck"},"cache":{"$ref":"#/components/schemas/HealthCheck"}},"required":["database","storage","cache"]}},"required":["status","version","env","latency","checks"]},"HealthCheck":{"type":"object","properties":{"ok":{"type":"boolean"},"latency":{"type":"number"},"error":{"type":"string"}},"required":["ok"]},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"hint":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]},"MagicLinkBody":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"user@example.com"}},"required":["email"]},"User":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"username":{"type":"string"},"email":{"type":"string","nullable":true,"format":"email"},"avatar_url":{"type":"string","nullable":true},"is_admin":{"type":"boolean"},"has_site":{"type":"boolean"},"personal_org":{"type":"object","nullable":true,"properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"plan":{"type":"string","enum":["free","starter","pro"]},"subscription_status":{"type":"string","nullable":true,"enum":["trialing","active","canceled","revoked","past_due",null]},"can_deploy":{"type":"boolean"}},"required":["id","slug","plan","subscription_status","can_deploy"]},"notifications":{"type":"object","properties":{"deploy_success":{"type":"boolean"},"deploy_failure":{"type":"boolean"}},"required":["deploy_success","deploy_failure"]}},"required":["id","username","email","avatar_url","is_admin","has_site","personal_org","notifications"]},"UpdateMeBody":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"newemail@example.com"},"notifications":{"type":"object","properties":{"deploy_success":{"type":"boolean"},"deploy_failure":{"type":"boolean"}}}}},"Org":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"plan":{"type":"string","enum":["free","starter","pro"]},"owner_user_id":{"type":"string","format":"uuid"},"storage_used_bytes":{"type":"number"},"created_at":{"type":"string","format":"date-time"},"deleted_at":{"type":"string","nullable":true,"format":"date-time"}},"required":["id","slug","plan","owner_user_id","storage_used_bytes","created_at","deleted_at"]},"CreateOrgBody":{"type":"object","properties":{"slug":{"type":"string","minLength":2,"maxLength":63,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},"required":["slug"]},"Site":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"org_id":{"type":"string","format":"uuid"},"github_repo":{"type":"string","nullable":true},"current_deployment_id":{"type":"string","nullable":true,"format":"uuid"},"storage_used_bytes":{"type":"number"},"suspended_at":{"type":"string","nullable":true,"format":"date-time"},"suspension_reason":{"type":"string","nullable":true},"password_protection_scope":{"type":"string","enum":["none","production","preview","all"]},"is_ephemeral":{"type":"number"},"ephemeral_expires_at":{"type":"string","nullable":true,"format":"date-time"},"ephemeral_ip_address":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"deleted_at":{"type":"string","nullable":true,"format":"date-time"}},"required":["id","slug","org_id","github_repo","current_deployment_id","storage_used_bytes","suspended_at","suspension_reason","password_protection_scope","is_ephemeral","ephemeral_expires_at","ephemeral_ip_address","created_at","deleted_at"]},"CreateSiteBody":{"type":"object","properties":{"slug":{"type":"string","minLength":2,"maxLength":63,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"org_slug":{"type":"string","minLength":2,"maxLength":63},"github_repo":{"type":"string","nullable":true,"pattern":"^[a-zA-Z0-9_.-]+\\/[a-zA-Z0-9_.-]+$"}},"required":["slug","org_slug"]},"UpdateSiteBody":{"type":"object","properties":{"github_repo":{"type":"string","nullable":true,"pattern":"^[a-zA-Z0-9_.-]+\\/[a-zA-Z0-9_.-]+$"}}},"UpdatePasswordBody":{"type":"object","properties":{"scope":{"type":"string","enum":["none","production","preview","all"]},"password":{"type":"string","minLength":4,"maxLength":128}},"required":["scope"]},"GitHubConnection":{"type":"object","nullable":true,"properties":{"id":{"type":"string","format":"uuid"},"site_id":{"type":"string","format":"uuid"},"repo_full_name":{"type":"string"},"repo_id":{"type":"number"},"root_directory":{"type":"string","nullable":true},"include_paths":{"type":"array","nullable":true,"items":{"type":"string"}},"production_branch":{"type":"string"},"auto_deploy_enabled":{"type":"boolean"},"preview_deploys_enabled":{"type":"boolean"},"install_command":{"type":"string","nullable":true},"build_command":{"type":"string","nullable":true},"node_version":{"type":"string","nullable":true},"installation":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"account_login":{"type":"string"},"account_type":{"type":"string"},"suspended_at":{"type":"string","nullable":true}},"required":["id","account_login","account_type"]},"created_at":{"type":"string"}},"required":["id","site_id","repo_full_name","repo_id","root_directory","include_paths","production_branch","auto_deploy_enabled","preview_deploys_enabled","install_command","build_command","node_version","installation","created_at"]},"ConnectRepoBody":{"type":"object","properties":{"installation_id":{"type":"string","format":"uuid"},"repo_full_name":{"type":"string","pattern":"^[a-zA-Z0-9_.-]+\\/[a-zA-Z0-9_.-]+$"},"repo_id":{"type":"integer","minimum":0,"exclusiveMinimum":true},"root_directory":{"type":"string","nullable":true},"include_paths":{"type":"array","nullable":true,"items":{"type":"string"}},"production_branch":{"type":"string","default":"main"},"auto_deploy_enabled":{"type":"boolean","default":true},"preview_deploys_enabled":{"type":"boolean","default":true},"install_command":{"type":"string","nullable":true},"build_command":{"type":"string","nullable":true},"node_version":{"type":"string","nullable":true,"default":"20"}},"required":["installation_id","repo_full_name","repo_id"]},"UpdateRepoBody":{"type":"object","properties":{"root_directory":{"type":"string","nullable":true},"include_paths":{"type":"array","nullable":true,"items":{"type":"string"}},"production_branch":{"type":"string"},"auto_deploy_enabled":{"type":"boolean"},"preview_deploys_enabled":{"type":"boolean"},"install_command":{"type":"string","nullable":true},"build_command":{"type":"string","nullable":true},"node_version":{"type":"string","nullable":true}}},"WorkflowResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"filename":{"type":"string"},"content":{"type":"string"},"token_secret_name":{"type":"string"}},"required":["filename","content","token_secret_name"]}},"required":["data"]},"Deployment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"site_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","uploading","processing","ready","failed"]},"url":{"type":"string","format":"uri"},"error_message":{"type":"string","nullable":true},"pr_number":{"type":"number","nullable":true},"pr_title":{"type":"string","nullable":true},"commit_sha":{"type":"string","nullable":true},"commit_message":{"type":"string","nullable":true},"branch":{"type":"string","nullable":true},"file_count":{"type":"number"},"total_size_bytes":{"type":"number"},"created_at":{"type":"string","format":"date-time"},"deleted_at":{"type":"string","nullable":true,"format":"date-time"}},"required":["id","site_id","status","url","error_message","pr_number","pr_title","commit_sha","commit_message","branch","file_count","total_size_bytes","created_at","deleted_at"]},"CreateDeploymentBody":{"type":"object","properties":{"site_slug":{"type":"string","minLength":2,"maxLength":63,"pattern":"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"},"pr_number":{"type":"integer","minimum":0,"exclusiveMinimum":true},"pr_title":{"type":"string","maxLength":500},"commit_sha":{"type":"string","minLength":40,"maxLength":40},"commit_message":{"type":"string","maxLength":500},"branch":{"type":"string","maxLength":255},"mode":{"type":"string","enum":["replace","append"],"default":"replace","description":"Deploy mode: replace (default) replaces all files, append copies existing files first"}},"required":["site_slug"]},"DeploymentWithCurrent":{"allOf":[{"$ref":"#/components/schemas/Deployment"},{"type":"object","properties":{"is_current":{"type":"boolean"},"preview_url":{"type":"string","format":"uri"}},"required":["is_current","preview_url"]}]},"ApiToken":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"token_prefix":{"type":"string"},"user_id":{"type":"string","format":"uuid"},"scope_type":{"type":"string","enum":["user","site","org"]},"site_id":{"type":"string","nullable":true,"format":"uuid"},"site_slug":{"type":"string","nullable":true,"description":"Site slug (included for site-scoped tokens)"},"org_id":{"type":"string","nullable":true,"format":"uuid"},"org_slug":{"type":"string","nullable":true,"description":"Organization slug (included for org-scoped tokens)"},"permissions":{"type":"array","items":{"type":"string"}},"expires_at":{"type":"string","nullable":true,"format":"date-time"},"last_used_at":{"type":"string","nullable":true,"format":"date-time"},"created_at":{"type":"string","format":"date-time"}},"required":["id","name","token_prefix","user_id","scope_type","site_id","site_slug","org_id","org_slug","permissions","expires_at","last_used_at","created_at"]},"ApiTokenWithSecret":{"allOf":[{"$ref":"#/components/schemas/ApiToken"},{"type":"object","properties":{"token":{"type":"string","description":"The raw token value (only shown once on creation)"}},"required":["token"]}]},"CreateTokenBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Token name for identification"},"scope_type":{"type":"string","enum":["user","site","org"],"default":"user","description":"Token scope: \"site\" for site-scoped token, \"org\" for org-scoped token"},"site_slug":{"type":"string","description":"Site slug (required when scope_type is \"site\")"},"org_slug":{"type":"string","description":"Organization slug (required when scope_type is \"org\")"},"permissions":{"type":"array","items":{"type":"string"},"description":"Fine-grained permissions in resource:action format (e.g., \"sites:read\", \"deployments:write\")"},"expires_in_days":{"type":"integer","minimum":1,"maximum":365,"description":"Token expiration in days (optional, null = never expires)"}},"required":["name","permissions"]},"DomainWithVerification":{"allOf":[{"$ref":"#/components/schemas/Domain"},{"type":"object","properties":{"verification":{"type":"object","properties":{"record_type":{"type":"string","enum":["CNAME"]},"record_name":{"type":"string"},"record_value":{"type":"string","enum":["zerodeploy.app"]},"is_apex":{"type":"boolean"},"instructions":{"type":"string"}},"required":["record_type","record_name","record_value","is_apex","instructions"]},"dns_provider":{"$ref":"#/components/schemas/DNSProvider"},"existing_records":{"$ref":"#/components/schemas/ExistingRecords"}},"required":["verification"]}]},"DNSProvider":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"supports_cname_flattening":{"type":"boolean"},"dns_settings_url":{"type":"string","nullable":true}},"required":["id","name","supports_cname_flattening","dns_settings_url"]},"ExistingRecords":{"type":"object","nullable":true,"properties":{"cname_target":{"type":"string","nullable":true},"has_a_record":{"type":"boolean"},"has_aaaa_record":{"type":"boolean"},"warning":{"type":"string","nullable":true}},"required":["cname_target","has_a_record","has_aaaa_record","warning"]},"Domain":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"site_id":{"type":"string","format":"uuid"},"domain":{"type":"string"},"verification_status":{"type":"string","enum":["pending","verified","failed"]},"verification_token":{"type":"string"},"verified_at":{"type":"string","nullable":true,"format":"date-time"},"redirect_mode":{"type":"string","enum":["none","www_to_apex","apex_to_www"]},"created_at":{"type":"string","format":"date-time"},"deleted_at":{"type":"string","nullable":true,"format":"date-time"}},"required":["id","site_id","domain","verification_status","verification_token","verified_at","redirect_mode","created_at","deleted_at"]},"AddDomainBody":{"type":"object","properties":{"domain":{"type":"string","minLength":4,"maxLength":253}},"required":["domain"]},"UpdateDomainRedirectBody":{"type":"object","properties":{"redirect_mode":{"type":"string","enum":["none","www_to_apex","apex_to_www"]}},"required":["redirect_mode"]},"Form":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"submission_count":{"type":"number"},"notification_email":{"type":"string","nullable":true,"format":"email"},"created_at":{"type":"string","format":"date-time"}},"required":["id","name","submission_count","notification_email","created_at"]},"FormSubmission":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"form_id":{"type":"string","format":"uuid"},"data":{"type":"object","additionalProperties":{"nullable":true}},"ip_address":{"type":"string","nullable":true},"user_agent":{"type":"string","nullable":true},"referrer":{"type":"string","nullable":true},"spam_score":{"type":"number"},"spam_signals":{"type":"array","nullable":true,"items":{"type":"string"}},"created_at":{"type":"string","format":"date-time"}},"required":["id","form_id","data","ip_address","user_agent","referrer","spam_score","spam_signals","created_at"]},"UpdateFormBody":{"type":"object","properties":{"notification_email":{"type":"string","nullable":true,"format":"email","description":"Email to receive form submission notifications (null to disable)"}},"required":["notification_email"]},"BulkDeleteSubmissionsBody":{"type":"object","properties":{"submission_ids":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":50,"description":"Array of submission IDs to delete (max 50)"}},"required":["submission_ids"]},"Goal":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string","enum":["pageview","event"]},"match_value":{"type":"string"},"created_at":{"type":"string","format":"date-time"}},"required":["id","name","type","match_value","created_at"]},"CreateGoalBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"type":{"type":"string","enum":["pageview","event"]},"match_value":{"type":"string","minLength":1,"maxLength":500}},"required":["name","type","match_value"]},"GoalConversion":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string","enum":["pageview","event"]},"match_value":{"type":"string"},"completions":{"type":"number"},"unique_completions":{"type":"number"},"total_value":{"type":"number"},"conversion_rate":{"type":"number"}},"required":["id","name","type","match_value","completions","unique_completions","total_value","conversion_rate"]},"Funnel":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"steps":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["pageview","event"]},"match_value":{"type":"string","minLength":1,"maxLength":500}},"required":["type","match_value"]}},"created_at":{"type":"string","format":"date-time"}},"required":["id","name","steps","created_at"]},"CreateFunnelBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"steps":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["pageview","event"]},"match_value":{"type":"string","minLength":1,"maxLength":500}},"required":["type","match_value"]},"minItems":2,"maxItems":8}},"required":["name","steps"]},"FunnelData":{"type":"object","properties":{"period":{"type":"string"},"overall_conversion":{"type":"number"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/FunnelStepData"}}},"required":["period","overall_conversion","steps"]},"FunnelStepData":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["pageview","event"]},"match_value":{"type":"string"},"visitors":{"type":"number"},"conversion_from_previous":{"type":"number","nullable":true},"dropoff_percent":{"type":"number","nullable":true}},"required":["name","type","match_value","visitors","conversion_from_previous","dropoff_percent"]},"AnalyticsResponse":{"type":"object","properties":{"configured":{"type":"boolean"},"message":{"type":"string"},"period":{"type":"string","enum":["24h","7d","30d","90d"]},"active_filters":{"type":"object","additionalProperties":{"type":"string"}},"overview":{"$ref":"#/components/schemas/AnalyticsOverview"},"time_series":{"type":"array","items":{"$ref":"#/components/schemas/TimeSeriesPoint"}},"top_pages":{"type":"array","items":{"$ref":"#/components/schemas/TopPage"}},"entry_pages":{"type":"array","items":{"$ref":"#/components/schemas/EntryPage"}},"exit_pages":{"type":"array","items":{"$ref":"#/components/schemas/ExitPage"}},"countries":{"type":"array","items":{"$ref":"#/components/schemas/CountryStat"}},"status_codes":{"type":"array","items":{"$ref":"#/components/schemas/StatusCodeStat"}},"referrers":{"type":"array","items":{"$ref":"#/components/schemas/ReferrerStat"}},"channels":{"type":"array","items":{"$ref":"#/components/schemas/ChannelStat"}},"cities":{"type":"array","items":{"$ref":"#/components/schemas/CityStat"}},"browsers":{"type":"array","items":{"$ref":"#/components/schemas/BrowserStat"}},"operating_systems":{"type":"array","items":{"$ref":"#/components/schemas/OSStat"}},"device_types":{"type":"array","items":{"$ref":"#/components/schemas/DeviceTypeStat"}},"campaigns":{"type":"array","items":{"$ref":"#/components/schemas/CampaignStat"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/EventStat"}},"not_found_pages":{"type":"array","items":{"$ref":"#/components/schemas/NotFoundPage"}}},"required":["configured","period","overview","time_series","top_pages","entry_pages","exit_pages","countries","status_codes","referrers","channels","cities","browsers","operating_systems","device_types","campaigns","events","not_found_pages"]},"AnalyticsOverview":{"type":"object","properties":{"total_requests":{"type":"number"},"page_views":{"type":"number"},"total_bytes":{"type":"number"},"unique_visitors":{"type":"number"},"total_visits":{"type":"number"},"form_submissions":{"type":"number"},"bounce_rate":{"type":"number"},"avg_session_duration":{"type":"number"},"pages_per_visit":{"type":"number"},"views_per_visit":{"type":"number"}},"required":["total_requests","page_views","total_bytes","unique_visitors","total_visits","form_submissions","bounce_rate","avg_session_duration","pages_per_visit","views_per_visit"]},"TimeSeriesPoint":{"type":"object","properties":{"date":{"type":"string"},"requests":{"type":"number"},"bytes":{"type":"number"}},"required":["date","requests","bytes"]},"TopPage":{"type":"object","properties":{"path":{"type":"string"},"requests":{"type":"number"},"bytes":{"type":"number"}},"required":["path","requests","bytes"]},"EntryPage":{"type":"object","properties":{"path":{"type":"string"},"entries":{"type":"number"}},"required":["path","entries"]},"ExitPage":{"type":"object","properties":{"path":{"type":"string"},"exits":{"type":"number"}},"required":["path","exits"]},"CountryStat":{"type":"object","properties":{"country":{"type":"string"},"requests":{"type":"number"}},"required":["country","requests"]},"StatusCodeStat":{"type":"object","properties":{"status_code":{"type":"number"},"requests":{"type":"number"}},"required":["status_code","requests"]},"ReferrerStat":{"type":"object","properties":{"referrer":{"type":"string"},"requests":{"type":"number"}},"required":["referrer","requests"]},"ChannelStat":{"type":"object","properties":{"channel":{"type":"string"},"requests":{"type":"number"}},"required":["channel","requests"]},"CityStat":{"type":"object","properties":{"city":{"type":"string"},"country":{"type":"string"},"requests":{"type":"number"}},"required":["city","country","requests"]},"BrowserStat":{"type":"object","properties":{"browser":{"type":"string"},"requests":{"type":"number"}},"required":["browser","requests"]},"OSStat":{"type":"object","properties":{"os":{"type":"string"},"requests":{"type":"number"}},"required":["os","requests"]},"DeviceTypeStat":{"type":"object","properties":{"device_type":{"type":"string"},"requests":{"type":"number"}},"required":["device_type","requests"]},"CampaignStat":{"type":"object","properties":{"source":{"type":"string"},"medium":{"type":"string"},"campaign":{"type":"string"},"requests":{"type":"number"}},"required":["source","medium","campaign","requests"]},"EventStat":{"type":"object","properties":{"event_name":{"type":"string"},"completions":{"type":"number"},"unique_completions":{"type":"number"},"total_value":{"type":"number"}},"required":["event_name","completions","unique_completions","total_value"]},"NotFoundPage":{"type":"object","properties":{"path":{"type":"string"},"referrer":{"type":"string"},"hits":{"type":"number"}},"required":["path","referrer","hits"]},"RealtimeResponse":{"type":"object","properties":{"current_visitors":{"type":"number"}},"required":["current_visitors"]}},"parameters":{}},"paths":{"/health":{"get":{"tags":["Health"],"summary":"Shallow health check","description":"Quick health check that confirms the Worker is running. Use for uptime monitors.","security":[],"responses":{"200":{"description":"API is healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/health/ready":{"get":{"tags":["Health"],"summary":"Deep health check","description":"Comprehensive health check that verifies all dependencies (D1, R2, KV) are working. Use for status page.","security":[],"responses":{"200":{"description":"All dependencies healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadyResponse"}}}},"503":{"description":"One or more dependencies unhealthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadyResponse"}}}}}}},"/health/test-error":{"post":{"tags":["Health"],"summary":"Trigger a test error for Argus","description":"Throws an intentional error to test error monitoring. Requires X-Test-Secret header.","security":[],"responses":{"500":{"description":"Intentional test error"}}}},"/slugs/{slug}/check":{"get":{"tags":["Claim"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Slug availability check result","content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"boolean"},"reason":{"type":"string"}},"required":["available"]}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/auth/github":{"get":{"tags":["Auth"],"summary":"Start GitHub OAuth flow","description":"Redirects to GitHub OAuth authorization page. After authentication, redirects back to the specified callback URL with the JWT token.","parameters":[{"schema":{"type":"string","description":"URL to redirect to after authentication"},"required":true,"description":"URL to redirect to after authentication","name":"redirect","in":"query"},{"schema":{"type":"string","description":"Attribution source for new signups (e.g., producthunt, hackernews, twitter)"},"required":false,"description":"Attribution source for new signups (e.g., producthunt, hackernews, twitter)","name":"signup_source","in":"query"}],"responses":{"302":{"description":"Redirect to GitHub OAuth"},"400":{"description":"Missing redirect parameter","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/auth/github/callback":{"get":{"tags":["Auth"],"summary":"GitHub OAuth callback","description":"Handles the OAuth callback from GitHub, creates/updates user, and redirects with JWT token.","parameters":[{"schema":{"type":"string","description":"OAuth authorization code from GitHub"},"required":false,"description":"OAuth authorization code from GitHub","name":"code","in":"query"},{"schema":{"type":"string","description":"Encoded redirect URL"},"required":false,"description":"Encoded redirect URL","name":"state","in":"query"}],"responses":{"302":{"description":"Redirect to CLI/dashboard with token"},"400":{"description":"Missing code or invalid OAuth response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"500":{"description":"GitHub API error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/auth/google":{"get":{"tags":["Auth"],"summary":"Start Google OAuth flow","description":"Redirects to Google OAuth authorization page. After authentication, redirects back to the specified callback URL with the JWT token.","parameters":[{"schema":{"type":"string","description":"URL to redirect to after authentication"},"required":true,"description":"URL to redirect to after authentication","name":"redirect","in":"query"},{"schema":{"type":"string","description":"Attribution source for new signups (e.g., producthunt, hackernews, twitter)"},"required":false,"description":"Attribution source for new signups (e.g., producthunt, hackernews, twitter)","name":"signup_source","in":"query"}],"responses":{"302":{"description":"Redirect to Google OAuth"},"400":{"description":"Missing redirect parameter or Google OAuth not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/auth/google/callback":{"get":{"tags":["Auth"],"summary":"Google OAuth callback","description":"Handles the OAuth callback from Google, creates/updates user, and redirects with JWT token.","parameters":[{"schema":{"type":"string","description":"OAuth authorization code from Google"},"required":false,"description":"OAuth authorization code from Google","name":"code","in":"query"},{"schema":{"type":"string","description":"Encoded redirect URL"},"required":false,"description":"Encoded redirect URL","name":"state","in":"query"}],"responses":{"302":{"description":"Redirect to CLI/dashboard with token"},"400":{"description":"Missing code or invalid OAuth response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/auth/magic-link":{"post":{"tags":["Auth"],"summary":"Request magic link","description":"Sends a magic link to the provided email address for passwordless authentication.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MagicLinkBody"}}}},"responses":{"200":{"description":"Magic link sent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"devToken":{"type":"string","description":"Only returned in development mode"},"devUrl":{"type":"string","description":"Only returned in development mode"}},"required":["success","message"]}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Failed to send email","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/auth/magic-link/verify":{"get":{"tags":["Auth"],"summary":"Verify magic link","description":"Verifies a magic link token and returns a JWT for authentication. Sets an HttpOnly cookie for web clients.","parameters":[{"schema":{"type":"string","minLength":1,"description":"Magic link token from email"},"required":true,"description":"Magic link token from email","name":"token","in":"query"},{"schema":{"type":"string","description":"Attribution source for new signups (e.g., producthunt, hackernews)"},"required":false,"description":"Attribution source for new signups (e.g., producthunt, hackernews)","name":"signup_source","in":"query"}],"responses":{"200":{"description":"Token verified, user authenticated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid, expired, or already used token","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Too many attempts","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/auth/logout":{"post":{"tags":["Auth"],"summary":"Logout","description":"Clears the auth cookie for web clients. CLI clients should delete their stored token.","responses":{"200":{"description":"Logged out","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}}}},"get":{"tags":["Auth"],"summary":"Logout (redirect)","description":"Clears the auth cookie and redirects to the given URL. Use for first-party navigation to avoid cross-origin Set-Cookie issues.","parameters":[{"schema":{"type":"string","format":"uri"},"required":false,"name":"redirect","in":"query"}],"responses":{"302":{"description":"Redirect after logout"}}}},"/auth/me":{"get":{"tags":["Auth"],"summary":"Get current user","description":"Returns the currently authenticated user's profile and admin status.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Current user profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Service temporarily unavailable (database timeout)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"patch":{"tags":["Auth"],"summary":"Update user profile","description":"Updates the authenticated user's email address and/or notification preferences.","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMeBody"}}}},"responses":{"200":{"description":"Profile updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"email":{"type":"string","format":"email"},"notifications":{"type":"object","properties":{"deploy_success":{"type":"boolean"},"deploy_failure":{"type":"boolean"}},"required":["deploy_success","deploy_failure"]}},"required":["success"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Auth"],"summary":"Delete account","description":"Permanently deletes the user account and all associated data (organizations, sites, deployments). This action cannot be undone.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Account deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"deleted":{"type":"object","properties":{"user":{"type":"boolean"},"orgs":{"type":"number"},"sites":{"type":"number"},"deployments":{"type":"number"},"r2_objects":{"type":"number"}},"required":["user","orgs","sites","deployments","r2_objects"]}},"required":["success","message","deleted"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/orgs":{"get":{"tags":["Organizations"],"summary":"List organizations","description":"List all organizations the authenticated user owns or has access to.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"List of organizations","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Org"}}},"required":["data"]}}}},"401":{"description":"Unauthorized - missing or invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Organizations"],"summary":"Create organization","description":"Create a new organization. The authenticated user becomes the owner.","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrgBody"}}}},"responses":{"201":{"description":"Organization created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Org"}},"required":["data"]}}}},"400":{"description":"Validation error or slug already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Organization limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/orgs/{orgSlug}":{"get":{"tags":["Organizations"],"summary":"Get organization","description":"Get a single organization by slug.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-org"},"required":true,"name":"orgSlug","in":"path"}],"responses":{"200":{"description":"Organization details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Org"}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Organizations"],"summary":"Delete organization","description":"Delete an organization. The organization must have no sites.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-org"},"required":true,"name":"orgSlug","in":"path"}],"responses":{"200":{"description":"Organization deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]}}}},"400":{"description":"Organization has sites and cannot be deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites":{"get":{"tags":["Sites"],"summary":"List sites","description":"List all sites across all user organizations.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"List of sites","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Site"}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Sites"],"summary":"Create site","description":"Create a new site in an organization. Org slug is provided in the request body.","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSiteBody"}}}},"responses":{"201":{"description":"Site created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Site"}},"required":["data"]}}}},"400":{"description":"Validation error or slug taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Site limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}":{"get":{"tags":["Sites"],"summary":"Get site","description":"Get a single site by slug.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Site details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Site"}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization or site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Sites"],"summary":"Update site","description":"Update site GitHub repository.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSiteBody"}}}},"responses":{"200":{"description":"Site updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Site"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization or site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Sites"],"summary":"Delete site","description":"Delete a site and all its deployments. This also removes all assets from storage.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Site deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization or site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/password":{"patch":{"tags":["Sites"],"summary":"Update password protection","description":"Enable or disable password protection for a site. Requires Pro plan.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePasswordBody"}}}},"responses":{"200":{"description":"Password protection updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Site"}},"required":["data"]}}}},"400":{"description":"Validation error (password required when enabling)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Plan does not support password protection","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization or site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/github":{"get":{"tags":["GitHub"],"summary":"Get GitHub connection","description":"Get the GitHub connection status for a site","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"GitHub connection (null if not connected)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GitHubConnection"}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["GitHub"],"summary":"Connect to GitHub","description":"Connect a site to a GitHub repository","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectRepoBody"}}}},"responses":{"201":{"description":"Successfully connected","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GitHubConnection"}},"required":["data"]}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["GitHub"],"summary":"Update GitHub config","description":"Update the GitHub connection configuration","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRepoBody"}}}},"responses":{"200":{"description":"Successfully updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GitHubConnection"}},"required":["data"]}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["GitHub"],"summary":"Disconnect from GitHub","description":"Disconnect a site from its GitHub repository","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Successfully disconnected","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"message":{"type":"string"}},"required":["success","message"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/github/workflow":{"post":{"tags":["GitHub"],"summary":"Generate workflow","description":"Generate a GitHub Actions workflow file for deployments","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Generated workflow","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/deployments":{"post":{"tags":["Deployments"],"summary":"Create deployment","description":"Create a new deployment for a site. Returns upload URL for the tar.gz archive.","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDeploymentBody"}}}},"responses":{"201":{"description":"Deployment created with upload URL","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Deployment"},{"type":"object","properties":{"org_slug":{"type":"string"},"site_slug":{"type":"string"},"preview_url":{"type":"string","format":"uri"},"upload_url":{"type":"string","format":"uri"},"upload_and_finalize_url":{"type":"string","format":"uri"},"limits":{"type":"object","properties":{"max_file_size_bytes":{"type":"number"},"max_files_per_deployment":{"type":"number"},"max_deployment_size_bytes":{"type":"number"}},"required":["max_file_size_bytes","max_files_per_deployment","max_deployment_size_bytes"]}},"required":["org_slug","site_slug","preview_url","upload_url","upload_and_finalize_url","limits"]}]}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization or site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Deployment limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/deployments":{"get":{"tags":["Deployments"],"summary":"List deployments","description":"List all deployments for a site, including which one is current.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"List of deployments","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeploymentWithCurrent"}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization or site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/deployments/by-commit":{"get":{"tags":["Deployments"],"summary":"Find deployment by commit SHA(s)","description":"Find a ready deployment for one or more commit SHAs. Used by CLI to check if a preview can be promoted instead of re-deploying. Accepts comma-separated SHAs for merge commit support.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","description":"Comma-separated list of commit SHAs to check"},"required":true,"description":"Comma-separated list of commit SHAs to check","name":"commits","in":"query"}],"responses":{"200":{"description":"Deployment found","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Deployment"},{"nullable":true}]}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Org or site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/deployments/{id}":{"get":{"tags":["Deployments"],"summary":"Get deployment","description":"Get deployment details by ID.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Deployment ID (full UUID or 8-char prefix)","example":"abc12345"},"required":true,"description":"Deployment ID (full UUID or 8-char prefix)","name":"id","in":"path"}],"responses":{"200":{"description":"Deployment details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Deployment"}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Deployment not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/deployments/{id}/files":{"get":{"tags":["Deployments"],"summary":"List deployment files","description":"List all files in a deployment. Paginated with cursor.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Deployment ID (full UUID or 8-char prefix)","example":"abc12345"},"required":true,"description":"Deployment ID (full UUID or 8-char prefix)","name":"id","in":"path"},{"schema":{"type":"string","description":"Pagination cursor from previous response"},"required":false,"description":"Pagination cursor from previous response","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"default":500,"description":"Max files to return (default 500, max 1000)"},"required":false,"description":"Max files to return (default 500, max 1000)","name":"limit","in":"query"}],"responses":{"200":{"description":"File listing","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"size":{"type":"number"}},"required":["key","size"]}},"cursor":{"type":"string","nullable":true}},"required":["data","cursor"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Deployment not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/deployments/{id}/logs":{"get":{"tags":["Deployments"],"summary":"Get deployment logs","description":"Get structured logs from the deployment finalize process.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Deployment ID (full UUID or 8-char prefix)","example":"abc12345"},"required":true,"description":"Deployment ID (full UUID or 8-char prefix)","name":"id","in":"path"}],"responses":{"200":{"description":"Deployment logs","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"timestamp":{"type":"string"},"level":{"type":"string","enum":["info","warn","error"]},"phase":{"type":"string"},"message":{"type":"string"},"metadata":{"type":"object","additionalProperties":{"nullable":true}},"duration_ms":{"type":"number"}},"required":["timestamp","level","phase","message"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Deployment not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/deployments/{id}/upload-and-finalize":{"post":{"tags":["Deployments"],"summary":"Upload and finalize deployment","description":"Upload a tar.gz archive and finalize the deployment in a single request. Uses multipart/form-data with an optional \"archive\" part and a required \"metadata\" part (JSON string with finalize options).","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Deployment ID (full UUID or 8-char prefix)","example":"abc12345"},"required":true,"description":"Deployment ID (full UUID or 8-char prefix)","name":"id","in":"path"}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"archive":{"type":"string","format":"binary"},"metadata":{"type":"string","description":"JSON string with finalize options (preview, mode, incremental, manifest)"}},"required":["metadata"]}}}},"responses":{"200":{"description":"Deployment finalized","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Deployment"},{"type":"object","properties":{"preview":{"type":"boolean"}},"required":["preview"]}]}},"required":["data"]}}}},"400":{"description":"Invalid deployment state or archive","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Deployment not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"File or deployment size exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/deployments/{id}/diff":{"post":{"tags":["Deployments"],"summary":"Diff deployment manifest","description":"Compare a manifest against the current deployment to determine which files need uploading.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Deployment ID (full UUID or 8-char prefix)","example":"abc12345"},"required":true,"description":"Deployment ID (full UUID or 8-char prefix)","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"manifest":{"type":"object","properties":{"version":{"type":"number","enum":[1]},"files":{"type":"object","additionalProperties":{"type":"object","properties":{"hash":{"type":"string"},"size":{"type":"number"}},"required":["hash","size"]}}},"required":["version","files"]}},"required":["manifest"]}}}},"responses":{"200":{"description":"Diff result","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"upload_files":{"type":"array","items":{"type":"string"}},"copy_files":{"type":"array","items":{"type":"string"}},"removed_files":{"type":"array","items":{"type":"string"}},"previous_deployment_id":{"type":"string","nullable":true,"format":"uuid"},"is_incremental":{"type":"boolean"}},"required":["upload_files","copy_files","removed_files","previous_deployment_id","is_incremental"]}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Deployment not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/diff":{"post":{"tags":["Deployments"],"summary":"Diff manifest against current deployment","description":"Compare a manifest against the site's current deployment without creating a new one. Used by CLI to skip deploys when nothing changed.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"manifest":{"type":"object","properties":{"version":{"type":"number","enum":[1]},"files":{"type":"object","additionalProperties":{"type":"object","properties":{"hash":{"type":"string"},"size":{"type":"number"}},"required":["hash","size"]}}},"required":["version","files"]}},"required":["manifest"]}}}},"responses":{"200":{"description":"Diff result","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"upload_files":{"type":"array","items":{"type":"string"}},"copy_files":{"type":"array","items":{"type":"string"}},"removed_files":{"type":"array","items":{"type":"string"}},"previous_deployment_id":{"type":"string","nullable":true,"format":"uuid"},"is_incremental":{"type":"boolean"}},"required":["upload_files","copy_files","removed_files","previous_deployment_id","is_incremental"]}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/deployments/{id}/rollback":{"post":{"tags":["Deployments"],"summary":"Rollback to deployment","description":"Set a previous deployment as the current one. Supports full ID or 8-char prefix.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Deployment ID (full UUID or 8-char prefix)","example":"abc12345"},"required":true,"description":"Deployment ID (full UUID or 8-char prefix)","name":"id","in":"path"}],"responses":{"200":{"description":"Rollback successful","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deployment":{"$ref":"#/components/schemas/Deployment"}},"required":["deployment"]},"message":{"type":"string"}},"required":["data","message"]}}}},"400":{"description":"Deployment not ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Deployment not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/tokens":{"get":{"tags":["Tokens"],"summary":"List API tokens","description":"List all API tokens for the authenticated user. Supports filtering by scope type and site.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["user","site","org"],"description":"Filter by scope type"},"required":false,"description":"Filter by scope type","name":"scope_type","in":"query"},{"schema":{"type":"string","description":"Filter by site slug (only for site-scoped tokens)"},"required":false,"description":"Filter by site slug (only for site-scoped tokens)","name":"site_slug","in":"query"},{"schema":{"type":"string","description":"Filter by org slug (only for org-scoped tokens)"},"required":false,"description":"Filter by org slug (only for org-scoped tokens)","name":"org_slug","in":"query"}],"responses":{"200":{"description":"List of API tokens","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiToken"}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Tokens"],"summary":"Create API token","description":"Create a new API token. Use scope_type \"site\" for site-scoped or \"org\" for org-scoped tokens with fine-grained permissions.","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTokenBody"}}}},"responses":{"201":{"description":"Token created (token value only shown once)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ApiTokenWithSecret"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Token limit reached or site access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/tokens/{tokenId}":{"get":{"tags":["Tokens"],"summary":"Get API token","description":"Get details of a specific API token.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000"},"required":true,"name":"tokenId","in":"path"}],"responses":{"200":{"description":"Token details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ApiToken"}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Token not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Tokens"],"summary":"Delete API token","description":"Delete an API token. Any applications using this token will lose access.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000"},"required":true,"name":"tokenId","in":"path"}],"responses":{"200":{"description":"Token deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Token not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/domains":{"post":{"tags":["Domains"],"summary":"Add custom domain","description":"Add a custom domain to a site. Returns DNS verification instructions.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddDomainBody"}}}},"responses":{"201":{"description":"Domain added with verification instructions","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DomainWithVerification"}},"required":["data"]}}}},"400":{"description":"Validation error or domain already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Domain limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization or site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Domains"],"summary":"List custom domains","description":"List all custom domains for a site.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"List of domains","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Domain"}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization or site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/domains/{domainId}":{"get":{"tags":["Domains"],"summary":"Get domain","description":"Get a single custom domain by ID.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000"},"required":true,"name":"domainId","in":"path"}],"responses":{"200":{"description":"Domain details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Domain"}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization, site, or domain not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Domains"],"summary":"Remove custom domain","description":"Remove a custom domain from a site. Also removes Cloudflare custom hostname.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000"},"required":true,"name":"domainId","in":"path"}],"responses":{"200":{"description":"Domain removed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization, site, or domain not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/domains/{domainId}/verify":{"post":{"tags":["Domains"],"summary":"Verify domain ownership","description":"Check DNS records to verify domain ownership. Creates Cloudflare custom hostname on success.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000"},"required":true,"name":"domainId","in":"path"}],"responses":{"200":{"description":"Domain verified or already verified","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Domain"},{"type":"object","properties":{"site_slug":{"type":"string"},"message":{"type":"string"},"cloudflare":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}},"required":["message"]}]}},"required":["data"]}}}},"400":{"description":"Verification failed - DNS record not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization, site, or domain not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/domains/{domainId}/redirect":{"patch":{"tags":["Domains"],"summary":"Update domain redirect mode","description":"Set www/apex redirect behavior for a domain.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid","example":"123e4567-e89b-12d3-a456-426614174000"},"required":true,"name":"domainId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDomainRedirectBody"}}}},"responses":{"200":{"description":"Redirect mode updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Domain"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization, site, or domain not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/forms":{"get":{"tags":["Forms"],"summary":"List forms","description":"List all forms for a site. Forms are auto-created when submissions are received.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"List of forms","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"submission_count":{"type":"number"},"created_at":{"type":"string","format":"date-time"}},"required":["id","name","submission_count","created_at"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization or site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/forms/{formName}":{"get":{"tags":["Forms"],"summary":"Get form","description":"Get a single form by name.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","example":"contact"},"required":true,"name":"formName","in":"path"}],"responses":{"200":{"description":"Form details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Form"}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization, site, or form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Forms"],"summary":"Update form settings","description":"Update form settings like notification email.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","example":"contact"},"required":true,"name":"formName","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFormBody"}}}},"responses":{"200":{"description":"Form updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"form":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"notification_email":{"type":"string","nullable":true,"format":"email"}},"required":["id","name","notification_email"]}},"required":["success","form"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization, site, or form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Forms"],"summary":"Delete form","description":"Delete a form and all its submissions.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","example":"contact"},"required":true,"name":"formName","in":"path"}],"responses":{"200":{"description":"Form deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization, site, or form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/forms/{formName}/submissions":{"get":{"tags":["Forms"],"summary":"Get form submissions","description":"Get paginated form submissions with parsed data.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","example":"contact"},"required":true,"name":"formName","in":"path"},{"schema":{"type":"string","default":"20","description":"Number of items to return (1-100)"},"required":false,"description":"Number of items to return (1-100)","name":"limit","in":"query"},{"schema":{"type":"string","default":"0","description":"Number of items to skip"},"required":false,"description":"Number of items to skip","name":"offset","in":"query"},{"schema":{"type":"string","enum":["all","inbox","spam","deleted"],"default":"all","description":"Filter submissions: inbox (clean), spam (flagged), deleted (soft-deleted), or all"},"required":false,"description":"Filter submissions: inbox (clean), spam (flagged), deleted (soft-deleted), or all","name":"filter","in":"query"}],"responses":{"200":{"description":"Form submissions","content":{"application/json":{"schema":{"type":"object","properties":{"form":{"$ref":"#/components/schemas/Form"},"submissions":{"type":"array","items":{"$ref":"#/components/schemas/FormSubmission"}},"total":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"}},"required":["form","submissions","total","limit","offset"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization, site, or form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/forms/{formName}/export":{"get":{"tags":["Forms"],"summary":"Export form submissions as CSV","description":"Export all form submissions as a CSV file (up to 10,000 submissions).","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","example":"contact"},"required":true,"name":"formName","in":"path"}],"responses":{"200":{"description":"CSV file download","content":{"text/csv":{"schema":{"type":"string"}}}},"204":{"description":"No submissions to export"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization, site, or form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/forms/{formName}/submissions/bulk-delete":{"post":{"tags":["Forms"],"summary":"Bulk delete submissions","description":"Soft delete multiple form submissions by ID (max 50 at a time).","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","example":"contact"},"required":true,"name":"formName","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDeleteSubmissionsBody"}}}},"responses":{"200":{"description":"Submissions deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"deleted_count":{"type":"number"}},"required":["success","deleted_count"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization, site, or form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/forms/{formName}/submissions/bulk-restore":{"post":{"tags":["Forms"],"summary":"Bulk restore submissions","description":"Restore multiple soft-deleted form submissions by ID (max 50 at a time).","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","example":"contact"},"required":true,"name":"formName","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDeleteSubmissionsBody"}}}},"responses":{"200":{"description":"Submissions restored","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"restored_count":{"type":"number"}},"required":["success","restored_count"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization, site, or form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/forms/{formName}/submissions/bulk-hard-delete":{"post":{"tags":["Forms"],"summary":"Bulk permanently delete submissions","description":"Permanently delete multiple form submissions by ID (max 50 at a time). Use this for submissions already in the deleted tab.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","example":"contact"},"required":true,"name":"formName","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDeleteSubmissionsBody"}}}},"responses":{"200":{"description":"Submissions permanently deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"deleted_count":{"type":"number"}},"required":["success","deleted_count"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization, site, or form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/goals":{"get":{"tags":["Goals"],"summary":"List goals","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"List of goals","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Goal"}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Goals"],"summary":"Create goal","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGoalBody"}}}},"responses":{"201":{"description":"Goal created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Goal"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Goal limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/goals/{goalId}":{"delete":{"tags":["Goals"],"summary":"Delete goal","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"goalId","in":"path"}],"responses":{"200":{"description":"Goal deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Goal not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/goals/conversions":{"get":{"tags":["Goals"],"summary":"Get goal conversions","description":"Fetch conversion data for all goals. Runs one Analytics Engine query per goal.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","enum":["24h","7d","30d","90d"],"default":"7d"},"required":false,"name":"period","in":"query"}],"responses":{"200":{"description":"Goal conversion data","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"period":{"type":"string"},"unique_visitors":{"type":"number"},"goals":{"type":"array","items":{"$ref":"#/components/schemas/GoalConversion"}}},"required":["period","unique_visitors","goals"]}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/funnels":{"get":{"tags":["Funnels"],"summary":"List funnels","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"List of funnels","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Funnel"}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Funnels"],"summary":"Create funnel","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFunnelBody"}}}},"responses":{"201":{"description":"Funnel created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Funnel"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Funnel limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/funnels/{funnelId}":{"delete":{"tags":["Funnels"],"summary":"Delete funnel","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"funnelId","in":"path"}],"responses":{"200":{"description":"Funnel deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Funnel not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/funnels/{funnelId}/data":{"get":{"tags":["Funnels"],"summary":"Get funnel step-through data","description":"Runs one Analytics Engine query per step. Loaded on demand per funnel.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"funnelId","in":"path"},{"schema":{"type":"string","enum":["24h","7d","30d","90d"],"default":"7d"},"required":false,"name":"period","in":"query"}],"responses":{"200":{"description":"Funnel step-through data","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/FunnelData"}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Funnel not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/analytics":{"get":{"tags":["Analytics"],"summary":"Get site analytics","description":"Get traffic analytics for a site including requests, visitors, top pages, countries, and more.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","enum":["24h","7d","30d","90d"],"default":"7d","description":"Time period for analytics data"},"required":false,"description":"Time period for analytics data","name":"period","in":"query"},{"schema":{"type":"string","maxLength":500},"required":false,"name":"page","in":"query"},{"schema":{"type":"string","maxLength":2,"pattern":"^[A-Z]{2}$"},"required":false,"name":"country","in":"query"},{"schema":{"type":"string","maxLength":100},"required":false,"name":"city","in":"query"},{"schema":{"type":"string","maxLength":200},"required":false,"name":"referrer","in":"query"},{"schema":{"type":"string","maxLength":50},"required":false,"name":"browser","in":"query"},{"schema":{"type":"string","maxLength":50},"required":false,"name":"os","in":"query"},{"schema":{"type":"string","enum":["desktop","mobile","tablet"]},"required":false,"name":"device","in":"query"},{"schema":{"type":"string","maxLength":200},"required":false,"name":"utm_source","in":"query"},{"schema":{"type":"string","maxLength":200},"required":false,"name":"utm_medium","in":"query"},{"schema":{"type":"string","maxLength":200},"required":false,"name":"utm_campaign","in":"query"},{"schema":{"type":"string","maxLength":200},"required":false,"name":"event_name","in":"query"},{"schema":{"type":"string","maxLength":50},"required":false,"name":"user_id","in":"query"}],"responses":{"200":{"description":"Analytics data","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AnalyticsResponse"}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden - not organization owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization or site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{slug}/analytics/realtime":{"get":{"tags":["Analytics"],"summary":"Get real-time visitor count","description":"Get the number of distinct visitors on the site in the last 5 minutes.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"my-site"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Real-time visitor count","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RealtimeResponse"}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden - not organization owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Organization or site not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}