Amba

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}$ (lowercase letters, digits and hyphens, starting with a letter) and becomes part of the public URL's hostname. Public URL: https://{slug}.app.amba.host, where slug is the first 8 characters of your project id plus the site name. Slugs are unique: in the rare case another project's site already holds that slug, yours uses the first 12 hex characters of your project id instead. Read the slug (and the deploy's url) from the API response.
  • 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:

amba sites deploy ./dist --name marketing

The CLI leaves dotfiles out of every upload, at any depth: .env, .env.local and the other .env* files, .amba/, .mcp.json, .git, and any other name that starts with a dot. .well-known/ at the top of the directory is the one exception, so files such as .well-known/apple-app-site-association and .well-known/assetlinks.json still publish. The deploy output lists what it skipped.

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):

MethodPathDescription
POST/admin/projects/:projectId/sitesRegister a site. Body { name }.
POST/admin/projects/:projectId/sites/:name/deploymentsUpload a deployment (multipart).
GET/admin/projects/:projectId/sitesList sites.
GET/admin/projects/:projectId/sites/:nameDescribe a site + its custom domains.
PATCH/admin/projects/:projectId/sites/:nameSet status (active / disabled).
POST/admin/projects/:projectId/sites/:name/rollbackRepoint to a prior deployment.
DELETE/admin/projects/:projectId/sites/:name?confirm=:nameDelete a site (full cascade).

A successful deploy returns the canonical URL plus file/byte counts:

{
  "data": {
    "deployment_id": "…",
    "site_name": "marketing",
    "url": "https://abcd1234-marketing.app.amba.host",
    "deployed_at": "2026-05-27T10:00:00.000Z",
    "status": "active",
    "file_count": 12,
    "total_bytes": 348201
  }
}

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:

RequestResolves to
/index.html
/start/start/index.html. When only start.html exists, a 308 redirect to /start.
/startThe exact file start, then start.html. When only start/index.html exists, a 308 redirect to /start/.
/start.html, /app.cssThat exact file.
Anything unresolvedYour 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.

amba sites domain add marketing app.example.com
MethodPathDescription
POST/admin/projects/:projectId/sites/:name/domainsAttach a hostname.
GET/admin/projects/:projectId/sites/:name/domainsRe-poll and list attached hostnames + authoritative readiness.
POST/admin/projects/:projectId/sites/:name/domains/:hostname/refresh-certForce provider, Worker route, host KV, and managed-DNS reconciliation.
DELETE/admin/projects/:projectId/sites/:name/domains/:hostnameDetach 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:

{
  "data": {
    "hostname": "app.example.com",
    "cert_status": "pending_validation",
    "ownership_status": "pending",
    "route_status": "pending",
    "kv_status": "pending",
    "dns_managed": false,
    "dns_status": "not_required",
    "claim_expires_at": "2026-10-17T12:00:00.000Z",
    "live": false,
    "dns_target": "abcd1234-marketing.app.amba.host",
    "dns_record_type": "CNAME",
    "is_apex": false,
    "dns_note": "Publish a real CNAME; ALIAS, ANAME, and copied A/AAAA records do not activate standard custom-hostname routing.",
    "ssl_validation": [
      {
        "txt_name": "_acme-challenge.app.example.com",
        "txt_value": "<token>"
      }
    ],
    "ownership_verification": {
      "type": "txt",
      "name": "_amba-challenge.app.example.com",
      "value": "<token>"
    }
  }
}

The flow is:

  1. Publish the verification records. Add the ssl_validation TXT record(s) and the ownership_verification TXT record at your DNS provider. If ssl_validation is empty on the immediate response, it populates shortly — re-fetch the live records via the refresh-cert endpoint or amba_sites_list_domains.
  2. Point DNS at dns_target. Publish the returned dns_record_type exactly; 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 an ALIAS, ANAME, or resolved A/AAAA value. Use a subdomain if your DNS provider cannot store an apex CNAME (see dns_note on the response).
  3. 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_status and kv_status confirm the edge route and host-to-site mapping. Purchased domains also require dns_status: active; externally managed DNS reports not_required.

A hostname serves your site only after ownership_status is active and the hostname's DNS shows this attachment: either the ownership_verification TXT record from this attach response, or a CNAME that reaches your project's own dns_target. Until then Amba installs no edge route and no host-to-site mapping for it, so route_status and kv_status stay pending. Amba checks pending hostnames every few minutes and routes each one as soon as both hold; a list call or refresh-cert does the same at once.

An attachment that has not routed yet is a claim that lasts 72 hours (claim_expires_at). Once it lapses, the next attach of that hostname by any project takes it over, so publish the records within that window. If your claim lapses, attach the hostname again and publish the new ownership_verification value: each attachment gets its own. While another project's claim is current, attaching the same hostname returns 409 ALREADY_ATTACHED. Attaching is limited to 10 hostnames per project per hour.

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.

When you detach a hostname, also delete the DNS record that points it at Amba. The detach response repeats this in dns_cleanup: a record left in place lets whoever attaches the hostname next complete verification through it.

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):

curl -X POST 'https://api.amba.dev/v1/admin/projects/$PROJECT_ID/sites/marketing/rollback' \
  -H 'Authorization: Bearer $AMBA_PAT' \
  -H 'Content-Type: application/json' \
  -d '{ "deployment_id": "…" }'

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:

{
  "data": {
    "name": "marketing",
    "deleted": true,
    "cascade": { "domains_removed": 1, "objects_removed": 12 }
  }
}

MCP tools

ToolDoes
amba_sites_deployCreate-or-deploy in one call.
amba_sites_listList sites.
amba_sites_getDescribe a site + its domains.
amba_sites_deleteDelete a site (cascade).
amba_sites_add_domainAttach a custom hostname.
amba_sites_list_domainsList hostnames + cert status.
amba_sites_remove_domainDetach a hostname (idempotent).

See the CLI reference for the full amba sites command set.

On this page