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
Create a project in the panel and get a key on the API keys tab. The
tqk_…key is shown only once. - 2
Connect Tuqo to your AI as an MCP connector, or work directly with REST.
- 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
-
readonlyread only: lists and statuses -
editorcreate sites, deploy, attach domains, roll back; cannot delete sites or domains -
fullfull 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/whoamicheck the key: project_id, name, scopesreadonly - GET
/api/v1/siteslist sitesreadonly - POST
/api/v1/sitescreate a siteeditor - GET
/api/v1/sites/{id}get a sitereadonly - 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-sitessites in the trashreadonly - POST
/api/v1/sites/{id}/restorerestore a site from the trasheditor - POST
/api/v1/sites/{id}/enabledturn serving on or off: {enabled}editor - POST
/api/v1/sites/{id}/subdomain-changechange the address: {subdomain}; the switch happens in 24 h, the owner gets an emailfull - DELETE
/api/v1/sites/{id}/subdomain-changecancel a scheduled address changeeditor - GET
/api/v1/sites/{id}/deploysdeploy history of a site, newest firstreadonly - POST
/api/v1/sites/{id}/deploysdeploy an archive (tar.gz or zip as the body, up to 50 MB)editor - POST
/api/v1/sites/{id}/deploy-filesdeploy a set of files (JSON, body up to 50 MB, up to 2000 files)editor - POST
/api/v1/sites/{id}/deploys/checkmanifest → which blobs to uploadeditor - PUT
/api/v1/blobs/{sha256}upload one blob (raw bytes)editor - POST
/api/v1/sites/{id}/deploys/manifestassemble a deploy from blobs (capped by the plan's storage quota)editor - GET
/api/v1/sites/{id}/manifestfiles of the live deploy as {path, sha256}readonly - GET
/api/v1/deploys/{id}deploy statusreadonly - POST
/api/v1/deploys/{id}/activatemake a deploy live / roll backeditor - POST
/api/v1/deploys/{id}/previewbuild preview link (idempotent; 409 for the live version)editor - DELETE
/api/v1/deploys/{id}/previewrevoke the preview linkeditor - DELETE
/api/v1/deploys/{id}delete a build's files (not the live one: 409)editor - GET
/api/v1/sites/{id}/domainscustom domains of a sitereadonly - POST
/api/v1/sites/{id}/domainsattach a domaineditor - POST
/api/v1/domains/{id}/verifyverify a domain (+ A record diagnostics in a_record)editor - DELETE
/api/v1/domains/{id}detach a domainfull - GET
/api/v1/forms/overviewforms summary for the projectreadonly - GET
/api/v1/forms/submissionssubmissions: ?site=&status=verified|spam|archive&q=&limit=&offset=readonly - GET
/api/v1/sites/{id}/autoreplya site's auto-reply settings plus countersreadonly - GET
/api/v1/sites/{id}/statsserver-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/topstops for the period: pages, referrers, downloads, 404s, devices, plus bot filter reasons; rows with fewer than 5 hits are hiddenreadonly - PUT
/api/v1/sites/{id}/autoreplyauto-reply: enabled, subject, body ≤4096 (**bold**, lists, [links], {{field}} placeholders from the submission), email_field; enabling needs the Start planeditor - 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 deliveryeditor - PUT
/api/v1/sites/{id}/autoreply/files/{fid}/labelfile label on the delivery page: {label}editor - PUT
/api/v1/sites/{id}/autoreply/deliverydelivery design: link_ttl_days (1–60), page_title + upsell_text (only together), page_logo_framed/roundededitor - PUT
/api/v1/sites/{id}/autoreply/page-logodelivery page logo (raw image bytes; needs the design texts filled in)editor - DELETE
/api/v1/sites/{id}/autoreply/page-logoremove the logoeditor - GET
/api/v1/sites/{id}/accesssite lock state plus sign-in countersreadonly - PUT
/api/v1/sites/{id}/accessgate the site with a password or change the passwordeditor - DELETE
/api/v1/sites/{id}/accessremove the lock: the site is open to everyone againeditor - GET
/api/v1/sites/{id}/access/passwordshow the current password (the response is not cached)editor - PATCH
/api/v1/sites/{id}/access/expiryaccess expiry: days | until | cleareditor - PATCH
/api/v1/sites/{id}/access/opensopens limit and counter reseteditor - PATCH
/api/v1/sites/{id}/access/brandlogin screen: title, description, logoeditor - POST
/api/v1/sites/{id}/access/email-modesign-in with an email code instead of a passwordeditor - POST
/api/v1/sites/{id}/access/subscribers-modeaccess for subscribers of Telegram/MAX channels (channel_ids)editor - POST
/api/v1/sites/{id}/access/members-modeturn on member sign-up (settings: PATCH members-settings)editor - GET
/api/v1/sites/{id}/membersmembers: list, search (q), counters, settings, documentsreadonly - POST
/api/v1/sites/{id}/members/{member_id}/moderateapprove | reject | block | unblockeditor - PATCH
/api/v1/sites/{id}/members-settingsmode settings: moderation, name, consents, form layout, seatseditor - POST
/api/v1/sites/{id}/members/reconsentask members to accept the documents againeditor - PUT
/api/v1/sites/{id}/documentsowner documents on the login screen (replaces the list, 0–3)editor - GET
/api/v1/sites/{id}/access/emailsaccess address list and sign-in logreadonly - POST
/api/v1/sites/{id}/access/emailsadd an addresseditor - POST
/api/v1/sites/{id}/access/emails/bulkadd addresses in bulk (raw field)editor - PATCH
/api/v1/sites/{id}/access/emails/{email_id}label an address with a nameeditor - DELETE
/api/v1/sites/{id}/access/emails/{email_id}remove one addresseditor - POST
/api/v1/sites/{id}/access/emails/removeremove addresses in bulk or all of them (confirm)editor - GET
/api/v1/sites/{id}/gitthe site's repository connection (no secrets)readonly - PUT
/api/v1/sites/{id}/gitconnect or update a repository: https URL, branch, optional token for a private repo, build commandseditor - DELETE
/api/v1/sites/{id}/gitdisconnect the repositoryeditor - POST
/api/v1/sites/{id}/git/deploydeploy the current head of the branch noweditor - POST
/api/v1/sites/{id}/git/webhookpush-to-deploy webhook: URL plus a secret shown onceeditor
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.
-
400invalid data: the body or a parameter failed validation -
401the key is missing, invalid, revoked or expired -
402the feature or limit needs a paid plan or add-on -
403the key lacks the required scope -
404not found, or it belongs to another project (the two look the same) -
409state conflict: subdomain taken, storage quota, a deploy that cannot be activated… -
429rate 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(ornpm installwhen there is nopackage-lock.json) andnpm run build, then takes the output fromdist/,build/orout/. - 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):
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
-
queuedthe deploy is waiting in the build queue -
buildingthe build is running (npm ci or npm install, then npm run build) -
readybuilt, but not live yet -
activelive: served on the site's subdomain -
failedthe build failed (details in get_logs / the error field) -
supersededreplaced 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
- Manifest → missing blobs.
POST /api/v1/sites/{id}/deploys/checkwith the body{"files":[{"path":"index.html","sha256":"…"},…]}→{"missing":["sha256",…]}. - 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. - Assemble the deploy.
POST /api/v1/sites/{id}/deploys/manifestwith 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):
GET /api/v1/sites/{id}/manifest(or MCPget_manifest) → an array of{path, sha256}for the current files.- Show first, publish later. Pass
"activate": falsein 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/activatepublishes it. Details: build preview. - Deploy with
deploy_files//deploy-files: pass unchanged files as a reference{"path":…,"sha256":…}(nocontent, so the bytes are not sent) and changed files with ordinarycontent.
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:
- Clean URL:
/about→ servesabout.html. - Directory index: if
about/index.htmlexists → 301 to/about/(so relative links work), then the index is served. A path with a slash (/about/) servesabout/index.htmlright away. - Custom 404: put
404.htmlat the root of the deploy; on a miss it is served with status 404 (an honest code for SEO). - SPA fallback: with no
404.html,index.htmlis 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.jpgwith 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 inget_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.ruaddress or a verified custom domain), otherwise403. 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.
-
200submission accepted (or silently dropped by the honeypot or as a repeat: the response looks the same) -
400the body could not be parsed with a valid Content-Type (reason: invalid_body), or it has more than 30 fields (reason: too_many_fields) -
402intake paused: the limit is used up and the cap on locked submissions is reached (reason: quota_exhausted), or the project is suspended (reason: suspended) -
403the request did not come from the site's domain (origin not allowed; reason: origin_required / origin_not_allowed) -
404no such site, or it is in the trash (reason: site_not_found) -
415unsupported Content-Type: send JSON, urlencoded or multipart (reason: unsupported_content_type) -
429rate 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) andget_form_submissions(site_id?,status?= verified|spam|archive,limit?,offset?). - REST:
GET /api/v1/forms/overviewandGET /api/v1/forms/submissions?site=&status=&limit=&offset=. - Auto-reply (an email to the sender, from the Start plan): MCP
get_autoreply/set_autoreply, RESTGET|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 withno-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
set_custom_domain(orPOST /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-verifywith that token. - 2
verify_domain(orPOST /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.