tuqo

Документация

API и MCP

Всё, что доступно в панели, можно сделать программно: через REST API api.tuqo.ru и через MCP-сервер для нейросетей mcp.tuqo.ru. Любая внешняя ИИ-модель может создавать сайты, деплоить и настраивать домены — авторизация по одному ключу проекта.

Быстрый старт

  1. 1

    Создайте проект в панели и получите ключ на вкладке API-ключи. Ключ формата tqk_… показывается один раз.

  2. 2

    Подключите Tuqo как MCP-коннектор в нейросети — или работайте напрямую через REST.

  3. 3

    Создайте сайт, загрузите архив исходников — он соберётся автоматически и опубликуется на <поддомен>.tuqo.ru.

API-ключ проекта

Все запросы авторизуются заголовком Authorization: Bearer tqk_…. Ключ привязан к конкретному проекту — операции скоупятся только в его пределах. В базе хранится лишь argon2-хеш ключа и публичный префикс для отображения; сам ключ восстановить нельзя — при потере выпустите новый.

Уровни доступа

  • read только чтение: списки и статусы
  • editor создавать сайты, деплоить, привязывать домены, откатывать — без удаления
  • full полный доступ, включая удаление сайтов и доменов

MCP для нейросетей

Tuqo поднимает MCP-сервер (JSON-RPC 2.0) по адресу https://mcp.tuqo.ru/mcp. Подключите его как коннектор в вашей ИИ-модели — и агент управляет деплоем на естественном языке.

Подключение

URL и заголовок авторизации (для MCP-клиентов с HTTP-транспортом и поддержкой заголовков):

{
  "mcpServers": {
    "tuqo": {
      "url": "https://mcp.tuqo.ru/mcp",
      "headers": { "Authorization": "Bearer tqk_xxx_yyy" }
    }
  }
}

Инструменты

  • whoami (—)

    project_id, название проекта и доступные scopes — ориентир в начале сессии

  • list_sites (—)

    список сайтов проекта (каждый сайт содержит url и form_endpoint)

  • create_site (name, subdomain)

    создать сайт (поддомен ≥9 символов на бесплатном тарифе)

  • get_site (site_id)

    получить сайт (в ответе url и form_endpoint для форм)

  • update_site (site_id, name)

    переименовать сайт

  • delete_site (site_id, confirm)

    мягко удалить сайт (корзина 24 ч)

  • restore_site (site_id)

    вернуть сайт из корзины

  • list_trashed (—)

    сайты в корзине

  • set_site_enabled (site_id, enabled)

    включить/выключить раздачу сайта

  • deploy_files (site_id, files)

    выложить файлы как есть (рекомендуется для статики) — публикуется мгновенно

  • deploy_site (site_id, source_base64)

    загрузить tar.gz исходников (base64) и запустить сборку

  • get_manifest (site_id)

    файлы активного деплоя {path, sha256} — для правки без перезаливки медиа

  • get_deploy_status (deploy_id)

    статус сборки: queued → building → ready → active / failed

  • list_deploys (site_id)

    история деплоев сайта

  • get_logs (deploy_id)

    логи сборки

  • activate_deploy (deploy_id)

    сделать готовый деплой боевым (так же откат на прошлую версию)

  • set_custom_domain (site_id, domain)

    привязать домен — в ответе TXT-запись _tuqo-verify

  • verify_domain (domain_id)

    подтвердить владение доменом после TXT + диагностика A-записи (a_record)

  • list_domains (site_id)

    кастомные домены сайта

  • delete_domain (domain_id, confirm)

    отвязать домен

  • get_forms_overview (—)

    сводка по формам: квота заявок, использовано, баланс, сайты, счётчики

  • get_form_submissions (site_id?, status?, limit?, offset?)

    заявки (лиды) с форм проекта

Пример запроса вручную (JSON-RPC tools/call):

curl -X POST https://mcp.tuqo.ru/mcp \
  -H "Authorization: Bearer tqk_xxx_yyy" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0", "id": 1, "method": "tools/call",
    "params": {
      "name": "create_site",
      "arguments": { "name": "Лендинг", "subdomain": "myproject" }
    }
  }'

REST API

Базовый адрес — https://api.tuqo.ru. Тело и ответы — JSON (кроме загрузки деплоя: там тело — бинарный tar.gz).

  • GET /api/v1/whoami/ проверить ключ: project_id, имя, scopes read
  • GET /api/v1/sites/ список сайтов read
  • POST /api/v1/sites/ создать сайт editor
  • GET /api/v1/sites/{id} получить сайт read
  • PATCH /api/v1/sites/{id} переименовать сайт editor
  • DELETE /api/v1/sites/{id} удалить сайт full
  • POST /api/v1/sites/{id}/deploys деплой архивом (tar.gz в теле, до 50 МБ) editor
  • POST /api/v1/sites/{id}/deploy-files деплой набором файлов (JSON, до 20 МБ) editor
  • POST /api/v1/sites/{id}/deploys/check манифест → какие блобы догрузить editor
  • PUT /api/v1/blobs/{sha256} загрузить один блоб (сырые байты) editor
  • POST /api/v1/sites/{id}/deploys/manifest собрать деплой из блобов (до 500 МБ) editor
  • GET /api/v1/deploys/{id} статус деплоя read
  • POST /api/v1/deploys/{id}/activate сделать деплой боевым / откатиться editor
  • POST /api/v1/sites/{id}/domains привязать домен editor
  • POST /api/v1/domains/{id}/verify подтвердить домен (+ диагностика A-записи a_record) editor
  • DELETE /api/v1/domains/{id} отвязать домен full
  • GET /api/v1/forms/overview/ сводка по формам проекта read
  • GET /api/v1/forms/submissions/ заявки: ?site=&status=verified|spam|archive&limit=&offset= read

Деплой: форматы и статусы

Загрузите архив .tar.gz (до 50 МБ). Варианты содержимого:

  • Исходники npm-проекта — Tuqo соберёт на Node 20: npm ci && npm run build, артефакт берётся из dist/, build/ или out/ (нужен package-lock.json).
  • Готовая статика — положите файлы (с index.html) в корень архива. Так публикуется вывод любого стека/языка, собранного локально.

Поддерживаемые стеки

Авто-сборка из коробки (вывод в dist/build/out):

Vite (React/Vue/Svelte/Solid/Preact)Astro (SSG)Vue CLI → distCreate React App → buildSvelteKit (static) → buildNext.js static export → out

Только статика — SSR и серверные функции не выполняются. Стеки с другим каталогом вывода (Gatsby → public/, Nuxt 3, Angular, Eleventy) и не-Node инструменты (Hugo, Jekyll) — соберите локально и залейте готовую статику (вариант «Готовая статика» выше). Так работает любой стек.

Деплой через REST (тело — бинарный архив):

curl -X POST https://api.tuqo.ru/api/v1/sites/$SITE/deploys \
  -H "Authorization: Bearer tqk_xxx_yyy" \
  -H "Content-Type: application/gzip" \
  --data-binary @dist.tar.gz

В MCP тот же архив передаётся как base64 в аргументе source_base64 инструмента deploy_site. При успешной сборке деплой активируется автоматически.

Статусы деплоя

  • queued деплой в очереди на сборку
  • building идёт сборка (npm ci && npm run build)
  • ready собран, но ещё не активирован
  • active боевой — отдаётся на поддомене сайта
  • failed ошибка сборки (текст — в get_logs / поле error)
  • superseded заменён более новым активным деплоем

Большие сайты: манифест-деплой

Один запрос ограничен размером тела (архив — до 50 МБ, набор файлов — до 20 МБ). Для тяжёлых сайтов (много фото/видео) и эффективных повторных деплоев — манифест-деплой: файлы грузятся поштучно по содержимому (sha256), без гигантского запроса, а совпадающие между деплоями файлы не перезаливаются (правка текста не трогает картинки).

Проще всего — CLI

Делает все шаги сам; байты читаются с диска (для агентов — без расхода токенов на base64). Ключ — через env TUQO_API_KEY (не светится в истории шелла); --wait дождётся публикации и напечатает боевой URL:

TUQO_API_KEY=tqk_xxx_yyy npx @tuqo/cli deploy ./dist --site $SITE --wait

Несколько сайтов из одного репозитория (локали, бренды) — файл tuqo.json в корне ({"sites": {"site/com": "<site_id>", "site/ru": "<site_id>"}}) и одна команда npx @tuqo/cli deploy. Ключ API в tuqo.json хранить нельзя — CLI откажется работать.

Или напрямую — 3 шага REST

  1. Манифест → недостающие блобы. POST /api/v1/sites/{id}/deploys/check с телом {"files":[{"path":"index.html","sha256":"…"},…]}{"missing":["sha256",…]}.
  2. Загрузка блобов. Для каждого недостающего — PUT /api/v1/blobs/{sha256}, тело — сырые байты файла (sha256 тела должен совпадать с путём). До 50 МБ на файл.
  3. Сборка деплоя. POST /api/v1/sites/{id}/deploys/manifest с тем же манифестом (path+sha256) → сервер соберёт сайт из блобов и опубликует. До 2000 файлов; суммарный объём ограничен только квотой хранилища вашего тарифа.

Нужен index.html в корне. Дедуп блобов — в пределах проекта. Для агента в браузерном чате (без файловой системы) тяжёлые медиа-сайты собираются именно так через CLI на машине пользователя; чистый веб-чат подходит для лёгких сайтов и правок текста.

Правка существующего сайта без перезаливки медиа

Чтобы изменить текст, не пересылая картинки заново (важно для веб-агента без файлов):

  1. GET /api/v1/sites/{id}/manifest (или MCP get_manifest) → массив {path, sha256} текущих файлов.
  2. Деплой через deploy_files//deploy-files: неизменные файлы передайте ссылкой {"path":…,"sha256":…} (без content — байты не пересылаются), изменённые — обычным content.

Сослаться по sha256 можно только на блоб своего проекта. Лимиты deploy_files: до 500 файлов и 20 МБ суммарно инлайн-контентом; файл, переданный ссылкой на блоб, — до 50 МБ.

Раздача: маршруты, 404.html и кэш

Как edge резолвит запрос /P (путь без расширения), если файла P нет — по порядку:

  1. Clean URL: /about → отдаётся about.html.
  2. Индекс каталога: есть about/index.html → 301 на /about/ (чтобы работали относительные ссылки), затем отдаётся индекс. Путь со слэшем (/about/) сразу отдаёт about/index.html.
  3. Кастомный 404: положите 404.html в корень деплоя — при промахе он отдаётся со статусом 404 (честный код для SEO).
  4. SPA-fallback: если 404.html нет — отдаётся index.html со статусом 200 (клиентский роутинг React/Vue).

Запрос файла с расширением, которого нет (/app.js), возвращает честный 404 — ассеты не подменяются на index.html.

Кэш-заголовки

  • *.htmlno-cache: обновления видны сразу.
  • Файлы с контент-хэшем в имени (app.4f3a2b1c.js, вывод Vite/Astro, каталог _astro/) — max-age=31536000, immutable: год в кэше, инвалидация — новым именем файла.
  • Остальная статика (hero.jpg со стабильным именем) — max-age=300: обновление файла с тем же именем доедет за ~5 минут. Отдельно управлять заголовками нельзя — хотите immutable, используйте хэш в имени (сборщики делают это сами).

Формы (приём заявок)

Любой сайт проекта может принимать заявки без своего бэкенда — фича доступна с тарифа Профи (или при докупленных заявках на любом тарифе). Форма отправляет данные на публичный эндпоинт, заявки копятся в панели проекта (вкладка «Формы»), приходят в колокольчик и, по настройке, на email.

Эндпоинт

POST https://api.tuqo.ru/f/{site_id} — принимает application/json, application/x-www-form-urlencoded и multipart/form-data — то есть обычный submit HTML-формы и new FormData() работают наравне с JSON. Другой Content-Type415 (явная ошибка, а не молчаливый 200). Авторизация не нужна — эндпоинт публичный, привязан к домену сайта.

Где взять эндпоинт через MCP: ответы create_site, get_site и list_sites содержат поле form_endpoint — готовый URL для встраивания. Он детерминирован: https://api.tuqo.ru/f/<site_id>.

Встраивание формы

Готовый сниппет с анти-спам-полями всегда есть в панели (вкладка «Формы»). Минимальный вид:

<form onsubmit="event.preventDefault();
  const f=event.target, d=Object.fromEntries(new FormData(f));
  d._submit_time=f.dataset.t; d._request_id=crypto.randomUUID();
  fetch('https://api.tuqo.ru/f/SITE_ID', {
    method:'POST', headers:{'Content-Type':'application/json'},
    body: JSON.stringify(d)
  }).then(()=>{ f.reset(); alert('Заявка отправлена'); });" data-t="">
  <input name="name" placeholder="Имя" required>
  <input name="email" type="email" placeholder="Email" required>
  <textarea name="message" placeholder="Сообщение"></textarea>
  <input name="_hp_xxx" style="display:none" tabindex="-1" autocomplete="off">
  <button type="submit">Отправить</button>
  <script>document.currentScript.closest('form').dataset.t=Date.now()</script>
</form>

Служебные поля и анти-спам

  • _submit_time — момент показа формы (мс, Date.now()). Отправка быстрее ~3 секунд после показа считается спамом. Верхней границы нет — вкладку можно держать открытой сколько угодно.
  • _request_id — UUID запроса: повторная отправка с тем же id не задваивает заявку (идемпотентность). Необязательно.
  • honeypot — скрытое поле с именем вида _hp_xxx (точное имя — в сниппете панели или в get_forms_overview). Можно не добавлять — без поля заявка проходит нормально; оно режет только если заполнено (бот). Если добавили — оставляйте пустым.
  • Origin — запрос должен идти с домена сайта (его *.tuqo.ru или подтверждённого кастомного домена), иначе 403. Поэтому форма работает на самом сайте, а не с произвольного origin.
  • Лимиты: тело ≤ 50 КБ, до 30 полей, не более 30 заявок/мин на сайт и 10/мин на IP.

Ответы

  • 200 заявка принята (или молча отклонена honeypot/повтором — наружу одинаковый ответ)
  • 400 тело не распарсилось при верном Content-Type (reason: invalid_body)
  • 402 формы недоступны: нет тарифа со Старта и нет докупленных заявок, либо проект приостановлен
  • 403 запрос не с домена сайта (origin не в списке разрешённых; reason: origin_required / origin_not_allowed)
  • 415 неподдерживаемый Content-Type — шлите JSON, urlencoded или multipart (reason: unsupported_content_type)
  • 429 превышен лимит частоты

Сверх месячного лимита тарифа заявки не теряются: содержимое сохраняется скрытым и открывается при сбросе лимита, докупке пакета заявок или повышении тарифа. Лимиты по тарифам — на странице тарифов, обзор — на странице Формы.

Чтение заявок (для ИИ-агентов и автоматизаций)

Заявки можно не только принимать, но и читать программно — для отчётов, авто-ответов и дашбордов. Скоуп — проект из API-ключа, нужен уровень read.

  • MCP: get_forms_overview (квоты, баланс, сайты, счётчики) и get_form_submissions (site_id?, status? = verified|spam|archive, limit?, offset?).
  • REST: GET /api/v1/forms/overview и GET /api/v1/forms/submissions?site=&status=&limit=&offset=.

Кастомные домены

Привязка домена — в два шага:

  1. 1

    set_custom_domain (или POST /api/v1/sites/{id}/domains) — в ответе придёт токен. Добавьте в DNS домена A-запись на IP платформы и TXT-запись _tuqo-verify с этим токеном.

  2. 2

    verify_domain (или POST /api/v1/domains/{id}/verify) — после распространения DNS. Владение подтверждается по TXT; в ответе также диагностика A-записи (a_record: ok / found / expected / message) — если A ведёт не на платформу, сайт по домену не откроется. Повторный вызов на уже подтверждённом домене перепроверяет A-запись. HTTPS-сертификат выпускается автоматически.

DNS обновляется не мгновенно — от нескольких минут до нескольких часов. Точные значения A- и TXT-записей всегда показываются в панели на вкладке «Домены» сайта.