tuqo

Documentation

API and MCP

Anything you can do in the panel, you can do from code: through the REST API at api.tuqo.ru and through the MCP server for AI agents at mcp.tuqo.ru. Any external AI model can create sites, deploy and set up domains, authorized with a single project key.

Quick start

  1. 1

    Create a project in the panel and get a key on the API keys tab. The tqk_… key is shown only once.

  2. 2

    Connect Tuqo to your AI as an MCP connector, or work directly with REST.

  3. 3

    Create a site and upload a source archive: it builds automatically and goes live on <subdomain>.tuqo.ru.

Project API key

Every request is authorized with the header Authorization: Bearer tqk_…. A key belongs to one project, and every operation is scoped to that project. We store only an argon2 hash of the key and a public prefix for display; the key itself cannot be recovered. If you lose it, issue a new one.

Access levels

  • readonly read only: lists and statuses
  • editor create sites, deploy, attach domains, roll back; cannot delete sites or domains
  • full full access, including deleting sites and domains and changing a site's address

MCP for AI agents

Tuqo runs an MCP server (JSON-RPC 2.0) at https://mcp.tuqo.ru/mcp. Add it as a connector in your AI model, and the agent manages deploys from plain-language requests. Over MCP everything is in English: tool descriptions, errors and publish recommendations. Build logs follow the language of the site owner's account.

Connecting

URL and authorization header (for MCP clients with HTTP transport and custom headers):

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

Web chats such as claude.ai and ChatGPT connect over OAuth with just the URL, no key needed. Setup for each client is on the Connect an AI page.

Tools

  • whoami (—)

    project_id, project name and available scopes; the first call of a session

  • list_sites (—)

    the project's sites (each one carries url and form_endpoint)

  • create_site (name, subdomain)

    create a site (subdomain of 9+ characters; 5–8 characters from the Pro plan)

  • get_site (site_id)

    get a site: url, form_endpoint for forms, and recommendations on what to improve after the last deploy, if any

  • update_site (site_id, name, description?)

    rename a site, optionally change its description

  • delete_site (site_id, confirm)

    soft-delete a site (kept in the trash for 24 h)

  • restore_site (site_id)

    bring a site back from the trash

  • list_trashed (—)

    sites in the trash

  • set_site_enabled (site_id, enabled)

    turn serving of a site on or off

  • change_site_subdomain (site_id, subdomain, confirm)

    change the <subdomain>.tuqo.ru address: the new name is reserved at once, the switch happens in 24 h (cancelable until then), the owner gets an email; at most once every 7 days

  • cancel_site_subdomain_change (site_id)

    cancel a scheduled address change before the switch

  • deploy_files (site_id, files)

    publish files as they are (recommended for static sites), live instantly; the response carries recommendations: advice on the files, not errors

  • deploy_site (site_id, source_base64)

    upload a tar.gz/zip source archive (base64) and start a build

  • get_manifest (site_id)

    files of the live deploy as {path, sha256}, for edits without re-uploading media

  • get_deploy_status (deploy_id)

    build status: queued → building → ready → active / failed

  • list_deploys (site_id)

    deploy history of a site

  • get_logs (deploy_id)

    build log

  • activate_deploy (deploy_id)

    make a finished deploy live (rollback to an earlier version works the same way)

  • get_preview_url (deploy_id)

    secret build preview link: opens without a password even on a gated site and can be turned off at any time. Not issued for the live version, whose content is already at the site address (from the Start plan)

  • delete_deploy (deploy_id)

    delete the files of a build that will not go live (frees space and a kept-version slot). The live one cannot be deleted, or the site would go dark

  • set_custom_domain (site_id, domain)

    attach a domain; the response has the _tuqo-verify TXT record

  • verify_domain (domain_id)

    confirm domain ownership after the TXT record is in place, plus A record diagnostics (a_record)

  • list_domains (site_id)

    custom domains of a site

  • delete_domain (domain_id, confirm)

    detach a domain

  • get_forms_overview (—)

    forms summary: submission quota, used, balance, number locked over the limit, sites, counters

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

    form submissions (leads) of the project

  • get_autoreply (site_id)

    a site's auto-reply settings plus counters (emails this month, file downloads)

  • set_autoreply (site_id, enabled, subject?, body?, email_field?)

    auto-reply email to the person who sent the form (from the Start plan)

  • add_autoreply_file (site_id, file_name, content_base64, label?)

    lead magnet file to deliver (up to three; PDF, images, zip, txt, up to 50 MB)

  • remove_autoreply_file (site_id, file_id)

    remove a file from delivery (the id is in get_autoreply)

  • set_autoreply_delivery (site_id, link_ttl_days?, page_title?, upsell_text?, logo_base64?, logo_framed?, logo_rounded?)

    delivery page design and link lifetime; patch semantics, title and description only together

  • get_site_stats (site_id, days?, from?, to?)

    server-side site analytics (days: 1–365 back from today, or a custom from/to period): views and visitors (with a separate “looks human” count), traffic, top pages and referrers, downloads, 404s, devices, submissions per day. No cookies or scripts: the serving layer counts

  • get_site_access (site_id)

    lock state: whether the site is gated, how, expiry, opens limit and counter; for paid access, whether sales are live and what is missing (sales are set up in the panel)

  • set_site_password (site_id, level? | password?)

    gate the site with a password or change it (level: easy|medium|strong)

  • reveal_site_password (site_id)

    show the site's current password to the owner

  • open_site_access (site_id)

    remove the lock so the site is open to everyone again (the password is deleted, expiry and counter reset)

  • set_site_email_access (site_id)

    switch the site to sign-in with an email code (for a list of addresses)

  • set_site_subscribers_access (site_id, channel_ids)

    gate the site for subscribers of the owner's Telegram/MAX channels (1–3 channels)

  • list_messenger_channels (—)

    the project owner's channels for subscribers-only mode (added in the panel profile)

  • list_site_access_emails (site_id)

    the address list: names, devices, who signed in and when, attempts from other addresses

  • add_site_access_email (site_id, email, label?)

    add an address to the access list

  • add_site_access_emails (site_id, emails)

    add addresses in bulk (lines, commas, semicolons)

  • set_site_access_email_label (site_id, email_id, label)

    label an address with a name for the log

  • remove_site_access_email (site_id, email_id)

    remove one address; its access ends immediately

  • remove_site_access_emails (site_id, emails? | all?, confirm)

    remove addresses in bulk or clear the whole list

  • set_site_access_expiry (site_id, days? | until? | clear?)

    access expiry: extend, set a date or remove (not for paid access, where the sales deadline is set in the panel)

  • set_site_access_opens (site_id, max_opens?, reset_counter?)

    limit on the number of opens (devices are counted) and counter reset; member sign-up and paid access use max_seats instead

  • set_site_access_brand (site_id, title?, text?, logo_base64?, logo_framed?, logo_rounded?, favicon_mode?, favicon_base64?)

    login screen: title, description, logo and tab icon (null clears a field; logo_framed adds a backdrop)

  • set_site_members_access (site_id, moderation?, ask_name?, name_label?, consent_mode?, form_style?, max_seats?)

    member sign-up: visitors register themselves with an email code (name and consent come after the code); settings go in the same call, max_seats is the number of seats

  • get_site_members (site_id, q?)

    members: searchable list, counters by status, seats taken, settings and documents; for paid access, when and how much each member paid

  • moderate_site_member (site_id, member_id, action, confirm?)

    approve | reject | block | unblock | delete: sign-up requests, blocks and data removal (delete only with confirm:true; the member is emailed on behalf of the site)

  • set_site_documents (site_id, documents, request_reconsent?)

    owner documents on the login screen (0–3, https, replaces the list); request_reconsent asks members to consent again

A request by hand (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": "Landing page", "subdomain": "myproject" }
    }
  }'

REST API

Base URL: https://api.tuqo.ru. Requests and responses are JSON, except for raw uploads: a deploy archive (binary tar.gz or zip), a blob, a lead magnet file or a logo.

The machine-readable spec is OpenAPI 3.1 (tuqo.dev/openapi.json): use it for client generators, ChatGPT Actions and schema checks. The MCP server card with the full list of tools is at mcp.tuqo.ru/.well-known/mcp.json.

  • GET /api/v1/whoami check the key: project_id, name, scopes readonly
  • GET /api/v1/sites list sites readonly
  • POST /api/v1/sites create a site editor
  • GET /api/v1/sites/{id} get a site readonly
  • PATCH /api/v1/sites/{id} rename a site (and optionally change its description) editor
  • DELETE /api/v1/sites/{id} delete a site (kept in the trash for 24 h) full
  • GET /api/v1/trashed-sites sites in the trash readonly
  • POST /api/v1/sites/{id}/restore restore a site from the trash editor
  • POST /api/v1/sites/{id}/enabled turn serving on or off: {enabled} editor
  • POST /api/v1/sites/{id}/subdomain-change change the address: {subdomain}; the switch happens in 24 h, the owner gets an email full
  • DELETE /api/v1/sites/{id}/subdomain-change cancel a scheduled address change editor
  • GET /api/v1/sites/{id}/deploys deploy history of a site, newest first readonly
  • POST /api/v1/sites/{id}/deploys deploy an archive (tar.gz or zip as the body, up to 50 MB) editor
  • POST /api/v1/sites/{id}/deploy-files deploy a set of files (JSON, body up to 50 MB, up to 2000 files) editor
  • POST /api/v1/sites/{id}/deploys/check manifest → which blobs to upload editor
  • PUT /api/v1/blobs/{sha256} upload one blob (raw bytes) editor
  • POST /api/v1/sites/{id}/deploys/manifest assemble a deploy from blobs (capped by the plan's storage quota) editor
  • GET /api/v1/sites/{id}/manifest files of the live deploy as {path, sha256} readonly
  • GET /api/v1/deploys/{id} deploy status readonly
  • POST /api/v1/deploys/{id}/activate make a deploy live / roll back editor
  • POST /api/v1/deploys/{id}/preview build preview link (idempotent; 409 for the live version) editor
  • DELETE /api/v1/deploys/{id}/preview revoke the preview link editor
  • DELETE /api/v1/deploys/{id} delete a build's files (not the live one: 409) editor
  • GET /api/v1/sites/{id}/domains custom domains of a site readonly
  • POST /api/v1/sites/{id}/domains attach a domain editor
  • POST /api/v1/domains/{id}/verify verify a domain (+ A record diagnostics in a_record) editor
  • DELETE /api/v1/domains/{id} detach a domain full
  • GET /api/v1/forms/overview forms summary for the project readonly
  • GET /api/v1/forms/submissions submissions: ?site=&status=verified|spam|archive&q=&limit=&offset= readonly
  • GET /api/v1/sites/{id}/autoreply a site's auto-reply settings plus counters readonly
  • GET /api/v1/sites/{id}/stats server-side site analytics: daily series plus totals (views, visitors, bytes; human_* after the bot filter), submissions and deploys per day; ?days=1..365 or ?from=&to= (Moscow dates) readonly
  • GET /api/v1/sites/{id}/stats/tops tops for the period: pages, referrers, downloads, 404s, devices, plus bot filter reasons; rows with fewer than 5 hits are hidden readonly
  • PUT /api/v1/sites/{id}/autoreply auto-reply: enabled, subject, body ≤4096 (**bold**, lists, [links], {{field}} placeholders from the submission), email_field; enabling needs the Start plan editor
  • POST /api/v1/sites/{id}/autoreply/files?name=&label= lead magnet file (raw bytes as the body; up to three files, up to 50 MB) editor
  • DELETE /api/v1/sites/{id}/autoreply/files/{fid} remove a file from delivery editor
  • PUT /api/v1/sites/{id}/autoreply/files/{fid}/label file label on the delivery page: {label} editor
  • PUT /api/v1/sites/{id}/autoreply/delivery delivery design: link_ttl_days (1–60), page_title + upsell_text (only together), page_logo_framed/rounded editor
  • PUT /api/v1/sites/{id}/autoreply/page-logo delivery page logo (raw image bytes; needs the design texts filled in) editor
  • DELETE /api/v1/sites/{id}/autoreply/page-logo remove the logo editor
  • GET /api/v1/sites/{id}/access site lock state plus sign-in counters readonly
  • PUT /api/v1/sites/{id}/access gate the site with a password or change the password editor
  • DELETE /api/v1/sites/{id}/access remove the lock: the site is open to everyone again editor
  • GET /api/v1/sites/{id}/access/password show the current password (the response is not cached) editor
  • PATCH /api/v1/sites/{id}/access/expiry access expiry: days | until | clear editor
  • PATCH /api/v1/sites/{id}/access/opens opens limit and counter reset editor
  • PATCH /api/v1/sites/{id}/access/brand login screen: title, description, logo editor
  • POST /api/v1/sites/{id}/access/email-mode sign-in with an email code instead of a password editor
  • POST /api/v1/sites/{id}/access/subscribers-mode access for subscribers of Telegram/MAX channels (channel_ids) editor
  • POST /api/v1/sites/{id}/access/members-mode turn on member sign-up (settings: PATCH members-settings) editor
  • GET /api/v1/sites/{id}/members members: list, search (q), counters, settings, documents readonly
  • POST /api/v1/sites/{id}/members/{member_id}/moderate approve | reject | block | unblock editor
  • PATCH /api/v1/sites/{id}/members-settings mode settings: moderation, name, consents, form layout, seats editor
  • POST /api/v1/sites/{id}/members/reconsent ask members to accept the documents again editor
  • PUT /api/v1/sites/{id}/documents owner documents on the login screen (replaces the list, 0–3) editor
  • GET /api/v1/sites/{id}/access/emails access address list and sign-in log readonly
  • POST /api/v1/sites/{id}/access/emails add an address editor
  • POST /api/v1/sites/{id}/access/emails/bulk add addresses in bulk (raw field) editor
  • PATCH /api/v1/sites/{id}/access/emails/{email_id} label an address with a name editor
  • DELETE /api/v1/sites/{id}/access/emails/{email_id} remove one address editor
  • POST /api/v1/sites/{id}/access/emails/remove remove addresses in bulk or all of them (confirm) editor
  • GET /api/v1/sites/{id}/git the site's repository connection (no secrets) readonly
  • PUT /api/v1/sites/{id}/git connect or update a repository: https URL, branch, optional token for a private repo, build commands editor
  • DELETE /api/v1/sites/{id}/git disconnect the repository editor
  • POST /api/v1/sites/{id}/git/deploy deploy the current head of the branch now editor
  • POST /api/v1/sites/{id}/git/webhook push-to-deploy webhook: URL plus a secret shown once editor

The /git rows connect a repository for auto-deploy on push; providers and webhook setup are on the Git CD page.

Errors

An error comes back with an HTTP status and a JSON body {"message": "…"}. The message text follows the request language: send Accept-Language: en for English, otherwise it is Russian. Branch on the status code, not on the text.

  • 400 invalid data: the body or a parameter failed validation
  • 401 the key is missing, invalid, revoked or expired
  • 402 the feature or limit needs a paid plan or add-on
  • 403 the key lacks the required scope
  • 404 not found, or it belongs to another project (the two look the same)
  • 409 state conflict: subdomain taken, storage quota, a deploy that cannot be activated…
  • 429 rate limit: deploys per day, build minutes, DNS checks

Deploys: formats and statuses

Upload a .tar.gz or .zip archive (up to 50 MB). It can contain:

  • npm project sources: Tuqo builds them on Node 20 with npm ci (or npm install when there is no package-lock.json) and npm run build, then takes the output from dist/, build/ or out/.
  • A ready static site: put the files (with index.html) at the root of the archive. This is how you publish the output of any stack or language built locally.

Supported stacks

Built automatically out of the box (output in dist/build/out):

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

Static output only: SSR and server functions do not run. The output folder is looked up in this order: dist, build, out, .vitepress/dist, .output/public, _site, public, site, storybook-static, so Gatsby and Eleventy build automatically too. For stacks where static output needs a separate command (Nuxt → nuxi generate, Storybook), nested output (Angular) and non-Node tools (Hugo, Jekyll, MkDocs), build locally and upload the ready static files (the “ready static site” option above). That works for any stack.

Deploy over REST (the body is the binary archive):

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

Over MCP, the same archive goes as base64 in the source_base64 argument of deploy_site. When the build succeeds, the deploy goes live automatically.

Deploy statuses

  • queued the deploy is waiting in the build queue
  • building the build is running (npm ci or npm install, then npm run build)
  • ready built, but not live yet
  • active live: served on the site's subdomain
  • failed the build failed (details in get_logs / the error field)
  • superseded replaced by a newer live deploy

Large sites: manifest deploy

A single request is capped by body size (an archive or a file set: up to 50 MB). For heavy sites (lots of photos or video) and efficient repeat deploys, use a manifest deploy: files are uploaded one by one, addressed by content (sha256), with no giant request, and files that match between deploys are not uploaded again (editing text leaves images alone).

Easiest: the CLI

It does every step for you and reads bytes from disk (for agents, no tokens spent on base64). Pass the key in the TUQO_API_KEY environment variable (it stays out of your shell history); --wait waits for the deploy to go live and prints the live URL:

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

Several sites from one repository (locales, brands): a tuqo.json file at the root ({"sites": {"site/com": "<site_id>", "site/ru": "<site_id>"}}) and a single npx @tuqo/cli deploy. Never store the API key in tuqo.json: the CLI refuses to run if it finds one. More on the CLI page.

Or directly: 3 REST steps

  1. Manifest → missing blobs. POST /api/v1/sites/{id}/deploys/check with the body {"files":[{"path":"index.html","sha256":"…"},…]} → {"missing":["sha256",…]}.
  2. Upload the blobs. For each missing one: PUT /api/v1/blobs/{sha256}, the body is the raw file bytes (the sha256 of the body must match the path). Up to 50 MB per file.
  3. Assemble the deploy. POST /api/v1/sites/{id}/deploys/manifest with the same manifest (path+sha256) → the server assembles the site from the blobs and publishes it. Up to 2000 files; the total size is limited only by your plan's storage quota.

index.html must be at the root. Blobs are deduplicated within the project. The /__tuqo/ path is reserved by the platform (login screens, file delivery): files under it are filtered out on deploy, as are service files like .git/.env. An agent in a browser chat (no file system) deploys heavy media sites exactly this way, through the CLI on the user's machine; a plain web chat suits light sites and text edits.

Edit a live site without re-uploading media

To change text without sending the images again (important for a web agent with no files):

  1. GET /api/v1/sites/{id}/manifest (or MCP get_manifest) → an array of {path, sha256} for the current files.
  2. Show first, publish later. Pass "activate": false in the deploy body and the build does not replace the live site; POST /api/v1/deploys/{id}/preview (MCP: get_preview_url) gives the link, and /activate publishes it. Details: build preview.
  3. Deploy with deploy_files//deploy-files: pass unchanged files as a reference {"path":…,"sha256":…} (no content, so the bytes are not sent) and changed files with ordinary content.

A sha256 reference works only for a blob of your own project. deploy_files takes up to 2000 files; inline content counts toward the 50 MB request body, while a file passed as a blob reference can be up to 50 MB on its own.

Serving: routes, 404.html and cache

How the edge resolves a request for /P (a path without an extension) when there is no file P, in order:

  1. Clean URL: /about → serves about.html.
  2. Directory index: if about/index.html exists → 301 to /about/ (so relative links work), then the index is served. A path with a slash (/about/) serves about/index.html right away.
  3. Custom 404: put 404.html at the root of the deploy; on a miss it is served with status 404 (an honest code for SEO).
  4. SPA fallback: with no 404.html, index.html is served with status 200 (client-side routing for React/Vue).

A request for a missing file with an extension (/app.js) returns an honest 404: assets are never replaced with index.html.

Cache headers

  • *.html: no-cache, so updates show up immediately.
  • Files with a content hash in the name (app.4f3a2b1c.js, Vite/Astro output, the _astro/ folder): max-age=31536000, immutable, cached for a year and invalidated by a new file name.
  • Other static files (hero.jpg with a stable name): max-age=300, so an update under the same name arrives within ~5 minutes. Headers cannot be set per file: for immutable caching, put a hash in the name (bundlers do this for you).

Forms (submissions)

Any site in the project can take submissions without a backend of its own. Forms work on every plan, including Free (10 submissions a month); higher plans have a bigger monthly quota, and on any plan you can buy a submission pack. The form posts to a public endpoint; submissions collect in the project panel (the Forms tab), show up in the notification bell and, if you set it up, arrive by email.

Endpoint

POST https://api.tuqo.ru/f/{site_id} accepts application/json, application/x-www-form-urlencoded and multipart/form-data, so a plain HTML form submit and new FormData() work just like JSON. Any other Content-Type → 415 (an explicit error, not a silent 200). No authorization is needed: the endpoint is public and bound to the site's domain.

Getting the endpoint over MCP: the responses of create_site, get_site and list_sites contain a form_endpoint field, a ready URL to embed. It is deterministic: https://api.tuqo.ru/f/<site_id>.

Embedding a form

The panel always has a ready snippet with the anti-spam fields (the Forms tab). The minimal version:

<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('Message sent'); });" data-t="">
  <input name="name" placeholder="Name" required>
  <input name="email" type="email" placeholder="Email" required>
  <textarea name="message" placeholder="Message"></textarea>
  <input name="_hp_xxx" style="display:none" tabindex="-1" autocomplete="off">
  <button type="submit">Send</button>
  <script>document.currentScript.closest('form').dataset.t=Date.now()</script>
</form>

Service fields and anti-spam

  • _submit_time: when the form was shown (ms, Date.now()). Sending faster than ~3 seconds after that counts as spam. There is no upper bound: the tab can stay open as long as you like.
  • _request_id: a request UUID; resending with the same id does not duplicate the submission (idempotency). Optional.
  • honeypot: a hidden field named like _hp_xxx (the exact name is in the panel snippet or in get_forms_overview). You can leave it out: without the field a submission goes through normally; it blocks only when filled in (by a bot). If you add it, keep it empty.
  • Origin: the request must come from the site's domain (its *.tuqo.ru address or a verified custom domain), otherwise 403. So the form works on the site itself, not from an arbitrary origin.
  • Limits: body ≤ 50 KB, up to 30 fields, at most 30 submissions/min per site and 10/min per IP.

Responses

Errors come as {"ok": false, "reason": "…"}. The reason codes are stable English identifiers; the optional hint text is in the site's language.

  • 200 submission accepted (or silently dropped by the honeypot or as a repeat: the response looks the same)
  • 400 the body could not be parsed with a valid Content-Type (reason: invalid_body), or it has more than 30 fields (reason: too_many_fields)
  • 402 intake paused: the limit is used up and the cap on locked submissions is reached (reason: quota_exhausted), or the project is suspended (reason: suspended)
  • 403 the request did not come from the site's domain (origin not allowed; reason: origin_required / origin_not_allowed)
  • 404 no such site, or it is in the trash (reason: site_not_found)
  • 415 unsupported Content-Type: send JSON, urlencoded or multipart (reason: unsupported_content_type)
  • 429 rate limit exceeded (reason: rate_limited)

Submissions above the plan's monthly limit are not lost: the content is saved but locked, and it unlocks when you buy a submission pack or upgrade the plan (a new month does not unlock it). Limits by plan are on the pricing page, an overview is on the Forms page.

Reading submissions (for AI agents and automations)

You can read submissions from code, not just receive them: for reports, follow-ups and dashboards. The scope is the project of the API key; the readonly level is enough.

  • MCP: get_forms_overview (quotas, balance, sites, counters) and get_form_submissions (site_id?, status? = verified|spam|archive, limit?, offset?).
  • REST: GET /api/v1/forms/overview and GET /api/v1/forms/submissions?site=&status=&limit=&offset=.
  • Auto-reply (an email to the sender, from the Start plan): MCP get_autoreply / set_autoreply, REST GET|PUT /api/v1/sites/{id}/autoreply. The text supports markup (**bold**, lists, quotes, links) and {{field_name}} placeholders from the submission. The lead magnet file and the delivery page are set up in the panel. More: Auto-reply and lead magnet.

Gated access

You can close a site to outsiders: with a password for the whole site, or with sign-in by an email code for addresses on your list. The check runs in the serving layer and covers every file, not only HTML. The full walkthrough is on the Gated access page; here is what an agent or a script needs.

This is a paid feature: the password needs the Start plan, the email code needs Pro (subscribers only and member sign-up also need Pro), or the matching per-site add-on. Without it, the enabling calls return 402 with the plan that is needed. Removing the lock is free on any plan: we never gate loosening your own restrictions.

Good to know

  • — The password is returned in the response when you set it; to see it later, call GET /api/v1/sites/{id}/access/password (the response is sent with no-store).
  • — Changing the password or removing the lock signs out everyone who is already in.
  • — The address list holds up to 1000 addresses per site; removing an address ends its access immediately.
  • — Extending access counts from the current end date, not from today.
  • — The login screen logo can be any size up to 1 MB; we shrink it on our side.

The full list of endpoints is in the REST table above (the /access rows); the MCP tools are in the Gated access group on the MCP page.

Custom domains

Attaching a domain takes two steps:

  1. 1

    set_custom_domain (or POST /api/v1/sites/{id}/domains) returns a token. In the domain's DNS, add an A record pointing to the platform IP and a TXT record _tuqo-verify with that token.

  2. 2

    verify_domain (or POST /api/v1/domains/{id}/verify) once DNS has propagated. Ownership is confirmed by the TXT record; the response also carries A record diagnostics (a_record: ok / found / expected / message). If the A record does not point to the platform, the site will not open on the domain. Calling it again on an already verified domain re-checks the A record. The HTTPS certificate is issued automatically.

DNS does not update instantly: it takes from a few minutes to a few hours. The exact A and TXT values are always shown in the panel on the site's Domains tab.