Manage domains, email accounts, aliases, forwarders, and DNS via REST API. Full programmatic control over your email infrastructure.
Developer FriendlyOne key, one hostname67 operations Β· 24 objectsBearer-token JSON API
Overview
One API key covers everything your service can do through the API: the mail server's own management and data API, relayed through our passthrough, plus our forwarders endpoints. There is no separate key for each, and no second base URL to configure.
The mail-server passthrough (GET /mail/session, POST /mail, plus blob upload/download and an event stream) carries every JMAP method your key is allowed to call: mailboxes, domains, DKIM, aliases, app passwords, additional API keys, distribution lists, mail filters, the vacation responder, and more β see Mailboxes & access, Domains & DNS, API keys, and Mail data (JMAP). Forwarders are the one exception: they are system-managed routing rules your mail key is never allowed to write directly, so they have their own REST endpoints, documented on this page under Forwarders.
Base URL
https://dash.mailbux.com/api/v1 β every path on this page, including /mail, is relative to this one base URL.
Two older bases keep working and accept the exact same paths, scopes and keys: /api/v1/mailbux and /api/mailbux/v1. New integrations should use the base above; the older ones are not deprecated, just superseded.
Authentication
Send your key as a bearer token: Authorization: Bearer YOUR_API_KEY.
Create one key per service from your dashboard (service -> Manage -> API). It is shown once and is never recoverable β create a new one if it is lost. See API keys for the full key-management reference.
Paid and LTD plans only. A Free-plan service has no API keys, and downgrading an existing service to Free revokes every key it has.
App passwords are a different credential: they authenticate mail apps (IMAP/SMTP/JMAP) and let you log in as one mailbox directly. They are never accepted anywhere on this API β see Getting started β 5. Read a mailbox's mail.
What your key can reach
Your key is bound to one service, its owner, and its tenant namespace, so it can never reach another customer, another service, or any provisioning credential.
Each endpoint, and each JMAP method, requires its own scope. A key without that scope gets 403 β it is never silently downgraded to a partial response.
On the forwarders endpoints, an id belonging to another service is always 404: never returned, and never distinguishable from "does not exist". On the mail-server passthrough, objects outside your tenant are simply not visible to your key.
A set of server-wide and system objects on the mail server is never reachable by any organization key, no matter the scope it carries β see Not available to API keys.
Errors
Every error from this API β from the forwarders endpoints and from the mail-server passthrough's own transport layer β uses the same envelope:
The bearer token is missing, an unrecognized shape, or does not match any key.
401
token_suspended
The key is suspended. It stays disabled until it is enabled again from the dashboard.
401
token_inactive
The key has been revoked or has expired.
401
token_binding_invalid
The service, owner, or tenant this key was bound to has changed. Create a new key.
403
forbidden
The key lacks the scope this endpoint needs, the request came from an IP outside the key's allow-list, or the request used the wrong brand host for this service.
403
api_unavailable
The service is inactive, on a Free plan, or its mail connection is not ready.
429
rate_limited
More than 60 requests in 60 seconds from this key and source IP.
503
api_unavailable
The API is temporarily unavailable during a deploy window. Retry shortly.
Getting started β 1. Create a key
Open the service in your dashboard, go to Manage -> API, and create a key. Copy it immediately β it is shown once. Paid and LTD plans only.
Getting started β 2. Find your account ids
Call GET /mail/session. It returns the JMAP session object for your tenant, including the accounts your key can act on. The four URLs it advertises (apiUrl, downloadUrl, uploadUrl, eventSourceUrl) already point back at this API, never at the mail server directly.
Call GET /forwarders to see the mail-routing rules configured for this service. Forwarders are not part of the /mail passthrough β see Forwarders below for the full read/write reference.
Your API key is your organization's admin: every call above manages mailboxes, domains, aliases, DKIM, API keys and forwarders, the same as an administrator in the mail portal. Like that administrator, it cannot open the mail inside a mailbox β its own JMAP account (primaryAccounts["urn:ietf:params:jmap:mail"] from step 2's GET /mail/session) is its own, empty mailbox, not any mailbox in your organization.
To read or send a specific mailbox's mail from your app, authenticate as that mailbox instead, with JMAP: session https://my.mailbux.com/.well-known/jmap, HTTP Basic with the mailbox address as username and an app password as password β created for that mailbox on its own dashboard, under Manage -> App Passwords β then call the apiUrl the session document returns.
App passwords are not accepted at https://dash.mailbux.com/api/v1/* β that base takes API keys only, which is why sending one there returns 401.
curl -L -u "[email protected]:APP_PASSWORD" \
"https://my.mailbux.com/.well-known/jmap"
# then POST to the "apiUrl" the session document returns
curl -u "[email protected]:APP_PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
"methodCalls": [
["Mailbox/get", {"accountId": "YOUR_ACCOUNT_ID"}, "0"],
["Email/query", {
"accountId": "YOUR_ACCOUNT_ID",
"filter": {"inMailbox": "YOUR_INBOX_ID"},
"sort": [{"property": "receivedAt", "isAscending": false}],
"limit": 20
}, "1"]
]
}' \
"<apiUrl from the session above>"
Mail server API basics
Your API key is also your credential on the mail server itself. POST /mail forwards a standard JMAP request to it and relays the response back untouched, so every mailbox, domain, DKIM, distribution-list, app-password and API-key operation described in Mailboxes & access, Domains & DNS, API keys and Mail data (JMAP) rides on the mechanics described here: the request envelope, capabilities, pagination, incremental sync, blobs and the event stream. Read this section once; every other object on this site builds on it.
Forwarders are the one part of this API that is not a mail-server passthrough β they are described separately in the Forwarders section because they run as system-managed rules your key is never given direct write access to.
Request envelope, call ids and back-references
Every POST /mail body is a JMAP request: {"using": [...], "methodCalls": [...]}. Each entry in methodCalls is a 3-element array β the method name (e.g. Mailbox/get), its arguments, and a call id you choose. The response's methodResponses array echoes the same call id back so you can match results to calls, especially when you send more than one call at once.
A later call in the same request can reuse the result of an earlier one with a back-reference, instead of making a second round trip. In place of a plain argument (typically ids), pass {"resultOf": "<callId>", "name": "<Type>/query", "path": "/ids"} β the server substitutes the referenced call's result at that JSON path.
For example, this finds a domain by name and fetches it in one request, without knowing its id up front:
The using array lists every capability a request needs; a call that touches a capability you didn't list fails with unknownCapability. These are the capabilities relevant to a tenant API key:
Capability URN
Unlocks
Available to your key
urn:ietf:params:jmap:core
Base JMAP semantics: get/set/query/changes/queryChanges, the session object, back-references. Include this in every call.
Yes
urn:ietf:params:jmap:mail
Standard mailbox data β Mailbox, Email, Thread, SearchSnippet (see Mail data (JMAP)).
Yes
urn:ietf:params:jmap:submission
Sending mail and sender identities β Identity, EmailSubmission (see Mail data (JMAP)).
Yes
urn:ietf:params:jmap:sieve
Your mailbox's own filtering rules β SieveUserScript (see Mail data (JMAP)).
Yes
urn:ietf:params:jmap:quota
Storage usage and limits β Quota (see Mail data (JMAP)).
Yes
urn:ietf:params:jmap:vacationresponse
Your out-of-office auto-reply β VacationResponse (see Mail data (JMAP)).
Yes
urn:ietf:params:jmap:principals
Directory/sharing principal discovery.
No β withheld from every mailbox; operator-only.
Capabilities for the custom management methods
The custom management methods used throughout this API β the x:Account, x:Domain, x:DkimSignature, x:MailingList, x:AppPassword, x:ApiKey, x:AccountSettings and x:AccountPassword methods documented in Mailboxes & access, Domains & DNS and API keys β need no extra capability in using beyond urn:ietf:params:jmap:core: this API adds their management capability for you automatically. That is why every worked example calling one of those methods shows using set to exactly ["urn:ietf:params:jmap:core"] β you do not need to add anything else for them to work.
Session limits
These are the limits your mail server enforces (advertised in your session's capabilities), plus this API's own, sometimes stricter, ceilings:
Limit
Value
What it means
Calls per request
16
Maximum entries in one methodCalls array. Split a larger workflow into more than one POST /mail call.
Ids per Foo/get
500
A single Foo/get call can fetch at most 500 ids; asking for more fails the whole call with requestTooLarge. Page longer id lists in batches of 500 or fewer.
Objects per Foo/set
Not published as a fixed number
Your session advertises this limit, but no specific figure is confirmed β keep create/update/destroy batches modest.
Request body size
1 MB
This API's own hard cap on a POST /mail JSON body (stricter than the mail server's own advertised 10 MB) β a larger body never reaches the mail server; see payload_too_large.
Query page size
Up to 200 recommended
A Foo/query without an explicit limit can return a very large page (unadvertised defaults up to roughly 5000 have been observed). Always pass an explicit limit between 1 and 200 for predictable pages.
Blob upload size
Up to 25 MB
See Upload a blob above.
JMAP error types: request and method level
These are separate from the REST-level errors in the Overview section (unauthorized, forbidden, rate_limited, and so on), which apply before a request even reaches the mail server. The types below appear inside a JMAP response itself β either as the whole response body (a request-level error) or inside one methodResponses entry (a method-level error, shaped ["error", {"type": "...", ...}, "<callId>"]).
Type
Level
Meaning
unknownCapability
Request
A URN in using isn't recognized.
notJSON
Request
The request body isn't valid JSON.
notRequest
Request
The body is JSON but not a valid JMAP request object.
limit
Request
A request-level limit was exceeded; the response names which one (maxSizeRequest, maxCallsInRequest, maxConcurrentRequests, maxSizeUpload, maxConcurrentUpload).
unknownMethod
Method
The method name isn't recognized for this account β for example, a per-account method your mail server doesn't implement.
invalidArguments
Method
A required argument is missing or malformed.
invalidResultReference
Method
A back-reference (#ids) pointed at a call or path that doesn't exist or didn't produce the expected shape.
forbidden
Method
Your key doesn't have permission for this method or this specific call.
accountNotFound
Method
The accountId doesn't exist, or isn't visible to your key.
accountNotSupportedByMethod
Method
The method does not apply to the given account.
accountReadOnly
Method
A write was attempted against a read-only account context.
requestTooLarge
Method
The call's id list (or the batch) exceeded a server limit β for example, more than 500 ids in one Foo/get. The whole call fails, not just the extra ids.
unsupportedFilter
Query
A filter condition this object/method does not support β for example, querying DKIM signatures without the required domainId filter.
unsupportedSort
Query
A sort key this method does not support.
anchorNotFound
Query
The anchor id used for windowed query results does not exist.
cannotCalculateChanges
Changes
Your sinceState is too old or unrecognized β re-sync with a fresh Foo/get/Foo/query and resume from its state.
invalidForeignKey
Extension
A property referenced an id that exists but isn't a valid target for that relationship β for example, an id outside what your key can reach.
JMAP error types: Foo/set calls
These appear inside a /set method response, in notCreated, notUpdated or notDestroyed, keyed by the id (or your chosen creation id) that failed β see the /set response shapes shown throughout Mailboxes & access, Domains & DNS and API keys.
Type
Meaning
forbidden
Your key lacks create/update/destroy permission for this object.
overQuota
The write would exceed your plan's storage or object quota.
tooLarge
One object in the /set batch is too large.
rateLimit
Standard JMAP rate-limit error for /set calls.
notFound
update/destroy targeted an id that doesn't exist β nothing is changed.
invalidProperties
One or more properties failed validation; the response names them in a properties array β for example, a requested account quota below the server's 1 GB minimum.
singleton
For a singleton object (for example your account password or account settings), only update with id "singleton" is accepted β create/destroy are rejected.
invalidPatch
The patch shape itself is invalid β for example, sending an indexed-map property (like the alias list in Mailboxes & access) as a plain JSON array, or trying to change a server-managed property.
willDestroyContents
The destroy would also remove dependent content it did not ask to remove.
stateMismatch
You passed ifInState and the object has changed since β re-fetch and retry with the current state.
Pagination
Foo/query returns matching ids, not full records β pass the ids (or a back-reference) to Foo/get for the data. Window the results with position (a 0-based offset β from the start, or from anchor when both are given), anchor (an id already in the result set, to page around instead of by absolute offset; anchorOffset shifts before or after it), and limit (page size β see Session limits above for the recommended range). Pass calculateTotal: true to get a total count back; it is not calculated by default.
Incremental sync: /changes and queryChanges
Foo/changes takes a sinceState (the state string from a prior get, set or query response) and returns the created, updated and destroyed ids since then, plus a new state to use next time. If sinceState is too old or unrecognized, the call fails with cannotCalculateChanges β re-sync with a fresh Foo/get/Foo/query and start again from its state.
Foo/queryChanges does the same for a saved query's result list β it reports which ids were added or removed at which positions, rather than a plain changed-id set, so a client keeping a live filtered list in sync doesn't have to re-run the whole query.
GET/mail/session
Get your JMAP session
Returns your mail server session: your accounts, capabilities, and the URLs for every other mail-server call, rewritten to this API's own host.
the mail server's session response is unreachable, isn't valid JSON, or its URL templates don't exactly match the expected shape
relayed
(none)
the mail server answers with a non-200 that still passes template validation β its status and body are relayed as-is
Notes
Only apiUrl, downloadUrl, uploadUrl and eventSourceUrl are rewritten to this API's host. Every other field β accounts, capabilities, username, state, primaryAccounts β passes through from the mail server unchanged.
No side effects beyond the outbound call to your mail server.
Also subject to the standard authentication, authorization and rate-limit errors described in the Overview section (missing/expired/suspended key, wrong scope, disallowed IP or host, 429 rate_limited).
POST/mail
Send JMAP method calls
Forwards a JMAP request to your mail server and relays its response verbatim. Every custom and standard object documented on this site is driven through this one endpoint.
Scope: mail:native
Parameters
Send JMAP method calls parameters
Name
Type
Access
Default
Description
using*
String[]
β
β
The capability URNs this request needs. Always include urn:ietf:params:jmap:core; see Capability URNs below for the others.
methodCalls*
Array<[String, Object, String]>
β
β
A list of [method name, arguments, call id] triples, evaluated in order. The call id is any string you choose, used to match a methodResponses entry back to its call.
the request body exceeds 1 MB (see Session limits below)
413
upstream_response_too_large
your mail server's reply exceeds 25 MB (does not apply to GET /mail/download/..., which streams unbounded)
502
invalid_mail_session
this API's own connection to your mail server is misconfigured
502
mail_server_unavailable
the outbound call to your mail server itself failed or timed out
relayed
(none)
your mail server answers with its own status (for example, if your key was independently revoked directly on the mail server even though this API's own record of it is still active)
Notes
A request with more than one call in methodCalls is relayed as a single HTTP call β partial success/failure across the calls is your mail server's own per-call JMAP convention (see JMAP error types below), not reflected in the HTTP status.
Also subject to the standard authentication, authorization and rate-limit errors described in the Overview section.
GET/mail/download/{accountId}/{blobId}/{name}
Download a blob
Downloads a blob (an attachment, an exported message, an uploaded file) by id, streamed straight from your mail server.
Scope: mail:native
Parameters
Download a blob parameters
Name
Type
Access
Default
Description
accountId*
String
β
β
The account the blob belongs to.
blobId*
String
β
β
The blob id, from an Email body part, a Blob/ method response, or an upload response.
name*
String
β
β
The filename to send back in the response (used for Content-Disposition, not for lookup).
accept
String
β
application/octet-stream
Requested Content-Type β a media type or */*. This is the only query parameter accepted; any other query key is rejected.
Binary stream. `Content-Type`, `Content-Disposition`, `ETag`, `Last-Modified` and `Cache-Control` are forwarded from your mail server; no other upstream headers are passed through.
Errors
Errors
Status
Code
When
422
invalid_download_path
accountId, blobId or name contains a disallowed character (control characters, \, /, {, }, %, ?, #) or is longer than 1024 characters
422
invalid_download_query
a query key other than accept is present, or accept doesn't look like a media type
502
invalid_mail_session
session/template resolution fails
relayed
(none)
your mail server 404s a missing blob, or answers any other status β relayed as-is
Notes
Read-only; no side effects.
Unlike the JSON reply from POST /mail, a download is not capped at 25 MB β it streams unbounded.
POST/mail/upload/{accountId}
Upload a blob
Uploads a blob (a message to import, a Sieve script, an attachment) to your mail server, returning the blob metadata your next JMAP call references.
Scope: mail:native
Parameters
Upload a blob parameters
Name
Type
Access
Default
Description
accountId*
String
β
β
The account to upload into.
Content-Type*
String (header)
β
β
The blob's media type, e.g. application/sieve or message/rfc822.
(request body)*
binary
β
β
The raw blob bytes, streamed β not multipart/form-data.
more than 12 uploads/60s for your key (a narrower limit than the general one in API keys)
429
upload_busy
another upload for your key is already in flight
413
payload_too_large
the streamed bytes exceed the upload ceiling mid-transfer
503
upload_unavailable
a temporary resource for the upload could not be allocated
400
upload_unavailable
the request body could not be read as a stream
502
invalid_mail_session
session resolution fails
Notes
Size cap: the smallest of your mail server's own configured limit (25 MB by default), the limit advertised in your session's capabilities, and this API's own hard ceiling of 25 MB β this API never accepts more than 25 MB no matter how the mail server is configured.
Only one upload per key is in flight at a time β a second concurrent upload from the same key is rejected with upload_busy even if it targets a different accountId.
GET/mail/events
Event stream (SSE)
Opens a server-sent-events stream of near-real-time change notifications from your mail server, instead of polling.
Scope: mail:native
Parameters
Event stream (SSE) parameters
Name
Type
Access
Default
Description
types
String
β
*
Comma-separated object type names to watch, or * for all.
closeafter
String
β
state
One of state or no β whether the stream closes after sending a state change.
ping
Integer
β
30
Keep-alive interval in seconds, 1β60.
Last-Event-ID
String (header)
β
β
The id of the last event you saw, up to 512 characters, no control characters β send it to resume a stream after a reconnect.
A `text/event-stream` body; frames are relayed from your mail server unchanged.
Errors
Errors
Status
Code
When
422
invalid_event_parameters
types, closeafter or ping fails validation
422
invalid_event_id
Last-Event-ID fails validation
429
rate_limited
more than 10 stream-opens/60s for your key (this bounds how often you reconnect, not how long a stream stays open)
429
event_stream_busy
another stream for your key is already open
502
invalid_mail_session
session resolution fails
Notes
Only one open stream per key at a time.
A stream force-closes after roughly 32 MB of relayed traffic regardless of how much wall-clock time has passed, and also closes immediately if you disconnect. Reconnect with Last-Event-ID when that happens β there is no separate idle timeout beyond it.
Mailboxes & Access
This section covers every mailbox object your API key can reach: the mailbox itself (x:Account), app-specific passwords (x:AppPassword), per-mailbox settings (x:AccountSettings), and a mailbox's own password and two-factor login (x:AccountPassword). All four are called through the mail server passthrough β see Mail server API basics for the request envelope, GET /mail/session, pagination and rate limits, which are not repeated here.
Your key acts as an administrator of your whole organization. For x:Account itself, the JMAP accountId parameter is your own account id (from GET /mail/session) β you are calling the registry, and you can list, create, update or delete any mailbox in your organization by its id. For x:AppPassword, x:AccountSettings and x:AccountPassword, accountId is the target mailbox's own id instead, because those objects belong to one specific mailbox.
domainId values come from your own domain list β see Domains & DNS. A domainId or mailbox id that belongs to another organization is never visible to your key: it is refused or simply behaves as not found, exactly as if it did not exist.
x:Account
Mailboxes
A mailbox on your organization. Every mailbox is an x:Account record with @type "User"; the same object also models internal permission groups (@type "Group"), which this API does not create.
getqueryset: create, update, destroy
Properties (18)
x:Account properties
Name
Type
Access
Default
Description
@type*
String (enum: User | Group)
create-only
β
Discriminates a mailbox (User) from a permission group (Group). This API only ever creates User; Group exists in the schema but is not used here.
name*
String
read-write
β
Local part of the mailbox's address (before the @). Renaming changes the mailbox's login address; the full address is name@domain.
domainId*
Id
create-only
β
One of your organization's domain ids (see Domains & DNS). A domainId belonging to another organization is not visible to your key β see Errors. Moving a mailbox to a different domain after creation was not exercised in our evidence; treat domainId as fixed once the mailbox exists.
emailAddress
String
server-set
β
Computed as name@domain. Present only in responses.
credentials
Object
write-only
β
The mailbox's login password, keyed by index: {"0":{"@type":"Password","secret":"..."}}. Never returned by a get. Rejected inside the SAME call as a create β create the mailbox first, then send a follow-up update patching credentials/0/secret (see "Set or change a mailbox password" below). Must be written as an indexed object, never a JSON array, or the write fails invalidPatch.
createdAt
UTCDateTime
server-set
β
When the mailbox was created.
memberGroupIds
Id[]
read-write
β
Permission groups this mailbox belongs to.
memberTenantId
Id|null
server-set
β
Your organization id. Your key is itself confined to one organization, so omit this on create and update β sending it explicitly is refused with invalidPatch ("Cannot modify memberTenantId").
roles*
Object
read-write
β
{"@type":"User"} for an ordinary mailbox, or {"@type":"Admin"} for a tenant administrator (memberTenantId is filled in server-side). {"@type":"Custom","roleIds":{...}} referencing the global TenantAdministrator role is refused with invalidForeignKey; the older {"@type":"TenantAdmin"} wire value is refused with invalidPatch.
permissions*
Object
read-write
β
{"@type":"Inherit"} gives the mailbox its role's normal permission set β use this for a new mailbox. {"@type":"Merge",...} and {"@type":"Replace",...} narrow or replace the permission set; see "Suspend or unsuspend a mailbox's login" below for a caveat on writing this through an API key.
quotas
Object
read-write
β
{"maxDiskQuota": <bytes>}. This server refuses less than 1073741824 (1 GB) for a mailbox with invalidProperties; omitting quotas defaults to 0 and also fails.
usedDiskQuota
UnsignedInt
server-set
β
Bytes currently used.
aliases
Object
read-write
β
Indexed map of {enabled, name, domainId, description?} β see "Add or remove an alias" below for the exact write shape.
externalId
String|null
read-write
β
Free-form identifier for your own records; not used by Mailbux.
description
String|null
read-write
β
Display name shown in the dashboard and used as the mailbox's label.
locale
String
read-write
en-US
BCP-47 locale.
timeZone
String|null
read-write
β
IANA time zone name, e.g. "Europe/London".
encryptionAtRest*
Object
read-write
β
{"@type":"Disabled"}, or an encrypted variant ({"@type":"Aes128"|"Aes256"|"Aes256Gcm"|"ChaCha20Poly1305", "publicKey": "<Id>", "encryptOnAppend": false, "allowSpamTraining": false}). Every mailbox on this platform is provisioned with Disabled; the encrypted variants were not exercised in our evidence.
Filters (5)
x:Account filters
Name
Type
Access
Default
Description
text
String
β
β
Free-text match; which fields it searches was not independently verified.
name
String
β
β
Exact match on the mailbox's local-part.
domainId
Id
β
β
Restrict to mailboxes on one of your domains.
memberTenantId
Id
β
β
Restrict to one organization. Your key is already confined to your own, so this rarely needs to be set explicitly.
memberGroupIds
Id[]
β
β
Restrict to mailboxes belonging to a given permission group.
x:Account/changes and x:Account/queryChanges are part of the standard JMAP method family but were not confirmed as implemented for this object on this server version; this page does not document them β re-fetch with get/query instead of relying on incremental sync.
Your key already acts as a tenant administrator inside your own organization: pass your own account id (from GET /mail/session) as accountId on every call in this object, and act on any mailbox in your organization by its id, not just one mailbox.
POSTx:Account/query
List / filter mailboxes
Returns matching mailbox ids for your organization, filtered by domain, name or free text.
Parameters
List / filter mailboxes parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
Your own account id, from GET /mail/session.
filter
Object
β
β
Any of: text, name, domainId, memberTenantId, memberGroupIds.
Your key does not have the permission this call requires.
β
accountNotFound
The accountId in the call is not visible to your key.
Notes
Returns ids only; fetch full records with a follow-up x:Account/get, or chain them in the same request with a back-reference ({"resultOf":"0","name":"x:Account/query","path":"/ids"}).
Sort order is not documented for this object and was not independently verified; treat result order as unspecified.
POSTx:Account/get
Get one or more mailboxes
Returns full mailbox records for the given ids, or a page of your whole organization when ids is null.
Parameters
Get one or more mailboxes parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
Your own account id, from GET /mail/session.
ids
Id[]|null
β
β
Mailbox ids to fetch, or null for a capped page (see notes).
Your key does not have the permission this call requires.
β
accountNotFound
The accountId in the call is not visible to your key.
β
requestTooLarge
The ids list (or the unscoped page behind ids:null) exceeds 500 entries. This fails the WHOLE call, not just the excess ids.
Notes
ids:null returns a page capped at 500 records and is not guaranteed to include every mailbox in a larger organization. Scope with x:Account/query (e.g. by domainId) first, then fetch in batches of 500 or fewer ids.
credentials is never included in a get response, whatever properties you request.
A mailbox id belonging to another organization behaves as not found (listed in notFound, not in list).
POSTx:Account/set
Create a mailbox
Creates a mailbox on one of your domains. Send the password in a separate follow-up call (see "Set or change a mailbox password") β this server rejects credentials in the same call as the create.
Parameters
Create a mailbox parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
Your own account id, from GET /mail/session.
create*
Object
β
β
Map of your own creation-id (e.g. "new") to the new mailbox properties.
A required property is missing or malformed. Two verified shapes: properties:["quotas"] when maxDiskQuota is below 1073741824 (1 GB) or omitted; and a weak/too-short secret if you (incorrectly) send credentials at create.
β
invalidForeignKey
domainId does not resolve within your organization β including a domainId that belongs to another organization, which your key cannot see.
β
primaryKeyViolation
The address (name@domain) already exists. The response names the existing mailbox: notCreated.new = {"type":"primaryKeyViolation","objectId":{"object":"Account","id":"<existing id>"}} β look it up with x:Account/get rather than retrying blind.
β
overQuota
Your organization's storage pool is exhausted (also observed live with the raw text diskQuotaAllocationExceeded).
Notes
credentials is rejected in the same call as a tenant-confined key; create the mailbox first, then patch credentials/0/secret in a follow-up x:Account/set update.
A mailbox quota under 1 GB is refused outright β there is no free-tier "smaller mailbox" option at the JMAP layer.
The created response above is intentionally narrow (JMAP only echoes server-set properties by default); re-read with x:Account/get if you need the full record back.
POSTx:Account/set
Update a mailbox's quota, display name, locale or time zone
Patches one or more writable properties on an existing mailbox. Only send the properties you want to change.
Parameters
Update a mailbox's quota, display name, locale or time zone parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
Your own account id, from GET /mail/session.
update*
Object
β
β
Map of mailbox id to the properties to patch.
ifInState
String
β
β
Optional CAS token from a prior get/set state; see notes.
The mailbox id does not exist, or belongs to another organization (both look identical to your key).
β
invalidProperties
A new value fails validation, e.g. quotas.maxDiskQuota below 1073741824 (1 GB).
β
stateMismatch
ifInState was supplied and no longer matches the mailbox's current state β see notes.
Notes
This server commonly omits the collection state on x:Account get responses. If your prior get did not return a usable state, drop ifInState rather than sending an empty string β an empty value is rejected outright, not treated as "any state".
domainId and @type are not included here β see the property table above for their create-time-only status.
POSTx:Account/set
Set or change a mailbox password
Sets a mailbox's login password as its administrator β no proof of the old password is needed. This is the follow-up call after creating a mailbox, and the normal way to reset a password on this platform.
Parameters
Set or change a mailbox password parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
Your own account id, from GET /mail/session.
update*
Object
β
β
Map of mailbox id to {"credentials/0/secret": "<new password>"}.
The mailbox id does not exist, or belongs to another organization.
β
invalidProperties
The new password fails the server's strength check. Verified live shape: invalidProperties whose message names "secret".
Notes
Do not use x:AccountPassword for this (see that object below) β this product's own admin-reset path is x:Account/set, not x:AccountPassword/set.
POSTx:Account/set
Add or remove an alias
Aliases live on the mailbox's own aliases property as an indexed map β there is no separate alias object. Read the current list first, then write the WHOLE list back with your change.
Parameters
Add or remove an alias parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
Your own account id, from GET /mail/session.
update*
Object
β
β
Map of mailbox id to {"aliases": {"0": {...}, "1": {...}, ...}}.
The mailbox id does not exist, or belongs to another organization.
β
invalidPatch
aliases was sent as a JSON array instead of an indexed object ({"0": {...}} rather than [{...}]).
β
invalidProperties
An alias entry is missing name or domainId, or domainId is not one of your domains.
β
stateMismatch
ifInState no longer matches β see notes.
Notes
1) Read the current list first: x:Account/get with properties:["aliases"] (see "Get one or more mailboxes" above).
2) Write the FULL list back with your addition appended at the next free index β a partial patch is not supported; aliases is replaced wholesale, not merged.
3) To remove an alias, write the list back without that entry, renumbered from "0".
Pass the state you read in step 1 as ifInState to refuse the write if someone else changed the aliases in between β but this server commonly omits state on x:Account get responses, so ifInState is often unavailable in practice; omit it rather than send an empty string.
POSTx:Account/set
Suspend or unsuspend a mailbox's login
There is no separate "enabled" flag β login is suspended by replacing the mailbox's permission set with an empty one, and restored by reverting to Inherit.
Verified live for a different call with the same shape (mailbox creation with permissions:Replace): a machine identity in "Merge" permission mode was refused granting/revoking permissions it does not itself hold, with a message listing every withheld permission.
β
notFound
The mailbox id does not exist, or belongs to another organization.
Notes
To unsuspend, set permissions back to {"@type":"Inherit"}.
Caveat: whether an API key's own principal can successfully write permissions:Replace against another mailbox (as opposed to a human tenant-admin dashboard session, which is what our verified evidence covers) was not independently confirmed. Treat this operation as unconfirmed for API-key callers and test it against a disposable mailbox before depending on it β if refused, you will see the forbidden error above.
Your key does not have mailbox-destroy permission.
β
notFound
The mailbox id does not exist, was already deleted, or belongs to another organization.
Notes
Destroying a mailbox is not observed anywhere in our evidence with respect to its own aliases, app passwords or API keys β we do not assert whether they are cleaned up automatically or become orphaned. Confirm in a disposable mailbox before relying on either behavior.
A blind retry against an id already destroyed returns notFound rather than succeeding a second time.
x:AppPassword
App passwords
A mailbox-scoped secondary credential for IMAP/POP3/ManageSieve clients that need a password instead of your API key. Created under a specific mailbox's own accountId, not your own.
getqueryset: create, destroy
Properties (6)
x:AppPassword properties
Name
Type
Access
Default
Description
description*
String
create-only
β
A label for the app password, e.g. "iPhone Mail". There is no update method for this object (see notes) β the label cannot be renamed after creation.
secret
String
server-set
β
Server-generated; a client-supplied secret is rejected. Returned once, in the create response, and never again β store it immediately or the app password must be destroyed and recreated.
createdAt
UTCDateTime
server-set
β
When the app password was created.
expiresAt
UTCDateTime|null
create-only
β
Optional expiry. Omit for an app password that never expires.
permissions*
Object
create-only
β
Must be exactly {"@type":"Inherit"}. A nested Replace permission list ({"@type":"Replace","permissions":[...]}) is refused live with invalidPatch naming "permissions/permissions" β do not send enabledPermissions/disabledPermissions/permissions here.
allowedIps
Object
create-only
β
CIDR ranges this app password may be used from; empty (unrestricted) by default. The mail platform's own reference and its source disagree on the exact wire shape for a non-empty value (a Set vs. a Map) β this was not independently verified live, so leave it empty unless you confirm the shape yourself.
Filters (1)
x:AppPassword filters
Name
Type
Access
Default
Description
expiresAt
UTCDateTime
β
β
Filter app passwords by their expiry date.
This object has no update method β the mail platform's own permission reference lists Get, Create, Destroy and Query only for app passwords (an earlier internal draft of that reference listed an Update permission too; live behavior and this product's own code both treat app passwords as create-or-destroy only, so that draft entry is treated as stale).
App passwords are not plan-gated β a Free-plan mailbox can have them too. What is plan-gated is API access itself: a Free plan has no API key, so none of the operations on this page can be reached through the API on Free (see API keys). A downgrade to Free also revokes every existing API key and turns off IMAP/POP3 sign-in on the mailbox (SMTP stays on), so an app password created earlier stops working for IMAP/POP3 sign-in even though it is not deleted.
POSTx:AppPassword/get
List app passwords for a mailbox
Lists the app passwords created under one mailbox. Never includes the secret β that's returned once, at create.
Parameters
List app passwords for a mailbox parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
The mailbox's own account id (not your own).
ids
Id[]|null
β
β
Specific app password ids, or null for all of this mailbox's app passwords.
Your key does not have app-password-create permission.
β
accountNotFound
The mailbox's accountId does not exist, or belongs to another organization.
β
invalidPatch
permissions was sent as anything other than {"@type":"Inherit"} β a nested Replace list is refused, naming "permissions/permissions".
Notes
secret is shown exactly once, in this response. There is no way to retrieve it again β destroy and recreate if it is lost.
On a Free-plan service there is no API key at all, so this call cannot be reached through the API (see API keys) β that is a limit on API access, not on app passwords themselves. Even where an app password exists on a Free-plan mailbox, it cannot sign in over IMAP/POP3, because those protocols are off on Free (SMTP stays on).
POSTx:AppPassword/set
Revoke an app password
Permanently revokes one app password. There is no update method for this object β to change a label or permission, destroy and recreate.
Your key does not have app-password-destroy permission.
β
notFound
The app password id does not exist, was already revoked, or belongs to a mailbox outside your organization.
x:AccountSettings
Mailbox settings
A singleton (one per mailbox, id "singleton") carrying description, locale, time zone and encryption-at-rest. See the overlap caveat in this object's operations before using it.
getset: update
Properties (4)
x:AccountSettings properties
Name
Type
Access
Default
Description
description
String|null
read-write
β
Display name for the mailbox.
locale
String
read-write
en-US
BCP-47 locale.
timeZone
String|null
read-write
β
IANA time zone name.
encryptionAtRest*
Object
read-write
β
Same shape as x:Account's encryptionAtRest: {"@type":"Disabled"} or an encrypted variant. See the caveat in Notes below before writing this here instead of on x:Account.
No filters or sort: always addressed directly by accountId + id "singleton", never listed or queried.
POSTx:AccountSettings/get
Get a mailbox's settings
Reads the singleton settings record for one mailbox. The id is always the fixed string "singleton".
Your key does not have permission to update this mailbox's settings.
β
invalidProperties
A new value fails validation.
β
singleton
An id other than "singleton" was used, or a create/destroy was attempted against this object. The mail platform's own docs state create and destroy are rejected for singleton objects; the exact error type string returned live was not independently captured, so it is not asserted here.
Notes
This object is not exercised anywhere in this product's own code β description, locale, timeZone and encryptionAtRest are also plain, directly writable x:Account properties (see the Mailboxes object above), and this product writes them there instead. Whether writing the same field through both x:AccountSettings and x:Account stays consistent on this server version was not independently verified. Prefer x:Account/set for these fields unless you have a specific reason to use this object, and avoid writing the same field through both paths.
x:AccountPassword
Mailbox password & two-factor login
A singleton (one per mailbox, id "singleton") for a mailbox's own login password and TOTP two-factor login.
getset: update
Properties (3)
x:AccountPassword properties
Name
Type
Access
Default
Description
secret
String
write-only
β
The new password. Never returned by a get.
currentSecret
String
write-only
β
Proves the mailbox's existing password before changing it or enrolling two-factor login. Never returned by a get.
otpAuth
Object
write-only
β
{"otpUrl": "otpauth://...", "otpCode": "123456"} to enable two-factor (TOTP) login, or {"otpUrl": null} to disable it. otpUrl is a standard otpauth:// URL your own client generates; otpCode is the current code from an authenticator app for that URL. See Notes on this object for an unresolved requirement in the platform's own documentation.
No filters or sort: always addressed directly by accountId + id "singleton".
This product's own admin-side password reset uses x:Account/set (credentials/0/secret) instead of this object, precisely because otpAuth's requirements are geared to a self-service, OTP-proven change β an administrator resetting a mailbox they do not personally hold the authenticator for cannot supply a fresh otpCode. Use x:Account for an administrator-initiated reset; use this object for a mailbox's own owner changing their own password or their own two-factor login.
POSTx:AccountPassword/get
Get a mailbox's password/2FA record
Reads the singleton credential-reset record for one mailbox. secret, currentSecret and otpAuth are write-only and are not returned.
Your key does not have permission to read this mailbox's credential record.
β
accountNotFound
The mailbox's accountId does not exist, or belongs to another organization.
Notes
Useful mainly to confirm the record exists and to read its state before an update; the credential fields themselves never come back.
POSTx:AccountPassword/set
Change a mailbox's own password (self-service shape)
Changes a mailbox's password by proving the current one. This is the self-service shape (used by the mailbox owner); as an organization administrator, x:Account/set (see "Set or change a mailbox password" above) is this product's own verified path and does not require the old password.
Parameters
Change a mailbox's own password (self-service shape) parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
The mailbox's own account id.
update*
Object
β
β
Keyed by "singleton", with currentSecret and secret.
Your key does not have permission to update this mailbox's credentials.
β
invalidProperties
currentSecret does not match the mailbox's current password, or the new secret fails the strength check.
β
singleton
An id other than "singleton" was used, or create/destroy was attempted. Exact error type string not independently captured β see the same caveat on x:AccountSettings.
Notes
The mail platform's own reference describes otpCode as "required for credential changes" on this object, which would mean every update β including a plain password change β must include otpAuth.otpCode. The example above, with only currentSecret and secret and no otpCode, is this platform's own carried-over, previously verified working shape. This discrepancy was not re-resolved live: if a plain password change is refused without otpCode, that confirms the stricter reading; otherwise the example above holds.
POSTx:AccountPassword/set
Enable two-factor (TOTP) login
Turns on TOTP login for a mailbox. otpUrl is a standard otpauth:// URL your own client generates; otpCode is the current code it produces.
Parameters
Enable two-factor (TOTP) login parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
The mailbox's own account id.
update*
Object
β
β
Keyed by "singleton", with currentSecret and otpAuth.
Your key does not have permission to update this mailbox's credentials.
β
invalidProperties
The record has no two-factor login enabled to disable.
Domains & DNS
Domains, DKIM signing keys and distribution lists are all managed the same way as mailboxes: standard JMAP method calls through POST /mail, using your one API key. There are no separate REST endpoints for creating, changing or deleting any of them.
A domain (x:Domain) is the unit everything else attaches to β mailboxes, DKIM signatures, its catch-all address, sub-addressing, and mailing lists all reference a domainId. Add a domain here before you create mailboxes or lists on it.
x:DkimSignature manages the signing keys that authenticate your outgoing mail. x:MailingList manages distribution lists: one address that fans out to a fixed set of recipients.
DNS records
This API exposes one piece of DNS material directly: a DKIM signature's publicKey, which you publish in a selector._domainkey.<domain> TXT record (see x:DkimSignature below).
The rest of what a domain needs β MX, SPF, DMARC and autoconfig records β and the live per-record verification state are not exposed as a field on x:Domain or any other object in this build. Use your dashboard's DNS view for the full record checklist and its live verification status.
x:Domain
Domains
A domain (e.g. example.com) that receives mail on your organization. Owns DKIM signatures, its catch-all address, sub-addressing, and its certificate/DKIM/DNS management mode.
Server-assigned object id. Use it as domainId when referencing this domain from x:DkimSignature, x:MailingList, mailboxes and forwarders.
name*
DomainName
read-write
β
The domain name, e.g. example.com.
aliases
DomainName[]
read-write
β
Additional domain names that alias this domain.
isEnabled
Boolean
read-write
true
Whether the domain is active.
createdAt
UTCDateTime
server-set
β
When the domain was created.
description
String|null
read-write
β
Optional free-text note.
logo
String|null
read-write
β
Optional logo reference.
certificateManagement*
"Manual" | "Automatic"
read-write
β
How TLS certificates for this domain are managed. The schema marks this required on create β see the create operation's notes.
dkimManagement*
"Automatic" | "Manual"
read-write
β
How DKIM signing is managed for this domain.
dnsManagement*
"Manual" | "Automatic"
read-write
β
How DNS records for this domain are managed.
memberTenantId
Id|null
server-set
β
Enterprise sub-tenant scoping. A confined key (your API key) never needs to send this and cannot change it once set.
directoryId
Id|null
server-set
β
Directory binding. Not used on this platform β always null.
catchAllAddress
EmailAddress|null
read-write
β
A single catch-all recipient for mail sent to any address at this domain that does not otherwise match a mailbox or alias. For more than one destination, use a forwarder instead (see Forwarders).
subAddressing*
"Enabled" | "Custom" | "Disabled"
read-write
β
Plus-addressing mode (user+tag@domain).
allowRelaying
Boolean
read-write
false
Whether the domain may relay outbound mail beyond normal sending rules.
Enterprise sub-tenant scoping; a confined key does not need this.
Replaces the deprecated GET /domains and GET /dns summaries (see Deprecated).
A domain id that belongs to another organization behaves as not found, the same as an id that does not exist.
Only the DKIM public key is exposed as DNS material through this API (see x:DkimSignature below) β see "DNS records" above.
Enforcement of your plan's domain limit for a create call made directly against this API (outside the dashboard) is not confirmed; treat your plan's domain limit as a hard ceiling regardless.
POSTx:Domain/get
Get domains
Fetch specific domains by id, or every domain in your organization when you omit ids.
Scope: mail:native
Parameters
Get domains parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
Your management account id, from GET /mail/session.
ids
Id[]|null
β
β
Domain ids to fetch. Omit or send null to fetch every domain in your organization.
properties
String[]
β
β
Property names to return. Omit to return every property.
name is missing or not a valid domain, or the domain is already registered β the response properties array names the rejected field. The exact wording for an already-registered domain is not confirmed for this build; expect invalidProperties either way.
Notes
certificateManagement, dkimManagement, dnsManagement and subAddressing are all required by the schema. Send "Manual" for the first three and "Enabled" for subAddressing unless you have a specific reason to choose otherwise β that is what our own provisioning sends.
The response id (here "d1") is the domainId every other call β mailboxes, x:DkimSignature, x:MailingList, forwarders β references.
We could not confirm whether a create call is refused once your plan's domain limit is reached when called directly against this API (our dashboard enforces the limit before it gets this far). Stay within your plan's limit regardless.
POSTx:Domain/set
Update a domain
Change a domain's tenant-writable settings β catch-all address, DKIM/certificate/DNS management mode, sub-addressing, relaying, report address, description, aliases and enabled state.
Scope: mail:native
Parameters
Update a domain parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
Your management account id.
update*
Object
β
β
A map of domainId to the properties to change.
ifInState
String
β
β
Pass the state you last read to reject the update if the domain changed since (concurrency guard).
domainId does not exist, or belongs to another organization.
β
invalidProperties
a value fails validation, e.g. catchAllAddress is not one of your own addresses.
β
invalidPatch
you try to change memberTenantId β it is not patchable.
β
stateMismatch
ifInState was sent and no longer matches the domain's current state.
Notes
catchAllAddress accepts one address. For more than one destination, use a forwarder instead (see Forwarders).
The same call updates any other tenant-writable property from the table above β dkimManagement, certificateManagement, dnsManagement, subAddressing, isEnabled, description, logo, aliases, allowRelaying, reportAddressUri β send only the fields you want to change.
aliases is a plain array: send the whole replacement list, not a single entry to add.
A successful update returns null for that id in updated; re-fetch with get if you need the new values back.
your key does not carry domain-destroy permission.
β
notFound
domainId does not exist, or belongs to another organization β nothing is deleted.
Notes
A blind destroy of an id that does not exist returns notFound and touches nothing else in the same call.
We could not verify what happens to mailboxes still on the domain when you delete it directly through this API β remove or move them first rather than relying on a specific cascade or refusal behavior.
Deleting a domain also removes its DKIM signatures; delete signatures explicitly first if you want to confirm that step yourself.
x:DkimSignature
DKIM signatures
A DKIM signing key bound to one domain. Supports rotating in a new key before retiring the old one.
accountId is missing, malformed, or not visible to your key.
Notes
publicKey is what you publish in the selector._domainkey.<domain> DNS TXT record. privateKey is never returned by get β it is write-only, set on create.
Ids that do not exist, or belong to a domain outside your organization, come back in notFound.
POSTx:DkimSignature/query
Query DKIM signatures for a domain
List the DKIM signatures on one domain. domainId is required.
Scope: mail:native
Parameters
Query DKIM signatures for a domain parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
Your management account id.
filter*
Object
β
β
Must include domainId; memberTenantId is also accepted.
@type, domainId, selector or privateKey is missing or malformed, or domainId is not one of your own domains.
Notes
privateKey is a PEM key you generate; the server derives and returns publicKey. Once written, privateKey itself is never returned again by get or query.
To rotate: create the replacement with a new selector (this call), publish its publicKey in DNS, then once you have confirmed the new key is live, move the old signature's stage to "retiring" or destroy it outright β see the notes on this object.
POSTx:DkimSignature/set
Update a DKIM signature's rotation stage
Move a signature through its rotation lifecycle, or change its DKIM1 options (canonicalization, headers, report, expire, auid).
Scope: mail:native
Parameters
Update a DKIM signature's rotation stage parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
Your management account id.
update*
Object
β
β
A map of signature id to the properties to change.
the signature id does not exist, or belongs to a domain outside your organization.
β
invalidProperties
stage or another property is not a recognized value.
Notes
stage moves through active -> pending -> retiring -> retired. nextTransitionAt is set by the server when it is managing a scheduled transition; it is not something you write.
@type, domainId and privateKey are not meant to change on an existing signature β create a new one instead.
the signature id does not exist, or belongs to a domain outside your organization.
Notes
Destroying your only active signature stops DKIM signing for that domain immediately β publish and confirm a replacement first if you want continuous coverage.
x:MailingList
Distribution lists
One address that fans out to a fixed set of recipients on one of your domains.
Local part of the list address. Must match ^[a-z0-9._-]+$, max 64 characters (see notes).
domainId*
Id
read-write
β
The domain this list's address belongs to.
emailAddress
EmailAddress
server-set
β
The full list address: name@ the domain. Derived, not sent by you.
description
String|null
read-write
β
Optional free-text note.
aliases
Object
read-write
β
Additional addresses for this list, as an indexed object (same shape as a mailbox's aliases).
recipients
Object
read-write
β
The list's members, as a map keyed by address, e.g. {"[email protected]": true}. 1 to 100 addresses (see notes).
memberTenantId
Id|null
server-set
β
Enterprise sub-tenant scoping. A confined key never needs to send this.
Filters (2)
x:MailingList filters
Name
Type
Access
Default
Description
text
String
β
β
Substring match against the list's name/address.
memberTenantId
Id|null
β
β
Enterprise sub-tenant scoping.
name must match ^[a-z0-9._-]+$ and be at most 64 characters; recipients must contain 1 to 100 valid addresses. Our dashboard enforces both before it calls the API; whether this build's mail server also enforces them on a direct call is not confirmed, so keep to these limits regardless.
A mailing list id from another organization behaves as not found.
POSTx:MailingList/get
Get mailing lists
Fetch specific distribution lists by id, or every list in your organization when you omit ids.
your key does not carry mailing-list-create permission.
β
invalidProperties
name is missing, does not match ^[a-z0-9._-]+$, or is over 64 characters; domainId is missing or not one of your own domains; or recipients has zero or more than 100 addresses, or contains an invalid address.
Notes
name is the local part only β the full address (emailAddress) is name@ the domain and is returned by the server, not sent by you.
recipients is a map keyed by address, e.g. {"[email protected]": true} β not an array.
POSTx:MailingList/set
Update a mailing list's members
Replace a list's recipients (or rename it / move it to another domain).
Scope: mail:native
Parameters
Update a mailing list's members parameters
Name
Type
Access
Default
Description
accountId*
Id
β
β
Your management account id.
update*
Object
β
β
A map of list id to the properties to change.
ifInState
String
β
β
Pass the state you last read to reject the update if the list changed since.
your key does not carry mailing-list-update permission.
β
notFound
the list id does not exist, or belongs to another organization.
β
invalidProperties
the new name, domainId or recipients fail the same checks as create.
β
invalidPatch
recipients or aliases is sent as a JSON array instead of a map/indexed object.
β
stateMismatch
ifInState was sent and no longer matches the list's current state.
Notes
recipients is replaced whole on every update: read the current list with get first, then send the complete set back with your change (add, remove, or edit) applied β sending only the new address drops everyone else.
The verified update shape resends name and domainId alongside recipients, as shown above, even when only membership changed.
your key does not carry mailing-list-destroy permission.
β
notFound
the list id does not exist, or belongs to another organization.
β
stateMismatch
ifInState was sent and no longer matches the list's current state.
Notes
Deleting a list frees its address; recipients themselves are untouched.
API keys
A service on a paid or LTD plan can hold API keys; a Free-plan service has none, and downgrading to Free immediately revokes every key it had. There is no separate REST endpoint for minting a key β that happens in your dashboard β but the key itself is also a JMAP object your own key can list, and additional per-mailbox keys can be created, through POST /mail.
Your key is a single secret that does two jobs at once: it is the bearer token for every REST endpoint on this site (Forwarders, and this page itself), and it is your own native credential on the mail server for everything under Mail server API basics and Mail data (JMAP). There is nothing else to configure.
One key, two surfaces
Send it the same way everywhere: Authorization: Bearer YOUR_API_KEY.
A key is either the newer native format (a string of 24 or more printable characters, no fixed prefix) or the older proxy-only format (mbx_ followed by an 18-character id, an underscore, and a 48-character secret) for keys issued before native issuance became the default. Both formats authenticate the REST endpoints on this site; the native format additionally authenticates every /mail* call directly against the mail server, because it is a real credential there, not just a token this API recognizes. If you were issued a key some time ago and calls to POST /mail return unauthorized while your REST calls (like Forwarders) still work, your key predates native issuance β reissue it from the dashboard to get one that covers both surfaces.
Create and revoke a key
Keys are managed from your dashboard: open the service, then Manage β API. Creating a key asks for a label and issues a fresh secret immediately β it is shown once, at creation time; the dashboard cannot show it to you again, so store it before you navigate away.
Suspending a key stops it working right away without deleting it. Re-enabling a suspended key issues a brand-new secret β the old one is gone permanently, so treat re-enabling the same as issuing a new key, not as "resuming" the old one. Revoking a key is final: a revoked key cannot be re-enabled or recovered; create a new one instead.
A key can only be created or revoked from the dashboard, by a human admin on the account β a key cannot revoke or replace itself, and cannot mint another key of the same kind through this API (see the notes on x:ApiKey/set below). Additional, narrower-scoped keys for individual mailboxes are the one exception: those can be created through x:ApiKey/set itself.
Rate limits
Every request against this tenant API β REST endpoints and POST /mail alike β is limited to 60 requests per 60 seconds, bucketed per key and per source IP together: the same key from a different IP gets a fresh bucket, and a different key from the same IP gets its own bucket too. This is the general limit; POST /mail/upload/{accountId} and GET /mail/events carry their own additional, narrower limits described in Mail server API basics.
Going over the limit returns:
HTTP/1.1 429 Too Many Requests
{"error":{"code":"rate_limited","message":"Too many API requests. Try again shortly."}}
x:ApiKey
Additional API keys
The API key that authenticates your own calls is itself an x:ApiKey record β this object is how you can list it, and how you can issue narrower, per-mailbox keys through the API instead of the dashboard.
A label you choose to tell keys apart. This API's own dashboard-issued keys are labeled with a recognizable prefix; yours can be anything.
secret
String
server-set
β
The key value. Always server-generated β a client-supplied secret is rejected. Returned once, in the create response, and never again.
createdAt
UTCDateTime
server-set
β
When the key was created.
expiresAt
UTCDateTime|null
read-write
β
Optional expiry. Omit or set null for a key that does not expire.
permissions*
Object
read-write
β
The key's permission set. Send exactly {"@type": "Inherit"} to match the account's own permissions β a nested permission list ({"@type": "Replace", "permissions": [...]}) at create time is rejected with invalidPatch.
allowedIps
Set<IpMask>
read-write
β
CIDR ranges this key may be used from. Empty (unrestricted) by default.
Filters (1)
x:ApiKey filters
Name
Type
Access
Default
Description
expiresAt
UTCDateTime
β
β
Filter by expiry date.
The examples below send using: ["urn:ietf:params:jmap:core"] only β this API adds the serverβs management capability for you automatically (see Capability URNs in Mail server API basics), so you do not need to list it yourself.
This key cannot create further keys of its own kind through this API β that is a product-level restriction of this surface, not a hard block on the object itself, so use the dashboard to issue your primary key(s) instead.
Mailbox-scoped keys created this way are refused only on the Free plan; paid and LTD plans can create them.
A rejected create looks like "notCreated": {"new": {"type": "invalidProperties", "properties": ["permissions"]}}; a rejected destroy looks like "notDestroyed": {"<id>": {"type": "notFound"}}.
POSTx:ApiKey/get
Get an API key by id
Fetches one or more keys by id, or every key on the account when ids is null.
the id does not exist β this includes any id from another organization, which behaves as not found
Notes
Destroying the key you are currently using ends your own session immediately β the response itself still returns normally.
Forwarders
Forwarders are mail-routing rules for this service: copy or redirect mail arriving at one address to one or more other addresses. They are not part of the JMAP mail-server passthrough β your mail key is deliberately never allowed to write them directly, so they have their own REST endpoints here instead.
How forwarders work
A forwarder only takes effect for mail the mail server actually accepts at the source address. If the source has no mailbox and no alias behind it, mail never reaches the rule that would forward it β creating or updating a forward_only forwarder whose source cannot be routed this way is rejected (see the errors on Create and Update below).
Every service has one reserved system catch-all forwarder, whose source local part is _catchall. It is included when you list forwarders, but it cannot be created, changed, or deleted through this API β any write that targets it, or would move another forwarder onto that address, returns 403 system_forwarder_locked.
Writes made with a reseller sub-tenant's key are applied asynchronously: create and update return 202 instead of 201/200, and delete returns 202 instead of 204. A direct service key always gets the synchronous result.
Every call below can also fail with the shared authentication, scope and rate-limit errors listed in the Overview (401, 403, 429, 503) β they are not repeated on each operation here.
GET/forwarders
List forwarders
Lists every forwarder configured for this service.
The forwarder id does not exist, or belongs to a different service.
Notes
Mirrors the row shape from List forwarders. A cross-service id 404s, the same as Update and Delete below.
POST/forwarders
Create a forwarder
Creates a forwarder for this service. The source address's domain must already belong to this service.
Scope: forwarders:write
Parameters
Create a forwarder parameters
Name
Type
Access
Default
Description
source*
String
β
β
The full source address, normalized to lowercase and trimmed before validation. The local part "_catchall" is always rejected β it is reserved for the system catch-all forwarder.
destinations*
String[]
β
β
1 to 20 destination addresses.
mode*
String
β
β
"copy" (deliver locally and forward) or "forward_only".
source, destinations, mode or comment fails validation.
403
system_forwarder_locked
source's local part is "_catchall".
422
domain_not_owned
source's domain does not belong to this service.
422
validation_failed
a forward_only source has no mailbox, alias, or catch-all behind it to route through.
409
forwarder_conflict
the mail server reports a conflict for this write.
502
upstream_error
any other failure completing the write on the mail server.
Notes
Direct service keys get 201 on success; a reseller sub-tenant's key gets 202 instead, because the write is applied asynchronously.
status starts as sync_pending and becomes active once the mail server has applied the change.
A duplicate create for the same source address is not explicitly deduplicated on our side β the mail server's own uniqueness rules decide the outcome.
PUT/forwarders/{forwarder}
Update a forwarder
Replaces an existing forwarder belonging to this service. There is no partial update and no upsert.
the forwarder id does not exist, or belongs to a different service.
403
system_forwarder_locked
the row is the system catch-all forwarder.
502
upstream_error
the mail server fails to complete the deletion.
Notes
Success is 204 No Content for a direct service key, or 202 for a reseller sub-tenant's key (applied asynchronously).
Deleting the last remaining forwarder on a service is allowed; List forwarders then simply returns [].
Deleting an already-deleted id 404s β there is no undo.
Mail data (JMAP)
Called with your bare API key β not as a specific mailbox β every object on this page is scoped to your key's own JMAP account: an empty administrative mailbox, not any real mailbox in your organization. To read or send a specific mailbox's mail, authenticate as that mailbox instead β see Getting started β 5. Read a mailbox's mail.
This is the standard, open JMAP data surface for one mailbox β messages, folders, sending, filtering, quota β reached with that mailbox's own account id, as opposed to Mailboxes & access, Domains & DNS and API keys, which manage your tenant's mailboxes, domains and credentials with your tenant-admin account id. Every object below uses its plain JMAP name (Email, Mailbox, β¦), never an x:-prefixed one.
All of it goes through the same POST /mail endpoint described in Mail server API basics β read that section first for the request envelope, capabilities, pagination and error shapes; this section only adds the object-specific detail.
Which accountId to use
GET /mail/session returns an accounts object keyed by account id, and a primaryAccounts object mapping each capability URN to your default account for it β for example, primaryAccounts["urn:ietf:params:jmap:mail"] is the account id to use for Mailbox/Email/Thread calls. If your service has more than one mailbox, each mailbox is its own account with its own id; look it up in accounts (or in x:Account/query, described in Mailboxes & access) rather than assuming the primary account is the one you want.
This is a different id space from the tenant-admin account id used for x:Account, x:Domain and the other management objects elsewhere on this site β a mailbox's own accountId only ever grants access to that one mailbox's own data, never another mailbox's, even within the same organization.
WebDAV, CalDAV and CardDAV
Calendar and contact sync over WebDAV/CalDAV/CardDAV (PROPFIND, REPORT, PUT and similar verbs against /dav/... paths) is a separate HTTP protocol, not JMAP, and this API does not proxy it β POST /mail only carries JMAP method calls. If your integration needs CalDAV/CardDAV specifically, it is not available through this API today.
Mailbox
Mailboxes (folders)
The folders inside one mailbox β Inbox, Sent, Drafts, Trash, and any custom folders. Not to be confused with x:Account in Mailboxes & access, which is the mailbox account itself.
Whether this script is the one currently applied to incoming mail. Only one script can be active at a time.
blobId*
Id
create-only
β
The uploaded script body's blob id (see Upload a blob in Mail server API basics) β upload the Sieve source first, then reference it here.
Filters (1)
SieveUserScript filters
Name
Type
Access
Default
Description
name
String
β
β
Filter by script name.
This is the per-account, no-x:-prefix form of the object (SieveUserScript/get, not x:SieveUserScript/get) β the name is ambiguous in the mail server's own documentation, which also uses it for a separate, operator-only registry listing every account's scripts. Your key only ever reaches the per-account form shown here, scoped to your own mailbox.
If your mail server edition does not implement this method at all, calls fail with unknownMethod rather than an empty list.
POSTSieveUserScript/get
List your filter scripts
Returns every script on this mailbox, including which one (if any) is active.
blobId does not point at a valid uploaded script, or name is missing
β
notFound
destroy targeted an id that does not exist
Notes
Validate a script before activating it by uploading it and calling SieveScript/validate with the blob id, if you want to catch syntax errors before isActive: true.
Quota
Storage and usage
Your mailbox's storage usage and limit, read the standard JMAP way.
Metadata calls about a blob (attachment, exported message, uploaded file) you already have the id for β the actual bytes move through the upload/download endpoints in Mail server API basics, not these methods.
getcopylookup
urn:ietf:params:jmap:core alone covers this object β no separate capability is needed.
POSTBlob/get
Check a blob before using it
Confirms a blob id is valid and reports its size before you build a download link for it.
the blob id does not exist, or was never visible to this account
PushSubscription
Push notifications
Registers an endpoint to receive near-real-time change notifications, for native mail apps rather than the server-sent-events stream in Mail server API basics.
getset: create, update, destroy
Properties (6)
PushSubscription properties
Name
Type
Access
Default
Description
id
Id
server-set
β
The subscription id.
deviceClientId*
String
create-only
β
An id you choose to recognize this device/registration later.
url*
String
create-only
β
Your push endpoint URL.
keys
Object
create-only
β
Encryption keys for the push payload (Web Push p256dh/auth), if your endpoint needs them.
expires
UTCDateTime|null
read-write
β
When the subscription expires.
types
String[]|null
read-write
β
Object type names to be notified about, or null for all types.
This object is not scoped to one mailbox's accountId β a single subscription can watch every account your key can see.
POSTPushSubscription/set
Register a push endpoint
No accountId argument β subscriptions apply across every account your key can see.
Not available to any mailbox on any plan β this capability is withheld across the board, not specifically restricted for tenant API keys. Listed here for completeness because it is part of the JMAP data surface; calling it returns forbidden.
ShareNotification
Share notifications
Notifications about calendars/contacts/files another user has shared with you.
getquerychangesqueryChangesset
Whether this is enabled on your plan or edition was not established β it is not documented as available. If your integration needs it, confirm current availability with support before relying on it.
Calendar / CalendarEvent
Calendars
JSCalendar-style calendars and events, alongside CalendarEventNotification and ParticipantIdentity for scheduling.
Whether calendars are enabled on your plan or edition was not established during verification β not documented as available here. Check your own session's capabilities for urn:ietf:params:jmap:calendars; if it is present, standard JSCalendar semantics apply.
Whether contacts are enabled on your plan or edition was not established during verification β not documented as available here. Check your own session's capabilities for urn:ietf:params:jmap:contacts before relying on it.
FileNode
Files
A file-storage/WebDAV-adjacent object for storing arbitrary files against your account, separate from mail.
getchangesqueryqueryChangessetcopy
Whether file storage is enabled on your plan or edition was not established during verification β not documented as available here.
Not available to API keys
Your API key is confined to your own organization. Everything on this page is what your key CAN reach; everything below is server-wide or system configuration on the mail server itself β never scoped to a single tenant β and no organization key, however it is scoped, can reach it.
What a call returns
A JMAP method call naming one of these objects returns the standard JMAP method-level error "forbidden" β the same error you would get for any permission you don't hold. There is no special error for "this object doesn't exist for you". Observed live for calls including x:Tenant/get made through a tenant-confined key:
{
"type": "forbidden",
"description": "You are not authorized to perform this action"
}
System-only object types
These 108 object types exist in the mail server's own management registry but are never available to an organization key, grouped the way the registry itself groups them:
x:SieveUserScript in the table above is the system-registry form: any account's scripts, browsable only with admin permissions. A separate, per-account form with no "x:" prefix is reachable with your own key for your own mailboxes β see Mail data (JMAP).
Also not available
Delivery-queue state, message tracing, archived-message access, and DMARC/TLS aggregate or failure reports are not reachable through any key on this API β reading them needs the elevated credential our own support team uses internally, not a customer key.
Reseller and brand operations, and access to any service other than the one your key was issued for, are out of scope for this API entirely β see the reseller API if that applies to your account.
Deprecated endpoints
These 8 read endpoints are being retired: everything they returned is available another way β through the mail-server passthrough, through forwarders, or only in your dashboard. Existing integrations keep working until the sunset date below; keys created after this change never receive their scopes.
Each of these can also fail with any of the shared errors listed in the Overview (401, 403, 429, 503) β they are not repeated below.
Headers before the sunset, and what happens after
Every response from one of these 8 endpoints carries three headers until the sunset: Deprecation (an HTTP-date timestamp for when it was marked deprecated), Sunset (2026-10-17), and a Link header pointing at more information.
On or after 2026-10-17, none of these 8 endpoints work at all. Every call gets:
HTTP/1.1 410 Gone
{
"error": {
"code": "endpoint_retired",
"message": "This endpoint was retired on 2026-10-17. Use POST /mail with the same key, or your dashboard."
}
}
New keys
A key created after this change never receives the scopes for these 8 endpoints, even before the sunset date. Only keys that already had one of these scopes keep using it until 2026-10-17.
This sunset does not apply to reseller sub-tenant keys.
GET/service
Service details
Read this service's label, billing status, tenant namespace, and the scopes this key carries.
Deprecated
Scope: service:read
Sunset 2026-10-17. Use No mail-server equivalent. This stays in your dashboard. instead.
Every field this endpoint returns is billing/account metadata, not mail-server data β there is no JMAP method that reports a service's billing status or its own key scopes.
GET/usage
Usage snapshot
Read the cached snapshot: storage capacity/used bytes, mailbox and domain counts, and their plan limits.
Deprecated
Scope: usage:read
Sunset 2026-10-17. Use x:Account/query and x:Domain/query for mailbox and domain counts. Storage bytes and the plan-limit fields have no confirmed mail-server equivalent and stay in your dashboard. instead.
Counting your own mailboxes and domains is a matter of listing them with x:Account/query and x:Domain/query and counting the results.
The storage capacity/used-bytes figures and the mailbox/domain plan-limit figures are not confirmed to come from any mail-server call β treat them as dashboard-only.
GET/storage
Storage breakdown
Read the storage pool: base plan GB, effective GB after Additional Storage add-ons, and each add-on's status.
Deprecated
Scope: storage:read
Sunset 2026-10-17. Use No mail-server equivalent. The Additional Storage add-on figures (base_gb, effective_gb, addons) are billing data and stay in your dashboard; the capacity/used-byte figures are not confirmed to come from a mail-server call either. instead.
id, name and is_enabled are confirmed x:Domain properties. verification_status has no mail-server equivalent β it is this endpoint's own cached DNS-check result, and that check itself does not survive the sunset (see GET /dns below).
GET/aliases
List aliases
List every alias on your domains and the mailbox each one delivers to.
Deprecated
Scope: aliases:read
Sunset 2026-10-17. Use x:Account/get β aliases are already part of each account object. See Mailboxes & access. instead.
Directly confirmed: this reads the same "aliases" data already embedded on each x:Account object β there is no separate alias object or endpoint on the mail server.
GET/dns
DNS records & readiness
For each owned domain, list the DNS records to publish, with a live verification result for each.
Deprecated
Scope: dns:read
Sunset 2026-10-17. Use x:Domain for the DNS-record properties, x:DkimSignature for the DKIM record specifically β see Domains & DNS. There is no mail-server call that re-checks live DNS propagation for you; that check stays dashboard-only. instead.
Calling this endpoint today is what makes GET /domains stop reporting "unchecked" for a domain β that side effect goes away when both endpoints retire.
GET/dkim
DKIM signatures
List the active DKIM selectors and public keys for each owned domain.
Deprecated
Scope: dkim:read
Sunset 2026-10-17. Use x:DkimSignature/query then x:DkimSignature/get β see Domains & DNS. Covers signature-backed keys only: a DKIM record published straight into a domain's DNS zone without a matching signature object has no x:DkimSignature equivalent. instead.