Appearance
Emails API
All endpoints require a bearer token (a login JWT or an API key) and a permission, listed with each endpoint.
INFO
Email hosting is offered on request - contact us before using these endpoints.
Mailboxes live on a domain you have already verified. If the domain's ownership is not verified yet, creating a mailbox returns 400 "Domain ownership is not verified. Publish the domain ownership TXT record (microapps-verify) and verify the domain before creating emails." See the Domains API.
Mailboxes are addressed by their id - a 24-character hex string - in every path: /api/emails/:domainName/:id.
The email object
json
{
"id": "6a4d5b83b7006ccae6391b22",
"email": "hello@example.com",
"domainName": "example.com",
"storageSize": 5,
"status": "ACTIVE",
"alias": "",
"catchAll": 0,
"diskUsage": 20971520,
"serverId": "6a4d5b83b7006ccae6391f88",
"createdAt": "2026-09-01T12:00:00Z",
"updatedAt": null
}| Field | Meaning |
|---|---|
id | The mailbox id. Use it in every /api/emails/:domainName/:id path. |
email | The full address. |
storageSize | Mailbox quota in GB (1 to 50). |
status | ACTIVE or DISABLED (you set these). SUSPENDED is set by us and locks the mailbox. |
alias | Forwarding target. Empty string means no forwarding. |
catchAll | 1 if this mailbox receives all mail sent to unknown addresses on the domain, else 0. A number, not a boolean, on the way out; a boolean on the way in. |
diskUsage | Current usage in bytes. |
serverId | The mail server the domain's mailboxes live on. |
updatedAt | null until the mailbox is first changed. |
List mailboxes
http
GET /api/emails?domain=example.com&page=1&limit=20&search=helloNeeds email:read. Paginated, newest first. The domain filter is optional; without it you get mailboxes across all your domains. search matches email, domainName, alias, and status.
bash
curl "https://cloud-api.microapps.io/api/emails?domain=example.com" \
-H "Authorization: Bearer $MICROAPPS_API_KEY"json
{
"data": [
{
"id": "6a4d5b83b7006ccae6391b22",
"email": "hello@example.com",
"domainName": "example.com",
"storageSize": 5,
"status": "ACTIVE",
"alias": "",
"catchAll": 0,
"diskUsage": 20971520,
"serverId": "6a4d5b83b7006ccae6391f88",
"createdAt": "2026-09-01T12:00:00Z",
"updatedAt": null
}
],
"total": 1,
"page": 1,
"limit": 20,
"totalPages": 1
}Mailbox counts per domain
http
GET /api/emails/domainsNeeds email:read. One entry per domain that has at least one mailbox, sorted by name:
json
[
{ "domainName": "example.com", "count": 3 }
]Create a mailbox
http
POST /api/emails/:domainNameNeeds email:create, $3 of credit, and a free slot under your mailbox limit.
bash
curl -X POST https://cloud-api.microapps.io/api/emails/example.com \
-H "Authorization: Bearer $MICROAPPS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "hello@example.com",
"password": "Sup3rSecret",
"storageSize": 5,
"serverId": "6a4d5b83b7006ccae6391f88"
}'| Field | Type | Notes |
|---|---|---|
email | string | The full address. Its domain part must equal :domainName, or you get 400 "Email domain does not match the domain name: ... vs ...". The part before the @ must be 1 to 64 characters of lowercase letters, digits, ., _ and -, none of them at the start or end. |
password | string | 8+ characters with an uppercase letter, a lowercase letter, and a digit. |
storageSize | number | Quota in GB, 1 to 50. Required. |
serverId | string | Only required the first time you create a mailbox on the domain (it picks the mail server, and must be one that offers email hosting). Omit it afterwards; the domain's existing server is reused. |
Response 201 with the email object, status already ACTIVE. Email addresses are globally unique, so a duplicate returns 400 "Email is already taken.".
New mailboxes go live within a few seconds rather than instantly.
Describe a mailbox
http
GET /api/emails/:domainName/:idNeeds email:read.
bash
curl https://cloud-api.microapps.io/api/emails/example.com/6a4d5b83b7006ccae6391b22 \
-H "Authorization: Bearer $MICROAPPS_API_KEY"Update a mailbox
http
PUT /api/emails/:domainName/:idNeeds email:update. Every field is optional - send only what you want to change:
bash
curl -X PUT https://cloud-api.microapps.io/api/emails/example.com/6a4d5b83b7006ccae6391b22 \
-H "Authorization: Bearer $MICROAPPS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "alias": "team@example.com", "storageSize": 10 }'| Field | Type | Notes |
|---|---|---|
password | string | New password (same rules as create). |
alias | string | Forward a copy of incoming mail here. Send "" to clear it. Cannot equal the mailbox's own address (400 "Alias cannot be the same as the email"). |
catchAll | boolean | Make this the catch-all for the domain. Sent as true/false; read back as 1/0. Only one mailbox per domain can hold it: enabling it while another already does returns 400 naming that mailbox. |
status | string | ACTIVE or DISABLED only, otherwise 400 "Status can either be ACTIVE or DISABLED". |
storageSize | number | New quota in GB, 1 to 50. |
Response 200 with the updated mailbox. Sending nothing that differs from the current values is a no-op that still returns 200.
Suspended mailboxes
A SUSPENDED mailbox rejects all edits (400 "Cannot update a suspended email"), and you cannot set SUSPENDED yourself - it is an account-level state we manage.
Delete a mailbox
http
DELETE /api/emails/:domainName/:idNeeds email:delete. Returns 204. The mailbox and its mail are gone; this is irreversible.
Mailbox stats
http
GET /api/emails/:domainName/:id/mailbox-statsNeeds email:read. A live count plus the last activity timestamps:
json
{
"messageCount": 214,
"lastSent": "2026-09-15T09:12:00Z",
"lastReceived": "2026-09-16T06:44:00Z"
}lastSent and lastReceived are null until the mailbox has sent or received something. messageCount is best effort: if the count cannot be taken, it comes back 0 rather than failing the call.
Set up email on a domain
http
POST /api/emails/:domainName/email-setupNeeds email:update and $3 of credit.
bash
curl -X POST https://cloud-api.microapps.io/api/emails/example.com/email-setup \
-H "Authorization: Bearer $MICROAPPS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "emailServerId": "6a4d5b83b7006ccae6391f88" }'Response 201 with no body. Turns on email for the domain and adds the records you need to publish to the domain's dnsRecords (all with TTL 300):
| Record | recordType | Name | Purpose | Required |
|---|---|---|---|---|
| MX | MX | @ | Where mail is delivered | Yes |
| SPF | TXT | @ | Authorises our servers to send for you | Yes |
| DKIM | TXT | <domain-id>._domainkey | Signs your outbound mail | Yes |
| DMARC | TXT | _dmarc | Reporting and policy | Optional |
| SRV | SRV | _imaps._tcp | IMAP autodiscovery (993) | Optional |
| SRV | SRV | _submission._tcp | SMTP submission autodiscovery (587) | Optional |
Read the exact values back from GET /api/domains/:id. A domain that already has email returns 400 "Domain already has an email server assigned" - use regenerate instead.
Revalidate DNS
http
POST /api/emails/:domainName/revalidate-dnsNeeds email:update. Re-checks every published record on the domain right now - ownership, pointing and SSL records included, not just the email ones - and updates each record's status and the domain's dnsStatus. Response 200 with no body; read the results back from GET /api/domains/:id. This is what the console's Recheck DNS button calls.
Regenerate DNS
http
POST /api/emails/:domainName/regenerate-dnsNeeds email:update. Wipes the domain's record list and rebuilds it from a fresh identity. Response 200 with no body. This is what the console's Regenerate Records button calls.
Regenerating invalidates three records you have already published
It rotates all of these at once, and every record starts unverified again:
- the ownership TXT gets a new verification token,
- the DKIM key rotates, so the
_domainkeyTXT value changes, - the SSL delegation label rotates, so the
_acme-challengeCNAME target changes.
Update all three at your DNS provider afterwards. Certificates already issued keep serving until they expire, but the next renewal fails if the _acme-challenge CNAME is stale.
Remove email setup
http
DELETE /api/emails/:domainName/email-setupNeeds email:update. Strips the email records and unassigns the mail server. Ownership, pointing and SSL records are left alone. Returns 204.
Delete the domain's mailboxes first: while any remain, this returns 400 "Cannot delete domain with active emails".
Metrics
Sent / received / bounced counts at three scopes, all needing email:read. Add ?from= and ?to= (RFC3339 or YYYY-MM-DD, where to counts to end of day) to pick a window - the default is the last 7 days and the range is capped at 90 days.
| Endpoint | Scope |
|---|---|
GET /api/emails/:domainName/:id/metrics | Mailbox total |
GET /api/emails/:domainName/:id/metrics/daily | Mailbox, daily |
GET /api/emails/:domainName/metrics | Domain total |
GET /api/emails/:domainName/metrics/daily | Domain, daily |
GET /api/emails/metrics/account | Account total |
GET /api/emails/metrics/account/daily | Account, daily |
Totals:
bash
curl "https://cloud-api.microapps.io/api/emails/example.com/metrics?from=2026-09-09&to=2026-09-16" \
-H "Authorization: Bearer $MICROAPPS_API_KEY"json
{ "sent": 1842, "received": 5310, "bounced": 7 }Daily variants return an array, one row per day that had activity:
json
[
{ "date": "2026-09-15", "sent": 264, "received": 771, "bounced": 1 },
{ "date": "2026-09-16", "sent": 301, "received": 688, "bounced": 0 }
]The two account-level endpoints are the only ones that take no domain in the path.
Unsubscribes
Every message you send carries a one-click unsubscribe link. When a recipient uses it, their address is added to your domain's suppression list and you stop sending to it.
http
GET /api/unsubscribes/:domainId?page=1&limit=20Needs email:read. Note this one takes the domain's id, not its name. Paginated list of unsubscribed addresses:
bash
curl https://cloud-api.microapps.io/api/unsubscribes/6a4d5b83b7006ccae6391c41 \
-H "Authorization: Bearer $MICROAPPS_API_KEY"json
{
"data": [
{
"id": "6a4d5b83b7006ccae6392055",
"email": "someone@recipient.example",
"domain": "example.com",
"accountId": "6a4d5b83b7006ccae639100a",
"domainId": "6a4d5b83b7006ccae6391c41",
"createdAt": "2026-09-14T18:22:41Z"
}
],
"total": 1,
"page": 1,
"limit": 20,
"totalPages": 1
}http
DELETE /api/unsubscribes/:domainId/:emailNeeds email:update. Removes an address from the list, which re-enables sending to it. Returns 204. A malformed domain id returns 400 "Invalid domain ID".
bash
curl -X DELETE https://cloud-api.microapps.io/api/unsubscribes/6a4d5b83b7006ccae6391c41/someone@recipient.example \
-H "Authorization: Bearer $MICROAPPS_API_KEY"