Sites
List, inspect and manage websites.
GET /v1/export
No API key: the signed token is the whole authorization, for one site, for 10 minutes. Streams the archive described under export.tar.gz. An expired, altered or unknown token, or one for a site since deleted or moved to another account, answers 404 with code export_link_invalid.
No authentication required.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| token | query | string | yes |
Responses:
| Status | Description |
|---|---|
| 200 | A gzipped tar archive |
| 404 | The link is not valid or has expired |
| 429 | Rate limited (code rate_limited). Saved-data reads allow 30 a second per address with a burst of 60 (SAVED_DATA_READ_PER_SEC, SAVED_DATA_READ_BURST). |
GET /v1/idle/keep
Opened from the idle-site email; no sign-in, the token is the authorization. Shows a page with a “Keep it online” button and changes nothing (mail scanners follow links). 404 page when the link was used, replaced or has expired; 403 page while the site is taken down. Emailed links carry the token in the fragment (#t=), so it never reaches a server log: without t the GET returns a small page whose script posts the fragment’s token back with peek=1, which shows this confirmation. Links sent before v0.7.1 carry ?t= and keep working until they expire.
No authentication required.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| t | query | string | The token from an older email |
Responses:
| Status | Description |
|---|---|
| 200 | HTML confirmation page with the button |
| 403 | HTML page; the site is taken down |
| 404 | HTML page; the link was used or has expired |
| 429 | Rate limited (code rate_limited). Saved-data reads allow 30 a second per address with a burst of 60 (SAVED_DATA_READ_PER_SEC, SAVED_DATA_READ_BURST). |
POST /v1/idle/keep
The confirmation page’s button (with peek=1, only the confirmation page). The site stays up and its idle clock restarts; the link is then spent. HTML page.
No authentication required.
Request body: application/x-www-form-urlencoded
Responses:
| Status | Description |
|---|---|
| 200 | HTML page confirming the site stays up |
| 403 | HTML page; the site is taken down |
| 404 | HTML page; the link was used or has expired |
| 429 | Rate limited (code rate_limited). Saved-data reads allow 30 a second per address with a burst of 60 (SAVED_DATA_READ_PER_SEC, SAVED_DATA_READ_BURST). |
GET /v1/idle/restore
Shows a page with a “Restore it” button for a site the idle-site cleanup moved to Recently deleted, and changes nothing. No sign-in. Emailed links carry the token in the fragment (#t=), so it never reaches a server log: without t the GET returns a small page whose script posts the fragment’s token back with peek=1, which shows this confirmation. Links sent before v0.7.1 carry ?t= and keep working until they expire.
No authentication required.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| t | query | string |
Responses:
| Status | Description |
|---|---|
| 200 | HTML confirmation page with the button |
| 403 | HTML page; the site is taken down |
| 404 | HTML page; the link was used or has expired |
| 429 | Rate limited (code rate_limited). Saved-data reads allow 30 a second per address with a burst of 60 (SAVED_DATA_READ_PER_SEC, SAVED_DATA_READ_BURST). |
POST /v1/idle/restore
Brings the site back exactly as it was (versions, saved data, names), with a fresh idle clock, while it is still in Recently deleted; the link is then spent (with peek=1, only the confirmation page). HTML page.
No authentication required.
Request body: application/x-www-form-urlencoded
Responses:
| Status | Description |
|---|---|
| 200 | HTML page confirming the site is back |
| 403 | HTML page; the site is taken down |
| 404 | HTML page; the link was used or has expired |
| 429 | Rate limited (code rate_limited). Saved-data reads allow 30 a second per address with a burst of 60 (SAVED_DATA_READ_PER_SEC, SAVED_DATA_READ_BURST). |
GET /v1/sites
Returns the authenticated account’s own sites, newest edited first (updated_at desc, then created_at desc). all=1 widens an admin’s list to every account’s sites; refused (403) for a non-admin.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| query | query | string | Every word must match the name, domain or summary (case-insensitive substrings). | |
| created_after | query | string | Created on or after RFC3339 or YYYY-MM-DD (UTC midnight). | |
| created_before | query | string | Created on or before RFC3339 or YYYY-MM-DD (UTC whole day). | |
| updated_after | query | string | Updated on or after RFC3339 or YYYY-MM-DD (UTC midnight). | |
| updated_before | query | string | Updated on or before RFC3339 or YYYY-MM-DD (UTC whole day). | |
| order | query | string, one of updated, created, name |
Sort key; default updated (last edited first). | |
| limit | query | integer | Maximum results, clamped to 500; default 200 when any filter or paging parameter is present. | |
| offset | query | integer | Skip this many matching sites. | |
| all | query | string, one of 1 |
Admin only: every account’s sites instead of just this one. 403 for a non-admin. |
Responses:
| Status | Description |
|---|---|
| 200 | List of sites |
| 400 | Invalid date (bad_date), order (bad_order), or non-negative integer required for limit/offset (bad_pagination). |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 403 | all=1 from a non-admin. |
PATCH /v1/sites/{sitename}
One change per request. With {“offline”: true} the site is taken offline: every address answers a plain “This site is offline” page (503), and visitor saves (state and collection writes from a page, private lists included), visitors’ reads of its state and lists, and visitor sign-in are refused with 403 site_offline; files, versions and saved data are kept, and the owner’s key or connector still deploys, reads and writes. {“offline”: false} puts it back online. An operator take-down wins over it, and a taken-down site cannot be switched (403 site_suspended). The answer is the site object with offline and a note. With {“name”: “new-name”} it renames the database row and on-disk site directory. The new name uses the same validation and per-owner uniqueness rule as creation. A bound custom domain remains attached. Links to the old name keep working: its site host, person-path and sites.* addresses 302 to the new address (path and query kept), through any chain of renames, until a site with the old name is created again (the new site wins); the response says so with old_url_status “redirects”. Deleting the site stops the redirects (restoring brings them back); purging it removes them.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Request body:
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | The new name (renames the site). | |
| offline | boolean | true takes the site offline; false puts it back online. |
Responses:
| Status | Description |
|---|---|
| 200 | Renamed (old_name, name, old_url, site_url, old_url_status) or switched (the site object with offline and note) |
| 400 | Invalid request. A saved-data write whose JSON holds a NUL character (\u0000), half of a surrogate pair or bytes that are not UTF-8 is code invalid_json. |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 403 | The operator has taken the site down (code site_suspended) |
| 404 | Not found |
| 409 | Conflict: the code says which (site_exists, domain_taken, append_only, custom_domain_required, not_an_object) |
DELETE /v1/sites/{sitename}
Takes the site offline at once, with every version, its saved state and collections. It stays in Recently deleted (GET /v1/me/deleted-sites) for 7 days with its name held, and POST /v1/sites/{sitename}/restore brings it back exactly as it was; after that it is removed for good. Creating a site with a held name answers 409 with recently_deleted true.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Responses:
| Status | Description |
|---|---|
| 204 | Site deleted (restorable for 7 days) |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 404 | Not found |
GET /v1/sites/{sitename}/access
Owner only (the owner’s key or connector token; a deploy-only key is refused). access is anyone (anyone with the address, or with the passcode when the site has one) or specific (only the owner and the named viewers, each signed in with visitor sign-in on the site’s own address). viewers lists the named viewers by the email they sign in with. Never cached (Cache-Control: no-store). Hosted Simple Host and small boxes only.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Responses:
| Status | Description |
|---|---|
| 200 | Who can open the site |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 403 | A deploy-only key (deploy_only_key) |
| 404 | Not found |
PUT /v1/sites/{sitename}/access
Owner only. {"access": "specific"}: every address of the site (its own address, a custom domain, a free name, an address family, the person address when it is the home page) then shows a sign-in page (401, noindex, no-store, nothing of the site in it) to anyone not signed in on that address, and a plain “This site is private” page (403) with Switch account and Sign out to someone signed in who is not the owner or a named viewer. The site’s saved data (state, collections, storage resources, whatever their site_passcode says) answers 403 site_private to them too; the owner’s key and connector keep working, and previews still open. With no viewers named only the owner can open it. {"access": "anyone"} opens it again and keeps the list. The site leaves the person page while it is specific. A site has named viewers or a passcode, never both (409 passcode_set). Every request is checked against the list as it is then: a removed person is refused on their next request.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Request body:
| Name | Type | Required | Description |
|---|---|---|---|
| access | string, one of anyone, specific |
yes |
Responses:
| Status | Description |
|---|---|
| 200 | Who can open the site now, with a plain note |
| 400 | Body is not {“access”: “anyone”} or {“access”: “specific”} (invalid_request) |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 403 | The operator has taken the site down (site_suspended), or a deploy-only key (deploy_only_key) |
| 404 | Not found |
| 409 | passcode_set (remove the passcode first), named_viewers_need_own_address (every site on this server shares one address), sign_in_unavailable (this server has no email codes or Google), or named_viewers_unavailable |
| 429 | Rate limited (code rate_limited). Saved-data reads allow 30 a second per address with a burst of 60 (SAVED_DATA_READ_PER_SEC, SAVED_DATA_READ_BURST). |
POST /v1/sites/{sitename}/export-link
Returns a link that downloads the same archive as export.tar.gz without an API key, for people who use Simple Host only through a chat app’s connector (the export_site tool). The link is signed for this owner and this site, expires after 10 minutes, and stops working if the site is deleted or the server restarts. Anyone holding it can download the copy until then, so hand it to the person only.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | Opaque key for a create request. For 24 hours, the same authenticated account, POST path and query, key and exact request body replay the original handler status and body, including errors, without executing it again. A different body under that key returns 409 idempotency_key_reused. Concurrent identical requests wait for the first response. Stored only in this server’s memory; a restart clears it. Authentication and rate limiting still apply to replays. A replay does not extend a minted link’s expiry; use a new key to request a new link. Storage row creates support owner credentials and signed-in visitors; anonymous writes ignore this header. Storage replay also checks current resource permissions and is scoped to the resolved site. | |
| sitename | path | string | yes |
Responses:
| Status | Description |
|---|---|
| 200 | The link |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 404 | Not found |
| 409 | Idempotency-Key reused with a different body (idempotency_key_reused). |
GET /v1/sites/{sitename}/export.tar.gz
Streams the site’s published files (files/), its saved JSON state (state.json) and its collections (collections.json: every list, private ones included, each entry as {id, created_at, submitted_by, data}; submitted_by only on private lists) as one archive. Exists because an event instance is destroyed when the event ends and nothing is backed up, so the person who built something can take it with them. The saved data comes first: the files usually exist somewhere else, the JSON only lives here.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Responses:
| Status | Description |
|---|---|
| 200 | A gzipped tar archive |
| 404 | Not found |
| 429 | Rate limited (code rate_limited). Saved-data reads allow 30 a second per address with a burst of 60 (SAVED_DATA_READ_PER_SEC, SAVED_DATA_READ_BURST). |
PUT /v1/sites/{sitename}/keep
Mark the site Keep (or clear the mark). Where the idle-site cleanup is on (simple-host.app), a site with no visits by people and no new version for 90 days gets its owner an email with a “Keep it” link (and a link to their page, where they can sign in and download it); 30 days later with nothing done it moves to Recently deleted (restorable for 7 days, with a link in a second email). A site marked Keep is never flagged, and marking it ends a pending warning. Sites with their own domain or claimed name are never flagged either. Owner only (API key).
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Request body:
| Name | Type | Required | Description |
|---|---|---|---|
| keep | boolean | yes |
Responses:
| Status | Description |
|---|---|
| 200 | The flag as set |
| 400 | Body is not {“keep”: true|false} |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 404 | Not found |
| 423 | The site is taken down by the operator |
PUT /v1/sites/{sitename}/keep-versions
Every deploy stores a full copy of the site as a new version. keep_versions sets how many this site keeps: N from 1 to 1000 keeps the newest N plus any older live version. 0 goes back to the account default (KEEP_VERSIONS_OVERRIDES), else this server’s setting (KEEP_VERSIONS; 0 there means keep every version). Setting it removes the older versions at once, for good (their files and their rows; removed_versions names them), and every later deploy removes whatever falls outside it. The value is on the site (keep_versions in GET /v1/sites) when set. Owner only (a full API key; a deploy-only key is refused, since this deletes history). Only accounts in KEEP_VERSIONS_SELF_SET (current or old handles) may set a count when that knob is set. Others get 403 keep_versions_fixed with the plain message “Simple Host keeps your 4 latest versions”. Hosted defaults: 4; the chhotabreak, vineetu and jot-transcribe exceptions default to 10.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Request body:
| Name | Type | Required | Description |
|---|---|---|---|
| keep_versions | integer | yes |
Responses:
| Status | Description |
|---|---|
| 200 | The setting as stored, and what it removed |
| 400 | Body is not {“keep_versions”: N} with N from 0 to 1000 |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 403 | A deploy-only key (deploy_only_key), or an account whose version count is fixed (keep_versions_fixed) |
| 404 | Not found |
| 423 | The site is taken down by the operator |
| 429 | Too many site changes from this address; try again shortly |
GET /v1/sites/{sitename}/lock
Owner only (the owner’s key or connector token; a deploy-only key is refused). Says whether the site asks visitors for a passcode and, when it does, the passcode itself and when it was set. The passcode is stored sealed with a server key (AES-256-GCM under PASSCODE_ENC_KEY), so the owner can read it back; the admin’s routes never read it. If the server’s key changed, passcode is absent and note says to set a new one. Never cached (Cache-Control: no-store).
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Responses:
| Status | Description |
|---|---|
| 200 | The site’s passcode state |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 403 | A deploy-only key (deploy_only_key) |
| 404 | Not found |
PUT /v1/sites/{sitename}/lock
Owner only (the owner’s key or connector token; a deploy-only key is refused). Every address of the site (its own address, the person path while its certificate is pending, a free name, a custom domain) then answers a plain “This site is protected” page (401, noindex, no-store; nothing of the site in it; robots.txt answers Disallow: /) until the visitor enters the passcode. A right passcode sets an unlock cookie for that one host (HttpOnly, SameSite=Lax, __Host- prefixed, about 400 days) that lasts until the passcode changes or the owner signs everyone out; .app and .site addresses each need their own unlock. Send {"passcode": "..."} (any characters, at least PASSCODE_MIN_LENGTH, default 6, at most 128; digits only is fine; spaces around it are trimmed) or {"generate": true} for 6 random digits. Setting the same passcode again changes nothing; a different one signs everyone out. The site leaves the person page. The owner’s key, connector token and the admin key still read and write its data; previews still open. Take-down (410) and offline (503) win over the passcode. There is no end date: take the site offline or delete it instead. A passcode keeps out people who don’t have it: search engines, link previews, and anyone who finds or is forwarded the link without it. Anyone given it can open the site and pass it on. It is not a login, it does not say who visited, and it does not make saved data private per person.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Request body:
| Name | Type | Required | Description |
|---|---|---|---|
| generate | boolean | true: the server picks 6 random digits | |
| passcode | string | At least PASSCODE_MIN_LENGTH characters (default 6) |
Responses:
| Status | Description |
|---|---|
| 200 | The passcode is set (the answer carries it) |
| 400 | Body is not {“passcode”: “…”} or {“generate”: true} (invalid_request), or the passcode is too short or too long (invalid_passcode) |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 403 | The operator has taken the site down (site_suspended), or a deploy-only key (deploy_only_key) |
| 404 | Not found |
| 409 | passcodes_not_enabled (SITE_PASSCODES is off or PASSCODE_ENC_KEY is unset on this server), or passcode_needs_own_address (every site on this server shares one address, as on a small box or an event box, so an unlock would cover every site) |
| 429 | Rate limited (code rate_limited). Saved-data reads allow 30 a second per address with a burst of 60 (SAVED_DATA_READ_PER_SEC, SAVED_DATA_READ_BURST). |
DELETE /v1/sites/{sitename}/lock
Owner only (the owner’s key or connector token; a deploy-only key is refused). Every address of the site opens to everyone again. Removing a passcode the site does not have is not an error.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Responses:
| Status | Description |
|---|---|
| 200 | The site has no passcode |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 403 | A deploy-only key (deploy_only_key) |
| 404 | Not found |
| 429 | Rate limited (code rate_limited). Saved-data reads allow 30 a second per address with a burst of 60 (SAVED_DATA_READ_PER_SEC, SAVED_DATA_READ_BURST). |
POST /v1/sites/{sitename}/lock/sign-out-everyone
Owner only (the owner’s key or connector token; a deploy-only key is refused). Ends every unlock at once, on every address: each visitor must enter the passcode again. The passcode stays the same.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Responses:
| Status | Description |
|---|---|
| 200 | Everyone is signed out |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 403 | A deploy-only key (deploy_only_key) |
| 404 | Not found |
| 409 | The site has no passcode (no_passcode) |
| 429 | Rate limited (code rate_limited). Saved-data reads allow 30 a second per address with a burst of 60 (SAVED_DATA_READ_PER_SEC, SAVED_DATA_READ_BURST). |
POST /v1/sites/{sitename}/restore
Brings a site back from Recently deleted under the same name and address, with every version, its saved data, collections and connected address, live at once.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Responses:
| Status | Description |
|---|---|
| 200 | The restored site |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 403 | Site quota reached |
| 404 | No site with that name in Recently deleted |
| 409 | That site is live |
POST /v1/sites/{sitename}/viewers
Owner only. Adds each email (trimmed and lower-cased; letters, digits and ._%+- before the @; matched exactly, with no folding of dots or +tags) to the site’s named viewers and sets access to specific in the same call. At most SITE_VIEWERS_MAX (default 50) per site. added lists the new ones, already_listed the ones that were there.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes | |
| Idempotency-Key | header | string | Opaque key for a create request. For 24 hours, the same authenticated account, POST path and query, key and exact request body replay the original handler status and body, including errors, without executing it again. A different body under that key returns 409 idempotency_key_reused. Concurrent identical requests wait for the first response. Stored only in this server’s memory; a restart clears it. Authentication and rate limiting still apply to replays. A replay does not extend a minted link’s expiry; use a new key to request a new link. Storage row creates support owner credentials and signed-in visitors; anonymous writes ignore this header. Storage replay also checks current resource permissions and is scoped to the resolved site. |
Request body:
| Name | Type | Required | Description |
|---|---|---|---|
| emails | array of string | yes |
Responses:
| Status | Description |
|---|---|
| 200 | Who can open the site now |
| 400 | invalid_request, invalid_email (names the address), or too_many_viewers (more than SITE_VIEWERS_MAX in one call) |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 403 | site_suspended or deploy_only_key |
| 404 | Not found |
| 409 | too_many_viewers (the list would pass SITE_VIEWERS_MAX), passcode_set, named_viewers_need_own_address, sign_in_unavailable or named_viewers_unavailable |
| 429 | Rate limited (code rate_limited). Saved-data reads allow 30 a second per address with a burst of 60 (SAVED_DATA_READ_PER_SEC, SAVED_DATA_READ_BURST). |
DELETE /v1/sites/{sitename}/viewers/{email}
Owner only. The person’s next request is refused. Access does not change: removing the last viewer leaves the site open only to its owner.
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes | |
| path | string | yes |
Responses:
| Status | Description |
|---|---|
| 200 | Who can open the site now (removed names the email) |
| 400 | invalid_email |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 404 | No such site, or the email is not a named viewer (viewer_not_found) |
| 429 | Rate limited (code rate_limited). Saved-data reads allow 30 a second per address with a burst of 60 (SAVED_DATA_READ_PER_SEC, SAVED_DATA_READ_BURST). |
PUT /v1/sites/{sitename}/visibility
Set whether this site is listed on the owner’s account page at https://<handle>.simple-host.app/. public = listed there; unlisted = hidden from it. New sites are created unlisted — listing is an explicit act, not a side effect of deploying.
An unlisted site is hidden from the showcase, and from search engines unless the site has its own domain or name (a custom domain, an address-family address, or a claimed free name): every address of an unlisted site without one answers with X-Robots-Tag: noindex, nofollow, and its /robots.txt says Disallow: / unless the site ships its own robots.txt (the site’s file wins; the header still applies). A public site, or an unlisted one with its own domain or name, gets neither, on every address. The change applies on the next request. Both values are equally reachable by anyone with the URL: unlisted is not privacy and must never be described as such. Owner only (API key).
Auth: an API key (X-API-Key header, or a REST-audience connector token as Authorization: Bearer).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| sitename | path | string | yes |
Request body:
| Name | Type | Required | Description |
|---|---|---|---|
| visibility | string, one of public, unlisted |
yes |
Responses:
| Status | Description |
|---|---|
| 200 | Visibility updated |
| 400 | Invalid visibility value |
| 401 | Missing or invalid API key — send the X-API-Key header, not Authorization Bearer. The body’s code is missing_api_key, wrong_auth_header, invalid_api_key, key_expired or key_expired_idle (create a new key from the Keys panel). |
| 404 | Not found |