Документация / rest
REST API: один ZIP, явные метаданные, серверные ограничения
REST-контракт AIfra: ZIP upload, обязательные headers, status, update без продления TTL и delete.
Статус: Открытая beta. Перед загрузкой прочитайте утверждённые условия и сохраните management token приватно.
01
Endpoints
POST /v1/previews создаёт preview. GET /v1/previews/{id} возвращает публичный status. PUT заменяет revision, DELETE удаляет preview. GET /v1/health и /v1/ready предназначены для platform health checks.
POST и PUT принимают raw application/zip. Максимум проверяется и для upload, и после безопасного извлечения.
POST /v1/previews
GET /v1/previews/{id}
PUT /v1/previews/{id}
DELETE /v1/previews/{id}02
Как выглядит результат
Готовые index.html и style.css были упакованы в ZIP и отправлены опубликованным ниже примером. Viewer открыл сайт со сроком удаления и кнопкой сообщения о нарушении. Пользовательский JavaScript работает в изолированном iframe.
Это скриншот локальной проверки 9 сентября 2026 года. Он показывает фактический результат REST-сценария, но не подтверждает работу публичного HTTPS или доступность из всех сетей.

03
Загрузить подготовленный ZIP
Пример для Bash и curl. В preview.zip должен быть index.html в корне. API_ORIGIN берётся из вашего окружения; до открытия beta используйте только локальный или разрешённый staging API.
INSTALL_ID — ваш случайный локальный installation ID вида aid_<20 или более символов base64url>. Ответ содержит секрет: сохраняйте .ai-deploy приватно и добавьте этот каталог в .gitignore перед запуском.
umask 077
mkdir -p .ai-deploy
curl --fail-with-body --silent --show-error \
"$API_ORIGIN/v1/previews" \
-H "Content-Type: application/zip" \
-H "X-AI-Deploy-Accept-Terms: true" \
-H "X-AI-Deploy-Anonymous-ID: $INSTALL_ID" \
-H "X-AI-Deploy-Source: unknown" \
-H "X-AI-Deploy-Client-Name: curl" \
-H "X-AI-Deploy-Client-Version: 1.0.0" \
--data-binary @preview.zip \
--output .ai-deploy/create-response.json04
Обязательные deployment headers
При POST и PUT нужны accept terms, анонимный installation ID и честная client attribution. Если источник неизвестен, передавайте unknown.
Content-Type: application/zip
X-AI-Deploy-Accept-Terms: true
X-AI-Deploy-Anonymous-ID: aid_<unguessable>
X-AI-Deploy-Source: unknown
X-AI-Deploy-Client-Name: my-client
X-AI-Deploy-Client-Version: 1.0.005
Management token
Create возвращает management token один раз. Update и delete принимают его как Bearer token. Сервис хранит только hash и не включает token в status или логи.
Неверный token не раскрывает наличие preview. Update сохраняет первоначальный expires_at.
Authorization: Bearer pmt_<secret>06
Рабочий пример для Node.js
Этот пример проверяется автоматическим тестом на настоящем HTTP API в локальном окружении. Публичная доступность API проверяется отдельно. Нужен Node.js 24; дополнительные пакеты не требуются. Сохраните код в upload.mjs.
Подготовьте ZIP с index.html в корне. Задайте API_ORIGIN, ZIP_PATH и STATE_FILE — новый файл вне репозитория в личной папке пользователя. После прочтения условий и согласия задайте ACCEPT_TERMS=true. Запустите node upload.mjs. DEPLOY_SOURCE можно задать как codex, claude-code или cursor только если это настоящий источник запроса; иначе оставьте unknown.
В терминале появятся только ID, ссылка и время удаления. Полный ответ с ключом управления будет записан в STATE_FILE. На Windows храните его в личном профиле с ограниченным доступом; режим 0600 сам по себе не настраивает Windows ACL. Не открывайте этот файл в чате агента.
import { open, readFile, stat } from 'node:fs/promises';
import { randomBytes } from 'node:crypto';
if (process.env.ACCEPT_TERMS !== 'true') throw new Error('Read the terms and set ACCEPT_TERMS=true');
const api = new URL(process.env.API_ORIGIN);
if (api.username || api.password || api.pathname !== '/' || api.search || api.hash ||
!(api.protocol === 'https:' || (api.protocol === 'http:' && ['localhost', '127.0.0.1'].includes(api.hostname)))) {
throw new Error('Use HTTPS or a local test API origin');
}
const zipPath = process.env.ZIP_PATH || 'preview.zip';
if ((await stat(zipPath)).size > 50 * 1024 * 1024) throw new Error('ZIP exceeds 50 MiB');
const archive = await readFile(zipPath);
// STATE_FILE must be outside the repository, in your private user directory.
// Exclusive creation prevents overwriting an earlier management token.
const state = await open(process.env.STATE_FILE, 'wx', 0o600);
try {
const response = await fetch(new URL('/v1/previews', api), {
method: 'POST', redirect: 'error', signal: AbortSignal.timeout(120_000),
headers: {
'Content-Type': 'application/zip',
'X-AI-Deploy-Accept-Terms': 'true',
'X-AI-Deploy-Anonymous-ID': 'aid_' + randomBytes(18).toString('base64url'),
'X-AI-Deploy-Source': process.env.DEPLOY_SOURCE || 'unknown',
'X-AI-Deploy-Client-Name': 'rest-example',
'X-AI-Deploy-Client-Version': '1.0.0',
},
body: archive,
});
const result = await response.json();
if (!response.ok) {
const code = String(result.error?.code || 'request_failed');
throw new Error('HTTP ' + response.status + ': ' + (/^[a-z_]+$/.test(code) ? code : 'request_failed'));
}
await state.writeFile(JSON.stringify(result));
await state.sync();
console.log(JSON.stringify({ preview_id: result.preview_id, url: result.url, expires_at: result.expires_at }));
} finally {
await state.close();
}
07
Если запрос не прошёл
terms_not_accepted: явно примите условия перед повтором. archive_path_invalid или archive_link_forbidden: пересоберите ZIP из готовой папки без ссылок и путей за её пределы. phishing_content: удалите запрещённые формы и поля паролей; предупреждение не означает, что сервис вынес юридическое заключение о содержимом.
active_preview_limit: с этого IP уже заняты три места. Удалите свой ненужный preview с его ключом управления или дождитесь окончания срока. У пользователей общего VPN или офиса может быть один внешний IP. Смена IP для обхода лимита не является решением.
upload_too_large или extracted_too_large: уменьшите готовые файлы до 50 MiB. create_rate_limit или update_rate_limit: выдержите указанную сервером паузу, не запускайте повтор в бесконечном цикле. service_unavailable: создание временно закрыто; проверьте объявленный статус сервиса.
Если соединение оборвалось после отправки ZIP, не повторяйте POST автоматически: preview мог уже появиться. Пример оставляет файл состояния и не перезаписывает его при повторном запуске. Если полный ответ не сохранился, ключ нельзя восстановить; обратитесь в поддержку или дождитесь удаления через 48 часов.
08
Деплой через VPN и просмотр без VPN
ZIP отправляет процесс, который запускает HTTP-клиент. Это может быть ваш компьютер, облачное окружение агента или CI. У него должен быть доступ к API_ORIGIN по HTTPS. Доступность модели и доступность API — разные соединения.
Если выбранный VPN не соединяется с API, проверьте маршрут на устройстве, где выполняется загрузка. В собственном VPN-клиенте можно отдельно направить домен API через прямое соединение, оставив соединение с моделью через VPN. Результат зависит от сети; универсальная совместимость пока не подтверждена.
После загрузки другой человек открывает ссылку viewer со своего устройства. Его сеть не участвует в загрузке ZIP. Проверка просмотра в РФ без VPN и проверка загрузки из окружения агента нужны отдельно.
09
Машинная спецификация
Проверяемый OpenAPI 3.1 документ генерируется из того же TypeScript-объекта, который отдаёт API. Статическая копия не поддерживается вручную.
GET /openapi.json