{
  "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-17-v1",
    "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-17-v1",
    "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": [
      "public CLI package",
      "remote MCP",
      "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"
                    ]
                  },
                  "page": {
                    "type": "string",
                    "enum": [
                      "/",
                      "/docs/rest/",
                      "/docs/agent/",
                      "/docs/cli/",
                      "/docs/mcp/",
                      "/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/"
                    ]
                  },
                  "source": {
                    "type": "string",
                    "enum": [
                      "codex",
                      "claude-code",
                      "cursor",
                      "chatgpt",
                      "github",
                      "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",
                "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",
                "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"
          }
        }
      }
    }
  },
  "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
  }
}
