Sites
Host a static site on Amba — deploy a bundle of files, serve it at a free subdomain, attach your own custom domain with managed TLS, and roll back instantly.
Amba hosts static sites — marketing pages, docs, SPAs, anything that's a
bundle of files. Deploy a directory and Amba serves it at
https://{slug}.app.amba.host with a managed TLS certificate. Attach a
custom domain when you're ready, and roll back to any previous deployment
in one call. Every deployment is immutable, so a rollback is just a
pointer flip — no rebuild.
The fastest paths are the CLI (amba sites deploy ./dist) and the
amba_sites_* MCP tools. The REST surface below is what both wrap.
On the free tier, a site serves with a small "Built with Amba · preview" bar and an X-Robots-Tag: noindex header, and custom domains are a paid feature. Upgrading the project removes the bar and
unlocks custom domains. See free-tier limits.
Concepts
- Site — a named container for deployments. The name must match
^[a-z][a-z0-9_-]{0,49}$and becomes part of the public URL. Public URL:https://{slug}.app.amba.host, whereslugis the first 8 characters of your project id plus the site name. - Deployment — one immutable upload of files. The current deployment serves all traffic; older ones are retained for rollback.
- Custom domain — your own hostname (e.g.
app.example.com) bound to the site, with a TLS certificate Amba provisions and renews for you.
Deploy
The CLI bundles a directory and ships it in one command:
From an agent, amba_sites_deploy creates the site (if needed) and
uploads the files in a single tool call — pass project_id, name, and a
files object mapping each relative path to its content.
Under the hood, deploy is two REST calls — register the site, then upload the bundle as multipart form data (one part per file, keyed by relative path):
| Method | Path | Description |
|---|---|---|
| POST | /admin/projects/:projectId/sites | Register a site. Body { name }. |
| POST | /admin/projects/:projectId/sites/:name/deployments | Upload a deployment (multipart). |
| GET | /admin/projects/:projectId/sites | List sites. |
| GET | /admin/projects/:projectId/sites/:name | Describe a site + its custom domains. |
| PATCH | /admin/projects/:projectId/sites/:name | Set status (active / disabled). |
| POST | /admin/projects/:projectId/sites/:name/rollback | Repoint to a prior deployment. |
| DELETE | /admin/projects/:projectId/sites/:name?confirm=:name | Delete a site (full cascade). |
A successful deploy returns the canonical URL plus file/byte counts:
The per-file cap is 25 MiB. A failed upload leaves your live site untouched — the new deployment is only made current after all files land.
Routing
Amba maps each request path to a file in the current deployment using the
same conventions as other static hosts, so directory-style exports from
Astro, Hugo, Next.js (output: 'export'), and similar generators work with
their default links. Each request shape resolves in this order:
| Request | Resolves to |
|---|---|
/ | index.html |
/start/ | start/index.html. When only start.html exists, a 308 redirect to /start. |
/start | The exact file start, then start.html. When only start/index.html exists, a 308 redirect to /start/. |
/start.html, /app.css | That exact file. |
| Anything unresolved | Your 404.html with status 404, or the single-page-app fallback. |
Paths with a dot in the last segment, such as /blog/release-2.0, follow
the /start rules once the exact file misses, so dotted slugs resolve to
release-2.0.html or redirect to release-2.0/. Redirects keep the query
string, so /start?ref=nav lands on /start/?ref=nav. The trailing slash
matters for directory pages because the browser resolves relative links
(./logo.png, ../) against it. File names with spaces or non-ASCII
characters resolve from their percent-encoded URLs (/caf%C3%A9 serves
café.html).
Not found. Ship a 404.html at the root of the bundle and Amba serves
it, with status 404, for every path that doesn't resolve.
Single-page apps. Without a 404.html, a path with no file extension
(/dashboard/settings) serves the root index.html with status 200 so
your client-side router can take over. A missing file with an extension
(/assets/missing.js) always returns 404, so a broken script or
stylesheet reference never receives HTML. If your framework writes its
single-page-app shell to 404.html (some static adapters do), every deep
link returns status 404. Have it write the shell to index.html so the
single-page-app fallback serves it.
Caching. HTML responses and redirects are sent with
Cache-Control: no-cache, no-store, must-revalidate, so a new deployment is
visible on the next request. Other files are sent with
public, max-age=3600; give long-lived assets content-hashed filenames
(most bundlers do this by default) so a deploy never serves a stale copy.
Custom domains
Bind your own hostname, then publish the DNS records Amba returns. Amba registers the hostname and provisions and renews the certificate for you — you don't need any third-party credentials.
Subdomains like app.yourdomain.com are the simplest path. Apex (root) domains such as
yourdomain.com work only when authoritative DNS can store a real CNAME record at the apex.
Provider-native CNAME-at-root flattening is compatible when the provider retains that record as a
CNAME in its control plane. ALIAS, ANAME, and copied A/AAAA addresses are not substitutes:
they remove the CNAME relationship required for standard custom-hostname activation. If your
provider cannot store an apex CNAME, use www or another subdomain. Platform domains
(*.amba.host, *.amba.dev) are reserved and rejected.
| Method | Path | Description |
|---|---|---|
| POST | /admin/projects/:projectId/sites/:name/domains | Attach a hostname. |
| GET | /admin/projects/:projectId/sites/:name/domains | Re-poll and list attached hostnames + authoritative readiness. |
| POST | /admin/projects/:projectId/sites/:name/domains/:hostname/refresh-cert | Force provider, Worker route, host KV, and managed-DNS reconciliation. |
| DELETE | /admin/projects/:projectId/sites/:name/domains/:hostname | Detach a hostname. |
Lifecycle fields are read-only. The former PATCH .../domains/:hostname
surface returns 405 LIFECYCLE_READ_ONLY; clients cannot declare a hostname
active. Use the refresh endpoint and wait for the server-derived readiness
field instead.
The attach response returns the records you publish to verify ownership
(ssl_validation + ownership_verification), the dns_target to point a
CNAME at, a Public-Suffix-List-derived is_apex flag, an unconditional
dns_note record-type warning, and independent provider and serving-plane
statuses:
The flow is:
- Publish the verification records. Add the
ssl_validationTXT record(s) and theownership_verificationTXT record at your DNS provider. Ifssl_validationis empty on the immediate response, it populates shortly — re-fetch the live records via therefresh-certendpoint oramba_sites_list_domains. - Point DNS at
dns_target. Publish the returneddns_record_typeexactly; the real-CNAME warning is returned for every hostname, not only apexes. For a root domain (is_apex: true), provider-native CNAME-at-root flattening is valid only when the provider stores a real CNAME. Do not substitute anALIAS,ANAME, or resolvedA/AAAAvalue. Use a subdomain if your DNS provider cannot store an apex CNAME (seedns_noteon the response). - Wait for every required plane to reach
active.ownership_status(pending → active) confirms you control the hostname;cert_status(pending_validation → pending_issuance → pending_deployment → active) tracks the TLS certificate.route_statusandkv_statusconfirm the edge route and host-to-site mapping. Purchased domains also requiredns_status: active; externally managed DNS reportsnot_required.
Your domain is live — and the certificate renews automatically — only once
the server-derived live field is true. A successful TLS handshake, or even
active certificate and ownership states together, do not prove the Worker
route and host KV are ready. Poll the list endpoint (or
amba_sites_list_domains), or call refresh-cert to force all-plane repair.
route_status, kv_status, and managed dns_status errors are retryable and
remain eligible for bounded CLI polling/refresh. Provider-terminal
cert_status or ownership_status errors require re-attachment.
Rollback
Every deployment is immutable and kept on file. To revert, point the site
back at a previous deployment_id (find ids via amba_sites_get or
GET /sites/:name):
The flip is atomic — the next request serves the prior bundle. You can't
roll back to a failed deployment, and rolling back to the already-current
deployment is rejected.
Pause and delete
PATCH .../sites/:name with { "status": "disabled" } stops the site (and
its custom domains) from serving without losing the deployment history; set
it back to active to resume.
DELETE .../sites/:name?confirm=<name> tears everything down — custom
hostnames are unbound, every deployment's files are removed, and the site
row is archived. The ?confirm=<name> guard is required; the CLI and MCP
tool set it for you. The response reports what was cleaned up:
MCP tools
| Tool | Does |
|---|---|
amba_sites_deploy | Create-or-deploy in one call. |
amba_sites_list | List sites. |
amba_sites_get | Describe a site + its domains. |
amba_sites_delete | Delete a site (cascade). |
amba_sites_add_domain | Attach a custom hostname. |
amba_sites_list_domains | List hostnames + cert status. |
amba_sites_remove_domain | Detach a hostname (idempotent). |
See the CLI reference for the full amba sites command set.