Skip to content

Domains API

All endpoints require a bearer token (a login JWT or an API key).

A domain is a hostname you connect to MicroApps and prove you own. Once verified, you can map it to a pod and turn on SSL. Domain names are globally unique across all accounts - see Add a domain for what happens when a name is already taken.

The domain object

json
{
  "id": "dom_abc123",
  "name": "example.com",
  "type": "fqdn",
  "status": "Mapped",
  "hasEncryption": true,
  "ownershipStatus": "owned",
  "podId": "pod_xyz789",
  "mapping": {
    "trafficType": "https",
    "internalPort": 3000,
    "internalIp": "10.20.0.5"
  },
  "dnsStatus": "configured",
  "dnsRecords": [
    {
      "type": "TXT",
      "name": "verify-domain.example.com",
      "value": "microapps-verify=8f2c...",
      "status": "verified",
      "ttl": 300,
      "category": "ownership",
      "required": true
    }
  ],
  "uptimeStatus": "healthy",
  "createdAt": "2026-07-01T12:00:00Z",
  "updatedAt": "2026-07-01T12:05:00Z"
}
FieldMeaning
nameThe hostname.
typeDerived from the name, not something you set: fqdn (a root domain like example.com), subdomain (app.example.com), or local.
statusUnmapped, Configuring, Mapped, ConfigFailed, Healthy, or Unhealthy.
hasEncryptiontrue once SSL is active.
ownershipStatusowned, pending, verified, or unverified.
podIdThe mapped pod, or null when unmapped.
mappingThe current mapping: trafficType (http/https), internalPort, internalIp. null when unmapped.
dnsRecordsEvery record we expect on the domain, each with its live status (verified / unverified / unknown) and whether it is required.
dnsStatusRoll-up: unknown, incomplete, operational, configured, or misconfigured.
uptimeStatushealthy, degraded, down, or empty if not mapped.

List domains

http
GET /api/domains?page=1&limit=20

Returns a paginated list of your domains.

Add a domain

http
POST /api/domains
json
{ "name": "example.com" }

The name is the only input. The type is worked out from the name and returned in the response - you never choose it.

Because domain names are globally unique, one request has four possible outcomes:

StatusWhenWhat it means
201 CreatedNobody has this domain yetAdded. Publish the verify-domain TXT record we show you to prove ownership.
200 OKYou already own itReturns your existing domain, unchanged.
428 Precondition RequiredSomeone else added it but never verifiedYou can claim it. Publish the verify-domain TXT record within 48 hours to take it over. Body: {"message":"Requires validation"}.
409 ConflictVerified and locked by another accountIt cannot be claimed. Body: {"message":"This domain is already verified by another account and cannot be claimed."}.

To verify ownership, publish a TXT record at verify-domain.<your-domain> with the value from the domain's dnsRecords. If a claim is not completed within 48 hours it is dropped.

Credit gate

Like every create action, this needs at least $3 of available credit on your account, or it returns 402.

Describe a domain

http
GET /api/domains/:id

Returns the full domain object, including the current dnsRecords set and their verification status.

Domain stats

http
GET /api/domains/:id/stats

Traffic and response stats for a mapped domain:

json
{
  "requests": 128400,
  "status2xx": 121000,
  "status3xx": 2100,
  "status4xx": 5100,
  "status5xx": 200,
  "bytesIn": 44210000,
  "bytesOut": 981200000,
  "avgResponseMs": 42
}

Values are zero when the domain has no live pod mapping.

Map to a pod

http
POST /api/domains/:id/map
json
{
  "trafficType": "https",
  "port": 3000,
  "podId": "pod_xyz789"
}
FieldTypeNotes
trafficTypestringhttp or https (lowercase). Anything else is 400.
portnumberThe internal port your app listens on. Always send it - there is no default.
podIdstringThe pod to forward to. Must be one of yours.

Traffic for the domain is forwarded to the pod on the given port. You cannot map to a database pod (400 "Cannot map domain to a database pod"). This runs asynchronously: the call returns 201 and the domain moves to Configuring, then Mapped.

Unmap

http
DELETE /api/domains/:id/map

Stops forwarding and clears the mapping (status becomes Unmapped). The SSL certificate is kept, so re-mapping later does not mean re-issuing it. Returns 400 if the domain was not mapped.

Enable SSL

http
POST /api/domains/:id/ssl

Provisions an SSL certificate covering the domain and its subdomains, then auto-renews it for as long as the domain exists. There is no mapping prerequisite, but the domain's ssl DNS record (the _acme-challenge CNAME in dnsRecords) must be published at your DNS provider - issuance and renewal both run through it. The call returns 201 and issuance runs asynchronously.

INFO

Enabling SSL is a sensitive action - a team member needs the domains:ssl permission to call it. There is no disable-SSL endpoint; unmapping keeps the certificate.

Delete a domain

http
DELETE /api/domains/:id

Returns 204. If the domain still has email accounts, you get 400 ("Cannot delete domain with active emails") - delete the mailboxes first.

curl examples

Add a domain:

bash
curl -X POST https://cloud-api.microapps.io/api/domains \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "example.com" }'

Map it to a pod over HTTPS on internal port 3000:

bash
curl -X POST https://cloud-api.microapps.io/api/domains/dom_abc123/map \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "trafficType": "https", "port": 3000, "podId": "pod_xyz789" }'

Enable SSL:

bash
curl -X POST https://cloud-api.microapps.io/api/domains/dom_abc123/ssl \
  -H "Authorization: Bearer $TOKEN"

Built for the long tail.