{"openapi":"3.1.0","info":{"title":"AIfra preview API","version":"0.5.0-stage5","description":"SaaS for prebuilt static ZIP previews with server-enforced 48-hour expiry. Availability is announced on the platform website. Production uses isolated per-preview origins; the server never builds or executes user code."},"x-legal":{"terms_path":"/legal/terms/","privacy_path":"/legal/privacy/","retention_path":"/legal/retention/","abuse_path":"/abuse/","path_origin":"SITE_ORIGIN (platform website, not API or user-content origin)","terms_version":"2026-09-23-v3","acceptance":"Explicit accept_terms on create/update; not personal-data consent","activation":"TERMS_VERSION must match the reviewed document before public beta"},"x-technical-retention":{"version":"2026-09-23-v3","remoteApprovalSeconds":7200,"remoteReceiptSeconds":3600,"rateLimitHours":25,"lifecycleDays":30,"adminAuditDays":90,"backupMaxHours":48,"supportAfterResolutionDays":30,"abuseAfterResolutionDays":90},"x-agent-workflow":{"documentation_path":"/docs/agent/","path_origin":"SITE_ORIGIN, not the API or preview origin","input":"Prebuilt browser-only static ZIP with index.html at archive root","acceptance":"Obtain explicit terms acceptance before create/update","output":"Return viewer url and expires_at; keep manage_token in private local state","retries":"Do not blindly retry POST after an uncertain network result; creation has no idempotency key","discovery":"Instruction files aid use; no unaided model selection rate is claimed","unavailable":["server-side builds","user backend execution"]},"servers":[{"url":"{apiOrigin}","variables":{"apiOrigin":{"default":"https://api.aifra.ru"}}}],"paths":{"/v1/analytics":{"post":{"summary":"Record an approved, minimized browser event (disabled by default)","description":"First-party Origin only. Exact page/source/event allowlists; no raw URL, referrer, campaign, token or browser identifier. At most 30 attempts/minute/IP; 1024 temporary in-memory IP buckets on the single MVP API process.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["name","page","source","confidence"],"properties":{"name":{"type":"string","enum":["landing_view","docs_view","copy_cli_command","copy_agent_prompt","instruction_click"]},"page":{"type":"string","enum":["/","/docs/","/docs/rest/","/docs/agent/","/docs/cli/","/docs/mcp/","/docs/chat/","/docs/html-presentation/","/docs/static-report/","/docs/russia-preview/","/en/docs/mcp/","/en/docs/publish/","/deploy-from-codex/","/deploy-from-claude-code/","/deploy-from-cursor/","/mcp-deploy/","/guides/deploy-html/","/guides/deploy-vite/","/guides/deploy-react/","/russian-vercel-alternative/","/tuqo-alternative/","/preview-without-registration/","/hosting-for-ai-agents/","/legal/terms/","/legal/privacy/","/legal/retention/","/abuse/","/practice/","/updates/","/practice/ai-site-before-upload/","/practice/client-review-link/","/practice/russia-viewing-check/","/practice/two-page-html-site/","/practice/presentation-without-libraries/","/practice/report-with-local-chart/","/practice/missing-images-after-upload/","/practice/demo-button-instead-of-form/","/practice/cyrillic-html-encoding/","/practice/react-refresh-deep-link/","/practice/noindex-is-not-private/","/practice/javascript-without-api/","/practice/failed-update-keeps-old-version/","/practice/publish-dist-not-repository/","/practice/preview-before-meeting/"]},"source":{"type":"string","enum":["preview","codex","claude-code","cursor","chatgpt","github","claude","perplexity","bing-organic","glama","smithery","pulsemcp","mcpbeat","tracevero","mcp-registry","yandex-organic","google-organic","telegram","direct","unknown"]},"confidence":{"type":"string","enum":["inferred","self_reported","unknown"]}}}}}},"responses":{"204":{"description":"Recorded"},"400":{"description":"Invalid event"},"403":{"description":"Foreign Origin"},"404":{"description":"Collection disabled"},"413":{"description":"Body exceeds 1024 bytes"},"429":{"description":"Event budget exhausted"},"503":{"description":"Event storage unavailable"}}}},"/v1/health":{"get":{"responses":{"200":{"description":"Process is alive"}}}},"/v1/ready":{"get":{"responses":{"200":{"description":"Database and revision storage are ready"},"503":{"description":"A required dependency is unavailable"}}}},"/v1/previews":{"post":{"summary":"Create a 48-hour static preview","parameters":[{"name":"X-AI-Deploy-Accept-Terms","in":"header","required":true,"description":"Must be true. The server records its configured terms version and acceptance time.","schema":{"type":"string","const":"true"}},{"name":"X-AI-Deploy-Anonymous-ID","in":"header","required":true,"schema":{"type":"string","pattern":"^aid_[A-Za-z0-9_-]{20,96}$"}},{"name":"X-AI-Deploy-Source","in":"header","required":true,"description":"Use unknown when attribution is unavailable; never guess.","schema":{"type":"string","enum":["codex","chatgpt","claude-code","cursor","github","mcp-registry","yandex-organic","preview","google-organic","telegram","habr","vc","direct","unknown"]}},{"name":"X-AI-Deploy-Client-Name","in":"header","required":true,"schema":{"type":"string","maxLength":100}},{"name":"X-AI-Deploy-Client-Version","in":"header","required":true,"schema":{"type":"string","maxLength":100}}],"requestBody":{"required":true,"content":{"application/zip":{"schema":{"type":"string","format":"binary"}}}},"responses":{"201":{"description":"Preview created; management token is returned once"},"400":{"description":"Terms not accepted or client metadata invalid"},"413":{"description":"Compressed or extracted limit exceeded"},"422":{"description":"Archive or static-content validation failed"},"429":{"description":"Slot, byte or rate limit reached"},"503":{"description":"Public beta disabled or operational kill switch active"}}}},"/v1/previews/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^pv_[A-Za-z0-9_-]{20,}$"}}],"get":{"summary":"Get preview status","responses":{"200":{"description":"Active preview status"},"404":{"description":"Unknown preview"},"410":{"description":"Expired preview"}}},"put":{"summary":"Replace content without extending the original expiry","parameters":[{"name":"X-AI-Deploy-Accept-Terms","in":"header","required":true,"description":"Must be true. The server records its configured terms version and acceptance time.","schema":{"type":"string","const":"true"}},{"name":"X-AI-Deploy-Anonymous-ID","in":"header","required":true,"schema":{"type":"string","pattern":"^aid_[A-Za-z0-9_-]{20,96}$"}},{"name":"X-AI-Deploy-Source","in":"header","required":true,"description":"Use unknown when attribution is unavailable; never guess.","schema":{"type":"string","enum":["codex","chatgpt","claude-code","cursor","github","mcp-registry","yandex-organic","preview","google-organic","telegram","habr","vc","direct","unknown"]}},{"name":"X-AI-Deploy-Client-Name","in":"header","required":true,"schema":{"type":"string","maxLength":100}},{"name":"X-AI-Deploy-Client-Version","in":"header","required":true,"schema":{"type":"string","maxLength":100}}],"security":[{"managementToken":[]}],"requestBody":{"required":true,"content":{"application/zip":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"Immutable revision switched atomically"},"401":{"description":"Management token invalid"},"410":{"description":"Preview expired"}}},"delete":{"summary":"Delete preview and free its active slot","security":[{"managementToken":[]}],"responses":{"204":{"description":"Preview deleted"},"401":{"description":"Management token invalid"}}}},"/remote/aliases/{alias}":{"get":{"summary":"Check a public name without reserving it","parameters":[{"name":"alias","in":"path","required":true,"schema":{"type":"string","minLength":3,"maxLength":48}}],"responses":{"200":{"description":"Normalized alias, availability and viewer URL. Reservation happens atomically at publication; used names are never reissued."},"400":{"description":"Invalid or reserved name"},"429":{"description":"Rate limit"}}}},"/mcp":{"post":{"summary":"Stateless Streamable HTTP MCP","description":"JSON-RPC via official MCP transport. tools/list is the authoritative input contract. Provider archive references and file parameter metadata are advertised only when provider downloads are configured; otherwise use files or archive_base64. Unknown mutation input fields are rejected. create/update/delete prepare only; the user must open approval_url. No management tokens or model-supplied source IPs. JSON body <=70 MiB; decoded ZIP <=50 MiB; inline static bytes <=1 MiB.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"JSON-RPC response"},"202":{"description":"Notification accepted"},"400":{"description":"Invalid JSON-RPC"},"403":{"description":"Foreign Origin/Host"},"413":{"description":"Body too large"},"429":{"description":"Capacity or rate limit"}}}},"/remote/session":{"get":{"summary":"Browser-only short-lived CSRF session","description":"Same-origin approval UI only. Sets host-only HttpOnly SameSite=Strict cookie (Secure in production). Not an agent tool.","responses":{"200":{"description":"CSRF nonce; no-store"}}}},"/remote/operations/{id}":{"get":{"summary":"Read a remote operation receipt","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^rop_[A-Za-z0-9_-]{43}$"}}],"responses":{"200":{"description":"Pending/uncertain/complete/failed/expired, file metadata and public result. No archive, management key or source IP."},"404":{"description":"Unknown or purged operation"}}}},"/remote/operations/{id}/confirm":{"post":{"summary":"Browser-only explicit approval; never call from an agent","description":"Requires exact Origin, Sec-Fetch-Site same-origin, X-Aifra-Csrf matching the host cookie and a private browser-generated management key in Authorization. Consumes a pending request once. Actual socket/trusted-ingress IP supplies create quota. Terms version must match current. Ambiguous results must not be retried.","security":[{"managementToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^rop_[A-Za-z0-9_-]{43}$"}},{"name":"X-Aifra-Csrf","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["digest","terms_version","accept_terms","anonymous_id"],"properties":{"alias":{"type":"string","minLength":3,"maxLength":48,"description":"Optional on create only. Normalized to lowercase ASCII letters, digits and inner hyphens; system names reserved. Omit for random URL. Not a private link."},"digest":{"type":"string","pattern":"^[a-f0-9]{64}$"},"terms_version":{"type":"string","maxLength":100},"accept_terms":{"type":"boolean","const":true},"anonymous_id":{"type":"string","pattern":"^aid_[A-Za-z0-9_-]{20,96}$"}}}}}},"responses":{"200":{"description":"Public receipt only"},"400":{"description":"Current terms not accepted"},"401":{"description":"Wrong management key"},"403":{"description":"CSRF/Origin failure"},"404":{"description":"Unknown operation"},"409":{"description":"Expired, claimed or wrong digest; preview_alias_unavailable alone allows manual retry with another name without reupload. Never replay uncertain results."},"429":{"description":"Quota or rate limit"}}}}},"components":{"securitySchemes":{"managementToken":{"type":"http","scheme":"bearer","bearerFormat":"pmt_*"}}},"x-preview-constraints":{"ttlHours":48,"activePreviewsPerIp":3,"uploadBytes":52428800,"extractedBytes":52428800,"activeBytesPerIp":157286400,"staticOnly":true,"updateExtendsExpiry":false},"x-remote-mcp":{"endpoint":"https://api.aifra.ru/mcp","publication":"browser_confirmation_required","approval_ttl_seconds":7200,"receipt_retention_seconds":3600,"receipt_retention_starts":"completion_or_approval_expiry","named_links":"Optional browser-selected name on create; never reissued; random isolated asset origin is unchanged","management":"Private browser key; never an MCP parameter or result"}}