Mailbux Docs / API Reference

Management API

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:

{
  "error": {
    "code": "unauthorized",
    "message": "..."
  }
}
StatusCodeWhen
400https_requiredThe request was not made over HTTPS.
401unauthorizedThe bearer token is missing, an unrecognized shape, or does not match any key.
401token_suspendedThe key is suspended. It stays disabled until it is enabled again from the dashboard.
401token_inactiveThe key has been revoked or has expired.
401token_binding_invalidThe service, owner, or tenant this key was bound to has changed. Create a new key.
403forbiddenThe 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.
403api_unavailableThe service is inactive, on a Free plan, or its mail connection is not ready.
429rate_limitedMore than 60 requests in 60 seconds from this key and source IP.
503api_unavailableThe 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.

curl "https://dash.mailbux.com/api/v1/mail/session" \
  -H "Authorization: Bearer YOUR_API_KEY"

Getting started β€” 3. List your mailboxes

Make your first POST /mail call: x:Account/query lists the mailboxes your key can see. accountId is the management account id from step 2.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:Account/query", {"accountId": "YOUR_ACCOUNT_ID"}, "0"]]
  }'

Getting started β€” 4. List your forwarders

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.

curl "https://dash.mailbux.com/api/v1/forwarders" \
  -H "Authorization: Bearer YOUR_API_KEY"

Getting started β€” 5. Read a mailbox's mail

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:

{
  "using": ["urn:ietf:params:jmap:core"],
  "methodCalls": [
    ["x:Domain/query", {"filter": {"name": "example.com"}}, "q"],
    ["x:Domain/get", {
      "#ids": {"resultOf": "q", "name": "x:Domain/query", "path": "/ids"}
    }, "g"]
  ]
}

Capability URNs

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 URNUnlocksAvailable to your key
urn:ietf:params:jmap:coreBase JMAP semantics: get/set/query/changes/queryChanges, the session object, back-references. Include this in every call.Yes
urn:ietf:params:jmap:mailStandard mailbox data β€” Mailbox, Email, Thread, SearchSnippet (see Mail data (JMAP)).Yes
urn:ietf:params:jmap:submissionSending mail and sender identities β€” Identity, EmailSubmission (see Mail data (JMAP)).Yes
urn:ietf:params:jmap:sieveYour mailbox's own filtering rules β€” SieveUserScript (see Mail data (JMAP)).Yes
urn:ietf:params:jmap:quotaStorage usage and limits β€” Quota (see Mail data (JMAP)).Yes
urn:ietf:params:jmap:vacationresponseYour out-of-office auto-reply β€” VacationResponse (see Mail data (JMAP)).Yes
urn:ietf:params:jmap:principalsDirectory/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:

LimitValueWhat it means
Calls per request16Maximum entries in one methodCalls array. Split a larger workflow into more than one POST /mail call.
Ids per Foo/get500A 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/setNot published as a fixed numberYour session advertises this limit, but no specific figure is confirmed β€” keep create/update/destroy batches modest.
Request body size1 MBThis 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 sizeUp to 200 recommendedA 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 sizeUp to 25 MBSee 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>"]).

TypeLevelMeaning
unknownCapabilityRequestA URN in using isn't recognized.
notJSONRequestThe request body isn't valid JSON.
notRequestRequestThe body is JSON but not a valid JMAP request object.
limitRequestA request-level limit was exceeded; the response names which one (maxSizeRequest, maxCallsInRequest, maxConcurrentRequests, maxSizeUpload, maxConcurrentUpload).
unknownMethodMethodThe method name isn't recognized for this account β€” for example, a per-account method your mail server doesn't implement.
invalidArgumentsMethodA required argument is missing or malformed.
invalidResultReferenceMethodA back-reference (#ids) pointed at a call or path that doesn't exist or didn't produce the expected shape.
forbiddenMethodYour key doesn't have permission for this method or this specific call.
accountNotFoundMethodThe accountId doesn't exist, or isn't visible to your key.
accountNotSupportedByMethodMethodThe method does not apply to the given account.
accountReadOnlyMethodA write was attempted against a read-only account context.
requestTooLargeMethodThe 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.
unsupportedFilterQueryA filter condition this object/method does not support β€” for example, querying DKIM signatures without the required domainId filter.
unsupportedSortQueryA sort key this method does not support.
anchorNotFoundQueryThe anchor id used for windowed query results does not exist.
cannotCalculateChangesChangesYour sinceState is too old or unrecognized β€” re-sync with a fresh Foo/get/Foo/query and resume from its state.
invalidForeignKeyExtensionA 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.

TypeMeaning
forbiddenYour key lacks create/update/destroy permission for this object.
overQuotaThe write would exceed your plan's storage or object quota.
tooLargeOne object in the /set batch is too large.
rateLimitStandard JMAP rate-limit error for /set calls.
notFoundupdate/destroy targeted an id that doesn't exist β€” nothing is changed.
invalidPropertiesOne 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.
singletonFor a singleton object (for example your account password or account settings), only update with id "singleton" is accepted β€” create/destroy are rejected.
invalidPatchThe 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.
willDestroyContentsThe destroy would also remove dependent content it did not ask to remove.
stateMismatchYou 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.

Scope: mail:native

curl "https://dash.mailbux.com/api/v1/mail/session" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "capabilities": {
    "urn:ietf:params:jmap:core": { "maxCallsInRequest": 16, "maxObjectsInGet": 500 },
    "urn:ietf:params:jmap:mail": {},
    "urn:ietf:params:jmap:submission": {},
    "urn:ietf:params:jmap:sieve": {},
    "urn:ietf:params:jmap:quota": {}
  },
  "accounts": {
    "<accountId>": {
      "name": "[email protected]",
      "isPersonal": true,
      "isReadOnly": false,
      "accountCapabilities": { "urn:ietf:params:jmap:mail": {} }
    }
  },
  "primaryAccounts": { "urn:ietf:params:jmap:mail": "<accountId>" },
  "username": "[email protected]",
  "apiUrl": "https://dash.mailbux.com/api/v1/mail",
  "downloadUrl": "https://dash.mailbux.com/api/v1/mail/download/{accountId}/{blobId}/{name}?accept={type}",
  "uploadUrl": "https://dash.mailbux.com/api/v1/mail/upload/{accountId}/",
  "eventSourceUrl": "https://dash.mailbux.com/api/v1/mail/events?types={types}&closeafter={closeafter}&ping={ping}",
  "state": "<opaque session state>"
}
Errors
Errors
StatusCodeWhen
502invalid_mail_sessionthe 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
NameTypeAccessDefaultDescription
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.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
    "methodCalls": [["Mailbox/get", {"accountId": "<accountId>", "ids": null}, "a"]]
  }'
{
  "sessionState": "<opaque>",
  "methodResponses": [
    ["Mailbox/get", {
      "accountId": "<accountId>",
      "state": "<opaque>",
      "list": [
        { "id": "<mailboxId>", "name": "Inbox", "role": "inbox", "totalEmails": 42, "unreadEmails": 3 }
      ],
      "notFound": []
    }, "a"]
  ]
}
Errors
Errors
StatusCodeWhen
413payload_too_largethe request body exceeds 1 MB (see Session limits below)
413upstream_response_too_largeyour mail server's reply exceeds 25 MB (does not apply to GET /mail/download/..., which streams unbounded)
502invalid_mail_sessionthis API's own connection to your mail server is misconfigured
502mail_server_unavailablethe 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
NameTypeAccessDefaultDescription
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).
acceptStringβ€”application/octet-streamRequested Content-Type β€” a media type or */*. This is the only query parameter accepted; any other query key is rejected.
curl "https://dash.mailbux.com/api/v1/mail/download/<accountId>/<blobId>/invoice.pdf?accept=application%2Fpdf" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -o invoice.pdf
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
StatusCodeWhen
422invalid_download_pathaccountId, blobId or name contains a disallowed character (control characters, \, /, {, }, %, ?, #) or is longer than 1024 characters
422invalid_download_querya query key other than accept is present, or accept doesn't look like a media type
502invalid_mail_sessionsession/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
NameTypeAccessDefaultDescription
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.
curl -X POST "https://dash.mailbux.com/api/v1/mail/upload/<accountId>" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/sieve" \
  --data-binary @filters.sieve
{
  "accountId": "<accountId>",
  "blobId": "<blobId>",
  "type": "application/sieve",
  "size": 214
}
Errors
Errors
StatusCodeWhen
422invalid_upload_pathaccountId fails the same path check as download
422invalid_content_typeContent-Type is missing or malformed
429rate_limitedmore than 12 uploads/60s for your key (a narrower limit than the general one in API keys)
429upload_busyanother upload for your key is already in flight
413payload_too_largethe streamed bytes exceed the upload ceiling mid-transfer
503upload_unavailablea temporary resource for the upload could not be allocated
400upload_unavailablethe request body could not be read as a stream
502invalid_mail_sessionsession 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
NameTypeAccessDefaultDescription
typesStringβ€”*Comma-separated object type names to watch, or * for all.
closeafterStringβ€”stateOne of state or no β€” whether the stream closes after sending a state change.
pingIntegerβ€”30Keep-alive interval in seconds, 1–60.
Last-Event-IDString (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.
curl -N "https://dash.mailbux.com/api/v1/mail/events?types=Email,Mailbox&closeafter=no&ping=30" \
  -H "Authorization: Bearer YOUR_API_KEY"
A `text/event-stream` body; frames are relayed from your mail server unchanged.
Errors
Errors
StatusCodeWhen
422invalid_event_parameterstypes, closeafter or ping fails validation
422invalid_event_idLast-Event-ID fails validation
429rate_limitedmore than 10 stream-opens/60s for your key (this bounds how often you reconnect, not how long a stream stays open)
429event_stream_busyanother stream for your key is already open
502invalid_mail_sessionsession 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
NameTypeAccessDefaultDescription
@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*Stringread-writeβ€”Local part of the mailbox's address (before the @). Renaming changes the mailbox's login address; the full address is name@domain.
domainId*Idcreate-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.
emailAddressStringserver-setβ€”Computed as name@domain. Present only in responses.
credentialsObjectwrite-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.
createdAtUTCDateTimeserver-setβ€”When the mailbox was created.
memberGroupIdsId[]read-writeβ€”Permission groups this mailbox belongs to.
memberTenantIdId|nullserver-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*Objectread-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*Objectread-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.
quotasObjectread-writeβ€”{"maxDiskQuota": <bytes>}. This server refuses less than 1073741824 (1 GB) for a mailbox with invalidProperties; omitting quotas defaults to 0 and also fails.
usedDiskQuotaUnsignedIntserver-setβ€”Bytes currently used.
aliasesObjectread-writeβ€”Indexed map of {enabled, name, domainId, description?} β€” see "Add or remove an alias" below for the exact write shape.
externalIdString|nullread-writeβ€”Free-form identifier for your own records; not used by Mailbux.
descriptionString|nullread-writeβ€”Display name shown in the dashboard and used as the mailbox's label.
localeStringread-writeen-USBCP-47 locale.
timeZoneString|nullread-writeβ€”IANA time zone name, e.g. "Europe/London".
encryptionAtRest*Objectread-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
NameTypeAccessDefaultDescription
textStringβ€”β€”Free-text match; which fields it searches was not independently verified.
nameStringβ€”β€”Exact match on the mailbox's local-part.
domainIdIdβ€”β€”Restrict to mailboxes on one of your domains.
memberTenantIdIdβ€”β€”Restrict to one organization. Your key is already confined to your own, so this rarely needs to be set explicitly.
memberGroupIdsId[]β€”β€”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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your own account id, from GET /mail/session.
filterObjectβ€”β€”Any of: text, name, domainId, memberTenantId, memberGroupIds.
positionIntβ€”β€”Zero-based offset into the result window.
limitUnsignedIntβ€”β€”Page size; keep to 200 or less.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:Account/query", {
        "accountId": "m1",
        "filter": { "domainId": "d1" }
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:Account/query", {
      "accountId": "m1",
      "queryState": "s1",
      "canCalculateChanges": false,
      "position": 0,
      "ids": ["a1", "a2"]
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have the permission this call requires.
β€”accountNotFoundThe 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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your own account id, from GET /mail/session.
idsId[]|nullβ€”β€”Mailbox ids to fetch, or null for a capped page (see notes).
propertiesString[]β€”β€”Narrow the response to just these property names.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:Account/get", {
        "accountId": "m1",
        "ids": ["a1"],
        "properties": ["id", "name", "domainId", "emailAddress", "description", "quotas", "usedDiskQuota", "aliases", "createdAt"]
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:Account/get", {
      "accountId": "m1",
      "state": "s1",
      "list": [
        {
          "id": "a1",
          "name": "sales",
          "domainId": "d1",
          "emailAddress": "[email protected]",
          "description": "Sales inbox",
          "quotas": { "maxDiskQuota": 1073741824 },
          "usedDiskQuota": 52428800,
          "aliases": { "0": { "enabled": true, "name": "info", "domainId": "d1" } },
          "createdAt": "2026-01-15T10:00:00Z"
        }
      ],
      "notFound": []
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have the permission this call requires.
β€”accountNotFoundThe accountId in the call is not visible to your key.
β€”requestTooLargeThe 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
NameTypeAccessDefaultDescription
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.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:Account/set", {
        "accountId": "m1",
        "create": {
          "new": {
            "@type": "User",
            "name": "sales",
            "domainId": "d1",
            "description": "Sales inbox",
            "roles": { "@type": "User" },
            "permissions": { "@type": "Inherit" },
            "encryptionAtRest": { "@type": "Disabled" },
            "quotas": { "maxDiskQuota": 1073741824 }
          }
        }
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:Account/set", {
      "accountId": "m1",
      "oldState": "s1",
      "newState": "s2",
      "created": {
        "new": {
          "id": "a1",
          "emailAddress": "[email protected]",
          "createdAt": "2026-01-15T10:00:00Z",
          "usedDiskQuota": 0
        }
      },
      "notCreated": {}
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have mailbox-create permission.
β€”invalidPropertiesA 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.
β€”invalidForeignKeydomainId does not resolve within your organization β€” including a domainId that belongs to another organization, which your key cannot see.
β€”primaryKeyViolationThe 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.
β€”overQuotaYour 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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your own account id, from GET /mail/session.
update*Objectβ€”β€”Map of mailbox id to the properties to patch.
ifInStateStringβ€”β€”Optional CAS token from a prior get/set state; see notes.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:Account/set", {
        "accountId": "m1",
        "update": {
          "a1": {
            "description": "Sales team inbox",
            "quotas": { "maxDiskQuota": 2147483648 },
            "timeZone": "Europe/London"
          }
        }
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:Account/set", {
      "accountId": "m1",
      "oldState": "s2",
      "newState": "s3",
      "updated": { "a1": null },
      "notUpdated": {}
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have mailbox-update permission.
β€”notFoundThe mailbox id does not exist, or belongs to another organization (both look identical to your key).
β€”invalidPropertiesA new value fails validation, e.g. quotas.maxDiskQuota below 1073741824 (1 GB).
β€”stateMismatchifInState 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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your own account id, from GET /mail/session.
update*Objectβ€”β€”Map of mailbox id to {"credentials/0/secret": "<new password>"}.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:Account/set", {
        "accountId": "m1",
        "update": {
          "a1": { "credentials/0/secret": "N3wStrongPassw0rd!" }
        }
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:Account/set", {
      "accountId": "m1",
      "oldState": "s3",
      "newState": "s4",
      "updated": { "a1": null },
      "notUpdated": {}
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have mailbox-update permission.
β€”notFoundThe mailbox id does not exist, or belongs to another organization.
β€”invalidPropertiesThe 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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your own account id, from GET /mail/session.
update*Objectβ€”β€”Map of mailbox id to {"aliases": {"0": {...}, "1": {...}, ...}}.
ifInStateStringβ€”β€”Optional CAS token; see notes.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:Account/set", {
        "accountId": "m1",
        "update": {
          "a1": {
            "aliases": {
              "0": { "enabled": true, "name": "info", "domainId": "d1" },
              "1": { "enabled": true, "name": "sales", "domainId": "d1" }
            }
          }
        }
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:Account/set", {
      "accountId": "m1",
      "oldState": "s4",
      "newState": "s5",
      "updated": { "a1": null },
      "notUpdated": {}
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have mailbox-update permission.
β€”notFoundThe mailbox id does not exist, or belongs to another organization.
β€”invalidPatchaliases was sent as a JSON array instead of an indexed object ({"0": {...}} rather than [{...}]).
β€”invalidPropertiesAn alias entry is missing name or domainId, or domainId is not one of your domains.
β€”stateMismatchifInState 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.

Parameters
Suspend or unsuspend a mailbox's login parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your own account id, from GET /mail/session.
update*Objectβ€”β€”Map of mailbox id to a new permissions value.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:Account/set", {
        "accountId": "m1",
        "update": {
          "a1": { "permissions": { "@type": "Replace", "enabledPermissions": {}, "disabledPermissions": {} } }
        }
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:Account/set", {
      "accountId": "m1",
      "oldState": "s5",
      "newState": "s6",
      "updated": { "a1": null },
      "notUpdated": {}
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenVerified 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.
β€”notFoundThe 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.
POSTx:Account/set

Delete a mailbox

Permanently deletes a mailbox.

Parameters
Delete a mailbox parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your own account id, from GET /mail/session.
destroy*Id[]β€”β€”Mailbox ids to delete.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:Account/set", {
        "accountId": "m1",
        "destroy": ["a1"]
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:Account/set", {
      "accountId": "m1",
      "oldState": "s6",
      "newState": "s7",
      "destroyed": ["a1"],
      "notDestroyed": {}
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have mailbox-destroy permission.
β€”notFoundThe 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
NameTypeAccessDefaultDescription
description*Stringcreate-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.
secretStringserver-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.
createdAtUTCDateTimeserver-setβ€”When the app password was created.
expiresAtUTCDateTime|nullcreate-onlyβ€”Optional expiry. Omit for an app password that never expires.
permissions*Objectcreate-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.
allowedIpsObjectcreate-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
NameTypeAccessDefaultDescription
expiresAtUTCDateTimeβ€”β€”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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”The mailbox's own account id (not your own).
idsId[]|nullβ€”β€”Specific app password ids, or null for all of this mailbox's app passwords.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:AppPassword/get", {
        "accountId": "a1",
        "ids": null
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:AppPassword/get", {
      "accountId": "a1",
      "state": "s1",
      "list": [
        {
          "id": "ap1",
          "description": "iPhone Mail",
          "createdAt": "2026-01-15T10:00:00Z",
          "expiresAt": null,
          "permissions": { "@type": "Inherit" },
          "allowedIps": []
        }
      ],
      "notFound": []
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have app-password-read permission.
β€”accountNotFoundThe mailbox's accountId does not exist, or belongs to another organization.
Notes
  • x:AppPassword/query (filter: expiresAt) is also available if you only need ids, e.g. to page through a mailbox with many app passwords.
  • On a Free-plan service there is no API key to begin with, so this call is unreachable rather than refused (see API keys).
POSTx:AppPassword/set

Create an app password

Creates a secondary credential for a mail client that needs a password instead of your API key.

Parameters
Create an app password parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”The mailbox's own account id.
create*Objectβ€”β€”Map of your own creation-id to {description, permissions, expiresAt?, allowedIps?}.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:AppPassword/set", {
        "accountId": "a1",
        "create": {
          "new": {
            "description": "iPhone Mail",
            "permissions": { "@type": "Inherit" }
          }
        }
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:AppPassword/set", {
      "accountId": "a1",
      "oldState": "s1",
      "newState": "s2",
      "created": {
        "new": {
          "id": "ap1",
          "secret": "gk8x9QzP2mLwR7vN4tYcJ3hF",
          "createdAt": "2026-01-15T10:00:00Z"
        }
      },
      "notCreated": {}
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have app-password-create permission.
β€”accountNotFoundThe mailbox's accountId does not exist, or belongs to another organization.
β€”invalidPatchpermissions 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.

Parameters
Revoke an app password parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”The mailbox's own account id.
destroy*Id[]β€”β€”App password ids to revoke.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:AppPassword/set", {
        "accountId": "a1",
        "destroy": ["ap1"]
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:AppPassword/set", {
      "accountId": "a1",
      "oldState": "s2",
      "newState": "s3",
      "destroyed": ["ap1"],
      "notDestroyed": {}
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have app-password-destroy permission.
β€”notFoundThe 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
NameTypeAccessDefaultDescription
descriptionString|nullread-writeβ€”Display name for the mailbox.
localeStringread-writeen-USBCP-47 locale.
timeZoneString|nullread-writeβ€”IANA time zone name.
encryptionAtRest*Objectread-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".

Parameters
Get a mailbox's settings parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”The mailbox's own account id.
ids*Id[]β€”β€”Always ["singleton"].
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:AccountSettings/get", {
        "accountId": "a1",
        "ids": ["singleton"]
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:AccountSettings/get", {
      "accountId": "a1",
      "state": "s1",
      "list": [
        {
          "id": "singleton",
          "description": "Sales inbox",
          "locale": "en-US",
          "timeZone": "Europe/London",
          "encryptionAtRest": { "@type": "Disabled" }
        }
      ],
      "notFound": []
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have permission to read this mailbox's settings.
β€”accountNotFoundThe mailbox's accountId does not exist, or belongs to another organization.
POSTx:AccountSettings/set

Update a mailbox's settings

Patches the singleton settings record. create and destroy are rejected for this object β€” only update against id "singleton" is accepted.

Parameters
Update a mailbox's settings parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”The mailbox's own account id.
update*Objectβ€”β€”Must be keyed by "singleton".
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:AccountSettings/set", {
        "accountId": "a1",
        "update": {
          "singleton": { "locale": "en-GB" }
        }
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:AccountSettings/set", {
      "accountId": "a1",
      "oldState": "s1",
      "newState": "s2",
      "updated": { "singleton": null },
      "notUpdated": {}
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have permission to update this mailbox's settings.
β€”invalidPropertiesA new value fails validation.
β€”singletonAn 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
NameTypeAccessDefaultDescription
secretStringwrite-onlyβ€”The new password. Never returned by a get.
currentSecretStringwrite-onlyβ€”Proves the mailbox's existing password before changing it or enrolling two-factor login. Never returned by a get.
otpAuthObjectwrite-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.

Parameters
Get a mailbox's password/2FA record parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”The mailbox's own account id.
ids*Id[]β€”β€”Always ["singleton"].
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:AccountPassword/get", {
        "accountId": "a1",
        "ids": ["singleton"]
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:AccountPassword/get", {
      "accountId": "a1",
      "state": "s1",
      "list": [
        { "id": "singleton" }
      ],
      "notFound": []
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have permission to read this mailbox's credential record.
β€”accountNotFoundThe 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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”The mailbox's own account id.
update*Objectβ€”β€”Keyed by "singleton", with currentSecret and secret.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:AccountPassword/set", {
        "accountId": "a1",
        "update": {
          "singleton": {
            "currentSecret": "CurrentPassw0rd!",
            "secret": "N3wStrongPassw0rd!"
          }
        }
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:AccountPassword/set", {
      "accountId": "a1",
      "oldState": "s1",
      "newState": "s2",
      "updated": { "singleton": null },
      "notUpdated": {}
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have permission to update this mailbox's credentials.
β€”invalidPropertiescurrentSecret does not match the mailbox's current password, or the new secret fails the strength check.
β€”singletonAn 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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”The mailbox's own account id.
update*Objectβ€”β€”Keyed by "singleton", with currentSecret and otpAuth.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:AccountPassword/set", {
        "accountId": "a1",
        "update": {
          "singleton": {
            "currentSecret": "CurrentPassw0rd!",
            "otpAuth": {
              "otpUrl": "otpauth://totp/Mailbux:[email protected]?secret=EXAMPLE2FASECRET&issuer=Mailbux",
              "otpCode": "123456"
            }
          }
        }
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:AccountPassword/set", {
      "accountId": "a1",
      "oldState": "s2",
      "newState": "s3",
      "updated": { "singleton": null },
      "notUpdated": {}
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have permission to update this mailbox's credentials.
β€”invalidPropertiescurrentSecret is wrong, otpUrl is malformed, or otpCode does not match otpUrl at the time of the call.
POSTx:AccountPassword/set

Disable two-factor (TOTP) login

Turns off TOTP login for a mailbox by clearing otpUrl.

Parameters
Disable two-factor (TOTP) login parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”The mailbox's own account id.
update*Objectβ€”β€”Keyed by "singleton", with otpAuth.otpUrl set to null.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [
      ["x:AccountPassword/set", {
        "accountId": "a1",
        "update": {
          "singleton": { "otpAuth": { "otpUrl": null } }
        }
      }, "0"]
    ]
  }'
{
  "methodResponses": [
    ["x:AccountPassword/set", {
      "accountId": "a1",
      "oldState": "s3",
      "newState": "s4",
      "updated": { "singleton": null },
      "notUpdated": {}
    }, "0"]
  ],
  "sessionState": "st1"
}
Errors
Errors
StatusCodeWhen
β€”forbiddenYour key does not have permission to update this mailbox's credentials.
β€”invalidPropertiesThe 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.

urn:ietf:params:jmap:coregetqueryset: create, update, destroy
Properties (16)
x:Domain properties
NameTypeAccessDefaultDescription
idIdserver-setβ€”Server-assigned object id. Use it as domainId when referencing this domain from x:DkimSignature, x:MailingList, mailboxes and forwarders.
name*DomainNameread-writeβ€”The domain name, e.g. example.com.
aliasesDomainName[]read-writeβ€”Additional domain names that alias this domain.
isEnabledBooleanread-writetrueWhether the domain is active.
createdAtUTCDateTimeserver-setβ€”When the domain was created.
descriptionString|nullread-writeβ€”Optional free-text note.
logoString|nullread-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.
memberTenantIdId|nullserver-setβ€”Enterprise sub-tenant scoping. A confined key (your API key) never needs to send this and cannot change it once set.
directoryIdId|nullserver-setβ€”Directory binding. Not used on this platform β€” always null.
catchAllAddressEmailAddress|nullread-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).
allowRelayingBooleanread-writefalseWhether the domain may relay outbound mail beyond normal sending rules.
reportAddressUriString|nullread-writemailto:postmasterDMARC / TLS-RPT / CAA aggregate report destination.
Filters (3)
x:Domain filters
NameTypeAccessDefaultDescription
textStringβ€”β€”Substring match against the domain name.
nameDomainNameβ€”β€”Exact domain name match.
memberTenantIdId|nullβ€”β€”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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id, from GET /mail/session.
idsId[]|nullβ€”β€”Domain ids to fetch. Omit or send null to fetch every domain in your organization.
propertiesString[]β€”β€”Property names to return. Omit to return every property.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:Domain/get", {
      "accountId": "a1",
      "ids": ["d1"]
    }, "a"]]
  }'
{
  "methodResponses": [["x:Domain/get", {
    "accountId": "a1",
    "state": "s1",
    "list": [{
      "id": "d1",
      "name": "example.com",
      "aliases": [],
      "isEnabled": true,
      "createdAt": "2026-01-10T12:00:00Z",
      "description": null,
      "logo": null,
      "certificateManagement": { "@type": "Manual" },
      "dkimManagement": { "@type": "Manual" },
      "dnsManagement": { "@type": "Manual" },
      "memberTenantId": null,
      "directoryId": null,
      "catchAllAddress": null,
      "subAddressing": { "@type": "Enabled" },
      "allowRelaying": false,
      "reportAddressUri": "mailto:postmaster"
    }],
    "notFound": []
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry domain-read permission.
β€”accountNotFoundaccountId is missing, malformed, or not visible to your key.
Notes
  • Ids that do not exist, or belong to another organization, come back in notFound β€” the call still succeeds.
  • Omitting ids returns every domain in your organization in one call; there is no separate "list" method.
POSTx:Domain/query

Query / search domains

Search your domains by name, with optional full-text matching.

Scope: mail:native

Parameters
Query / search domains parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
filterObjectβ€”β€”Any combination of text, name and memberTenantId (see Filters above).
limitUnsignedIntβ€”β€”Maximum ids to return.
positionIntβ€”β€”Zero-based starting index into the result list.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:Domain/query", {
      "accountId": "a1",
      "filter": { "name": "example.com" }
    }, "a"]]
  }'
{
  "methodResponses": [["x:Domain/query", {
    "accountId": "a1",
    "queryState": "s1",
    "canCalculateChanges": false,
    "position": 0,
    "ids": ["d1"]
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry domain-read permission.
Notes
  • query returns ids only. Chain a x:Domain/get in the same request using a back-reference (see "Mail server API basics"), or issue a follow-up get call.
  • filter.text does a substring match against the domain name; filter.name matches it exactly.
POSTx:Domain/set

Add a domain

Register a new domain on your organization.

Scope: mail:native

Parameters
Add a domain parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
create*Objectβ€”β€”A map of a temporary client id (e.g. "new") to the domain to create.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:Domain/set", {
      "accountId": "a1",
      "create": {
        "new": {
          "name": "example.com",
          "certificateManagement": { "@type": "Manual" },
          "dkimManagement": { "@type": "Manual" },
          "dnsManagement": { "@type": "Manual" },
          "subAddressing": { "@type": "Enabled" }
        }
      }
    }, "a"]]
  }'
{
  "methodResponses": [["x:Domain/set", {
    "accountId": "a1",
    "oldState": "s1",
    "newState": "s2",
    "created": {
      "new": {
        "id": "d1",
        "name": "example.com",
        "isEnabled": true,
        "createdAt": "2026-01-10T12:00:00Z",
        "certificateManagement": { "@type": "Manual" },
        "dkimManagement": { "@type": "Manual" },
        "dnsManagement": { "@type": "Manual" },
        "subAddressing": { "@type": "Enabled" },
        "catchAllAddress": null,
        "allowRelaying": false,
        "reportAddressUri": "mailto:postmaster"
      }
    },
    "notCreated": null
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry domain-create permission.
β€”invalidPropertiesname 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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
update*Objectβ€”β€”A map of domainId to the properties to change.
ifInStateStringβ€”β€”Pass the state you last read to reject the update if the domain changed since (concurrency guard).
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:Domain/set", {
      "accountId": "a1",
      "ifInState": "s2",
      "update": {
        "d1": { "catchAllAddress": "[email protected]" }
      }
    }, "a"]]
  }'
{
  "methodResponses": [["x:Domain/set", {
    "accountId": "a1",
    "oldState": "s2",
    "newState": "s3",
    "updated": { "d1": null },
    "notUpdated": null
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry domain-update permission.
β€”notFounddomainId does not exist, or belongs to another organization.
β€”invalidPropertiesa value fails validation, e.g. catchAllAddress is not one of your own addresses.
β€”invalidPatchyou try to change memberTenantId β€” it is not patchable.
β€”stateMismatchifInState 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.
POSTx:Domain/set

Delete a domain

Remove a domain from your organization.

Scope: mail:native

Parameters
Delete a domain parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
destroy*Id[]β€”β€”Domain ids to delete.
ifInStateStringβ€”β€”Optional concurrency guard.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:Domain/set", {
      "accountId": "a1",
      "destroy": ["d1"]
    }, "a"]]
  }'
{
  "methodResponses": [["x:Domain/set", {
    "accountId": "a1",
    "oldState": "s3",
    "newState": "s4",
    "destroyed": ["d1"],
    "notDestroyed": null
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry domain-destroy permission.
β€”notFounddomainId 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.

urn:ietf:params:jmap:coregetquery (domainId filter required)set: create, update, destroy
Properties (18)
x:DkimSignature properties
NameTypeAccessDefaultDescription
idIdserver-setβ€”Server-assigned object id.
@type*"Dkim1Ed25519Sha256" | "Dkim1RsaSha256" | "Dkim2Ed25519Sha256" | "Dkim2RsaSha256"read-writeβ€”The signing algorithm and DKIM generation (DKIM1 or DKIM2).
domainId*Idread-writeβ€”The domain this key signs for. Also the required query filter.
selector*Stringread-writeβ€”Used to locate the public key in DNS: selector._domainkey.<domain>.
privateKey*Stringwrite-onlyβ€”PEM private key you generate. Never returned again once written.
publicKeyStringserver-setβ€”Derived from privateKey. This is what you publish in DNS.
stage"active" | "pending" | "retiring" | "retired"read-writeactiveWhere this key is in its rotation lifecycle.
nextTransitionAtUTCDateTime|nullserver-setβ€”When the server will next move stage automatically, if it is managing a scheduled transition.
createdAtUTCDateTimeserver-setβ€”When the signature was created.
canonicalization"relaxed/relaxed" | "simple/simple" | "relaxed/simple" | "simple/relaxed"read-writerelaxed/relaxedDKIM1 only.
headersString[]read-writeDate, From, Message-ID, Subject, ToHeaders this signature covers. DKIM1 only.
reportBooleanread-writetrueDKIM1 only.
expireString|nullread-writeβ€”Signature expiry duration. DKIM1 only.
auidString|nullread-writeβ€”Agent/User identifier (DKIM1's i= tag). DKIM1 only.
thirdPartyString|nullread-writeβ€”DKIM1 only.
thirdPartyHash"sha256" | "sha1" | nullread-writeβ€”DKIM1 only.
flags("donotmodify" | "donotexplode" | "feedback")[]read-writeβ€”DKIM2 only.
memberTenantIdId|nullserver-setβ€”Enterprise only. A confined key never needs to send this.
Filters (2)
x:DkimSignature filters
NameTypeAccessDefaultDescription
domainId*Idβ€”β€”Required on every query β€” an unfiltered query is rejected.
memberTenantIdId|nullβ€”β€”Enterprise sub-tenant scoping.
  • Replaces the deprecated GET /dkim summary (see Deprecated).
  • Key rotation is create + update + destroy composed β€” there is no single "rotate" call.
  • A DKIM signature id from another organization, or for a domain you do not own, behaves as not found.
POSTx:DkimSignature/get

Get DKIM signatures

Fetch specific DKIM signatures by id, or every signature in your organization when you omit ids.

Scope: mail:native

Parameters
Get DKIM signatures parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
idsId[]|nullβ€”β€”Signature ids to fetch. Omit for all.
propertiesString[]β€”β€”Property names to return.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:DkimSignature/get", {
      "accountId": "a1",
      "ids": ["k1"]
    }, "a"]]
  }'
{
  "methodResponses": [["x:DkimSignature/get", {
    "accountId": "a1",
    "state": "s1",
    "list": [{
      "id": "k1",
      "@type": "Dkim1RsaSha256",
      "domainId": "d1",
      "selector": "mbx1",
      "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC...",
      "canonicalization": "relaxed/relaxed",
      "headers": ["Date", "From", "Message-ID", "Subject", "To"],
      "report": true,
      "stage": "active",
      "createdAt": "2026-01-10T12:00:00Z",
      "nextTransitionAt": null
    }],
    "notFound": []
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry DKIM-read permission.
β€”accountNotFoundaccountId 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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
filter*Objectβ€”β€”Must include domainId; memberTenantId is also accepted.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:DkimSignature/query", {
      "accountId": "a1",
      "filter": { "domainId": "d1" }
    }, "a"]]
  }'
{
  "methodResponses": [["x:DkimSignature/query", {
    "accountId": "a1",
    "queryState": "s1",
    "canCalculateChanges": false,
    "position": 0,
    "ids": ["k1"]
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry DKIM-read permission.
β€”unsupportedFilterdomainId is missing from filter β€” it is required, this type has no unfiltered listing.
Notes
  • Always filter by domainId. An unfiltered query is rejected rather than returning every signature in your organization.
POSTx:DkimSignature/set

Create (or start rotating) a DKIM signature

Add a DKIM signing key for a domain. Use a fresh selector to add a second, rotating key alongside an existing one.

Scope: mail:native

Parameters
Create (or start rotating) a DKIM signature parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
create*Objectβ€”β€”A map of a temporary client id to the signature to create.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:DkimSignature/set", {
      "accountId": "a1",
      "create": {
        "new": {
          "@type": "Dkim1RsaSha256",
          "domainId": "d1",
          "selector": "mbx2",
          "privateKey": "-----BEGIN PRIVATE KEY-----\nMIIEvQ...\n-----END PRIVATE KEY-----"
        }
      }
    }, "a"]]
  }'
{
  "methodResponses": [["x:DkimSignature/set", {
    "accountId": "a1",
    "oldState": "s1",
    "newState": "s2",
    "created": {
      "new": {
        "id": "k2",
        "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD...",
        "canonicalization": "relaxed/relaxed",
        "headers": ["Date", "From", "Message-ID", "Subject", "To"],
        "report": true,
        "stage": "active",
        "createdAt": "2026-02-01T09:00:00Z",
        "nextTransitionAt": null
      }
    },
    "notCreated": null
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry DKIM-create permission.
β€”invalidProperties@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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
update*Objectβ€”β€”A map of signature id to the properties to change.
ifInStateStringβ€”β€”Optional concurrency guard.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:DkimSignature/set", {
      "accountId": "a1",
      "update": { "k1": { "stage": "retiring" } }
    }, "a"]]
  }'
{
  "methodResponses": [["x:DkimSignature/set", {
    "accountId": "a1",
    "oldState": "s2",
    "newState": "s3",
    "updated": { "k1": null },
    "notUpdated": null
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry DKIM-update permission.
β€”notFoundthe signature id does not exist, or belongs to a domain outside your organization.
β€”invalidPropertiesstage 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.
POSTx:DkimSignature/set

Delete a DKIM signature

Remove a DKIM signing key.

Scope: mail:native

Parameters
Delete a DKIM signature parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
destroy*Id[]β€”β€”Signature ids to delete.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:DkimSignature/set", {
      "accountId": "a1",
      "destroy": ["k1"]
    }, "a"]]
  }'
{
  "methodResponses": [["x:DkimSignature/set", {
    "accountId": "a1",
    "oldState": "s3",
    "newState": "s4",
    "destroyed": ["k1"],
    "notDestroyed": null
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry DKIM-destroy permission.
β€”notFoundthe 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.

urn:ietf:params:jmap:coregetqueryset: create, update, destroy
Properties (8)
x:MailingList properties
NameTypeAccessDefaultDescription
idIdserver-setβ€”Server-assigned object id.
name*Stringread-writeβ€”Local part of the list address. Must match ^[a-z0-9._-]+$, max 64 characters (see notes).
domainId*Idread-writeβ€”The domain this list's address belongs to.
emailAddressEmailAddressserver-setβ€”The full list address: name@ the domain. Derived, not sent by you.
descriptionString|nullread-writeβ€”Optional free-text note.
aliasesObjectread-writeβ€”Additional addresses for this list, as an indexed object (same shape as a mailbox's aliases).
recipientsObjectread-writeβ€”The list's members, as a map keyed by address, e.g. {"[email protected]": true}. 1 to 100 addresses (see notes).
memberTenantIdId|nullserver-setβ€”Enterprise sub-tenant scoping. A confined key never needs to send this.
Filters (2)
x:MailingList filters
NameTypeAccessDefaultDescription
textStringβ€”β€”Substring match against the list's name/address.
memberTenantIdId|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.

Scope: mail:native

Parameters
Get mailing lists parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
idsId[]|nullβ€”β€”List ids to fetch. Omit for all.
propertiesString[]β€”β€”Property names to return.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:MailingList/get", {
      "accountId": "a1",
      "ids": ["l1"]
    }, "a"]]
  }'
{
  "methodResponses": [["x:MailingList/get", {
    "accountId": "a1",
    "state": "s1",
    "list": [{
      "id": "l1",
      "name": "sales-team",
      "domainId": "d1",
      "emailAddress": "[email protected]",
      "description": null,
      "aliases": {},
      "recipients": { "[email protected]": true, "[email protected]": true }
    }],
    "notFound": []
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry mailing-list-read permission.
β€”accountNotFoundaccountId is missing, malformed, or not visible to your key.
Notes
  • emailAddress is server-set β€” it is derived from name and domainId, not something you send.
  • Ids that do not exist, or belong to another organization, come back in notFound.
POSTx:MailingList/query

Query / search mailing lists

Search your distribution lists by name.

Scope: mail:native

Parameters
Query / search mailing lists parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
filterObjectβ€”β€”text and/or memberTenantId.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:MailingList/query", {
      "accountId": "a1"
    }, "a"]]
  }'
{
  "methodResponses": [["x:MailingList/query", {
    "accountId": "a1",
    "queryState": "s1",
    "canCalculateChanges": false,
    "position": 0,
    "ids": ["l1"]
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry mailing-list-read permission.
Notes
  • filter is optional β€” an empty filter lists every mailing list in your organization.
POSTx:MailingList/set

Create a mailing list

Create a distribution list: one address that fans out to a fixed set of recipients.

Scope: mail:native

Parameters
Create a mailing list parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
create*Objectβ€”β€”A map of a temporary client id to the list to create.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:MailingList/set", {
      "accountId": "a1",
      "create": {
        "new": {
          "name": "sales-team",
          "domainId": "d1",
          "recipients": { "[email protected]": true, "[email protected]": true }
        }
      }
    }, "a"]]
  }'
{
  "methodResponses": [["x:MailingList/set", {
    "accountId": "a1",
    "oldState": "s1",
    "newState": "s2",
    "created": {
      "new": {
        "id": "l1",
        "emailAddress": "[email protected]"
      }
    },
    "notCreated": null
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry mailing-list-create permission.
β€”invalidPropertiesname 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
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
update*Objectβ€”β€”A map of list id to the properties to change.
ifInStateStringβ€”β€”Pass the state you last read to reject the update if the list changed since.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:MailingList/set", {
      "accountId": "a1",
      "ifInState": "s2",
      "update": {
        "l1": {
          "name": "sales-team",
          "domainId": "d1",
          "recipients": { "[email protected]": true, "[email protected]": true }
        }
      }
    }, "a"]]
  }'
{
  "methodResponses": [["x:MailingList/set", {
    "accountId": "a1",
    "oldState": "s2",
    "newState": "s3",
    "updated": { "l1": null },
    "notUpdated": null
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry mailing-list-update permission.
β€”notFoundthe list id does not exist, or belongs to another organization.
β€”invalidPropertiesthe new name, domainId or recipients fail the same checks as create.
β€”invalidPatchrecipients or aliases is sent as a JSON array instead of a map/indexed object.
β€”stateMismatchifInState 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.
POSTx:MailingList/set

Delete a mailing list

Remove a distribution list.

Scope: mail:native

Parameters
Delete a mailing list parameters
NameTypeAccessDefaultDescription
accountId*Idβ€”β€”Your management account id.
destroy*Id[]β€”β€”List ids to delete.
ifInStateStringβ€”β€”Optional concurrency guard.
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:MailingList/set", {
      "accountId": "a1",
      "ifInState": "s3",
      "destroy": ["l1"]
    }, "a"]]
  }'
{
  "methodResponses": [["x:MailingList/set", {
    "accountId": "a1",
    "oldState": "s3",
    "newState": "s4",
    "destroyed": ["l1"],
    "notDestroyed": null
  }, "a"]]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry mailing-list-destroy permission.
β€”notFoundthe list id does not exist, or belongs to another organization.
β€”stateMismatchifInState 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.

getquery (filter: expiresAt)set: create, update, destroy
Properties (6)
x:ApiKey properties
NameTypeAccessDefaultDescription
description*Stringread-writeβ€”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.
secretStringserver-setβ€”The key value. Always server-generated β€” a client-supplied secret is rejected. Returned once, in the create response, and never again.
createdAtUTCDateTimeserver-setβ€”When the key was created.
expiresAtUTCDateTime|nullread-writeβ€”Optional expiry. Omit or set null for a key that does not expire.
permissions*Objectread-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.
allowedIpsSet<IpMask>read-writeβ€”CIDR ranges this key may be used from. Empty (unrestricted) by default.
Filters (1)
x:ApiKey filters
NameTypeAccessDefaultDescription
expiresAtUTCDateTimeβ€”β€”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.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:ApiKey/get", {"accountId": "<accountId>", "ids": null}, "a"]]
  }'
{
  "methodResponses": [
    ["x:ApiKey/get", {
      "accountId": "<accountId>",
      "state": "<opaque>",
      "list": [
        {
          "id": "<id>",
          "description": "integration-key",
          "createdAt": "2026-01-01T00:00:00Z",
          "expiresAt": null,
          "permissions": {"@type": "Inherit"},
          "allowedIps": {}
        }
      ],
      "notFound": []
    }, "a"]
  ]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key does not carry permission to read API keys
POSTx:ApiKey/query

List API keys

Returns matching key ids; follow up with x:ApiKey/get (or a back-reference) for the details.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:ApiKey/query", {"accountId": "<accountId>"}, "a"]]
  }'
{
  "methodResponses": [
    ["x:ApiKey/query", {
      "accountId": "<accountId>",
      "queryState": "<opaque>",
      "canCalculateChanges": false,
      "ids": ["<id>"]
    }, "a"]
  ]
}
POSTx:ApiKey/set

Create a mailbox-scoped API key

Issues a new key for a specific mailbox account. The secret is returned once, in this response.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:ApiKey/set", {"accountId": "<accountId>", "create": {"new": {
      "description": "integration-key",
      "permissions": {"@type": "Inherit"}
    }}}, "a"]]
  }'
{
  "methodResponses": [
    ["x:ApiKey/set", {
      "accountId": "<accountId>",
      "created": {
        "new": {
          "id": "<id>",
          "secret": "<returned once β€” store it now>",
          "createdAt": "2026-01-01T00:00:00Z"
        }
      },
      "notCreated": {}
    }, "a"]
  ]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key lacks create permission for API keys, or the Free plan blocks it
β€”invalidPropertiespermissions is missing, or description is missing
β€”invalidPatchpermissions was sent as a nested Replace list instead of {"@type": "Inherit"}
Notes
  • Do not send permissions.permissions, enabledPermissions or disabledPermissions β€” only the exact shape shown above is accepted at create time.
POSTx:ApiKey/set

Revoke an API key

Permanently destroys a key by id. There is no undo.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:ApiKey/set", {"accountId": "<accountId>", "destroy": ["<id>"]}, "a"]]
  }'
{
  "methodResponses": [
    ["x:ApiKey/set", {
      "accountId": "<accountId>",
      "destroyed": ["<id>"],
      "notDestroyed": {}
    }, "a"]
  ]
}
Errors
Errors
StatusCodeWhen
β€”forbiddenyour key lacks destroy permission for API keys
β€”notFoundthe 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.

Scope: forwarders:read

curl "https://dash.mailbux.com/api/v1/forwarders" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "data": [
    {
      "id": 501,
      "domain_id": "dom_1",
      "source_address": "[email protected]",
      "destination_addresses": ["[email protected]"],
      "mode": "copy",
      "enabled": true,
      "status": "active",
      "comment": "",
      "updated_at": "2026-09-17T10:00:00+00:00"
    }
  ]
}
Notes
  • An empty service returns "data": [].
  • The reserved system catch-all forwarder is included here even though it cannot be written to β€” see How forwarders work above.
GET/forwarders/{forwarder}

Get one forwarder

Reads a single forwarder belonging to this service.

Scope: forwarders:read

Parameters
Get one forwarder parameters
NameTypeAccessDefaultDescription
forwarder*Idβ€”β€”The forwarder id, from List forwarders.
curl "https://dash.mailbux.com/api/v1/forwarders/501" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "data": {
    "id": 501,
    "domain_id": "dom_1",
    "source_address": "[email protected]",
    "destination_addresses": ["[email protected]"],
    "mode": "copy",
    "enabled": true,
    "status": "active",
    "comment": "",
    "updated_at": "2026-09-17T10:00:00+00:00"
  }
}
Errors
Errors
StatusCodeWhen
404not_foundThe 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
NameTypeAccessDefaultDescription
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".
enabledBooleanβ€”trueWhether the forwarder is active.
commentString|nullβ€”β€”An optional note, up to 160 characters.
curl -X POST "https://dash.mailbux.com/api/v1/forwarders" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "[email protected]",
    "destinations": ["[email protected]"],
    "mode": "forward_only",
    "enabled": true
  }'
{
  "data": {
    "id": 502,
    "domain_id": "dom_1",
    "source_address": "[email protected]",
    "destination_addresses": ["[email protected]"],
    "mode": "forward_only",
    "enabled": true,
    "status": "sync_pending",
    "comment": null,
    "updated_at": "2026-09-17T10:05:00+00:00"
  }
}
Errors
Errors
StatusCodeWhen
422validation_failedsource, destinations, mode or comment fails validation.
403system_forwarder_lockedsource's local part is "_catchall".
422domain_not_ownedsource's domain does not belong to this service.
422validation_faileda forward_only source has no mailbox, alias, or catch-all behind it to route through.
409forwarder_conflictthe mail server reports a conflict for this write.
502upstream_errorany 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.

Scope: forwarders:write

Parameters
Update a forwarder parameters
NameTypeAccessDefaultDescription
forwarder*Idβ€”β€”Must belong to this service, or the request 404s.
source*Stringβ€”β€”Same validation as Create.
destinations*String[]β€”β€”Same validation as Create.
mode*Stringβ€”β€”Same validation as Create.
enabledBooleanβ€”trueSame as Create.
commentString|nullβ€”β€”Same validation as Create.
curl -X PUT "https://dash.mailbux.com/api/v1/forwarders/501" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "[email protected]",
    "destinations": ["[email protected]", "[email protected]"],
    "mode": "copy",
    "enabled": true,
    "comment": "updated"
  }'
{
  "data": {
    "id": 501,
    "domain_id": "dom_1",
    "source_address": "[email protected]",
    "destination_addresses": ["[email protected]", "[email protected]"],
    "mode": "copy",
    "enabled": true,
    "status": "sync_pending",
    "comment": "updated",
    "updated_at": "2026-09-17T10:10:00+00:00"
  }
}
Errors
Errors
StatusCodeWhen
404not_foundthe forwarder id does not exist, or belongs to a different service.
403system_forwarder_lockedthe existing row is the system catch-all forwarder, or the new source would move it onto the reserved "_catchall" address.
422validation_failedvalidation fails, the domain is not owned, or the source is not routable β€” same conditions as Create.
409forwarder_conflictsame as Create.
502upstream_errorsame as Create.
Notes
  • Updating an id that does not exist 404s β€” it never creates one.
  • Direct service keys get 200 on success; a reseller sub-tenant's key gets 202.
  • Two updates to the same forwarder are not locked against each other on our side β€” the last write to reach the mail server wins.
DELETE/forwarders/{forwarder}

Delete a forwarder

Deletes a forwarder belonging to this service.

Scope: forwarders:write

Parameters
Delete a forwarder parameters
NameTypeAccessDefaultDescription
forwarder*Idβ€”β€”Must belong to this service, or the request 404s.
curl -X DELETE "https://dash.mailbux.com/api/v1/forwarders/501" \
  -H "Authorization: Bearer YOUR_API_KEY"
Errors
Errors
StatusCodeWhen
404not_foundthe forwarder id does not exist, or belongs to a different service.
403system_forwarder_lockedthe row is the system catch-all forwarder.
502upstream_errorthe 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.

urn:ietf:params:jmap:mailgetqueryset: create, update, destroychangesqueryChanges
Properties (8)
Mailbox properties
NameTypeAccessDefaultDescription
idIdserver-setβ€”The folder id.
name*Stringread-writeβ€”Display name.
parentIdId|nullread-writeβ€”The containing folder, or null for a top-level folder.
roleString|nullread-onlyβ€”A standard role such as inbox, sent, drafts, trash, archive or junk, or null for a custom folder.
sortOrderUnsignedIntread-writeβ€”Display ordering hint among sibling folders.
totalEmailsUnsignedIntread-onlyβ€”Message count in this folder.
unreadEmailsUnsignedIntread-onlyβ€”Unread message count.
myRightsObjectread-onlyβ€”The permissions your account has on this folder (read, add items, remove items, and so on).
POSTMailbox/query

List your mailboxes (folders)

Lists every folder in the mailbox, then fetches their details in the same request using a back-reference (see Mail server API basics).

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
    "methodCalls": [
      ["Mailbox/query", {"accountId": "<accountId>"}, "q"],
      ["Mailbox/get", {
        "accountId": "<accountId>",
        "#ids": {"resultOf": "q", "name": "Mailbox/query", "path": "/ids"}
      }, "g"]
    ]
  }'
{
  "methodResponses": [
    ["Mailbox/query", {"accountId": "<accountId>", "queryState": "<opaque>", "canCalculateChanges": false, "ids": ["<inboxId>", "<sentId>"]}, "q"],
    ["Mailbox/get", {
      "accountId": "<accountId>",
      "state": "<opaque>",
      "list": [
        {"id": "<inboxId>", "name": "Inbox", "parentId": null, "role": "inbox", "totalEmails": 128, "unreadEmails": 4},
        {"id": "<sentId>", "name": "Sent", "parentId": null, "role": "sent", "totalEmails": 57, "unreadEmails": 0}
      ],
      "notFound": []
    }, "g"]
  ]
}
Email

Email messages

Individual messages: list, filter, read, move between folders, flag, or delete.

urn:ietf:params:jmap:mailgetqueryset: create, update, destroychangesqueryChangescopyimportparse
Properties (10)
Email properties
NameTypeAccessDefaultDescription
idIdserver-setβ€”The message id.
mailboxIdsObject (Id -> true)read-writeβ€”The folder(s) this message is filed under.
keywordsObject (String -> true)read-writeβ€”Flags such as $seen, $flagged, $answered, $draft.
subjectStringread-onlyβ€”The message subject. Set at creation/import; not patched on an existing message.
fromEmailAddress[]read-onlyβ€”Sender address(es).
toEmailAddress[]read-onlyβ€”Recipient address(es).
receivedAtUTCDateTimeread-onlyβ€”When the message was received.
hasAttachmentBooleanread-onlyβ€”Whether the message has a non-inline attachment.
previewStringread-onlyβ€”A short plain-text preview of the body.
bodyValuesObjectread-onlyβ€”Requested body part contents, keyed by part id β€” only populated when you ask for it (see the worked example below).
Filters (1)
Email filters
NameTypeAccessDefaultDescription
inMailboxIdβ€”β€”Only messages filed in this folder.
POSTEmail/query

List emails in a folder

Returns matching message ids, newest first.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
    "methodCalls": [["Email/query", {
      "accountId": "<accountId>",
      "filter": {"inMailbox": "<mailboxId>"},
      "sort": [{"property": "receivedAt", "isAscending": false}],
      "limit": 20
    }, "a"]]
  }'
{
  "methodResponses": [
    ["Email/query", {"accountId": "<accountId>", "queryState": "<opaque>", "canCalculateChanges": false, "position": 0, "ids": ["<emailId>"]}, "a"]
  ]
}
POSTEmail/get

Read an email's contents

Fetches one message with its plain-text body β€” request bodyValues explicitly, or you only get metadata.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
    "methodCalls": [["Email/get", {
      "accountId": "<accountId>",
      "ids": ["<emailId>"],
      "properties": ["subject", "from", "to", "receivedAt", "bodyValues", "textBody"],
      "fetchTextBodyValues": true
    }, "a"]]
  }'
{
  "methodResponses": [
    ["Email/get", {
      "accountId": "<accountId>",
      "state": "<opaque>",
      "list": [{
        "id": "<emailId>",
        "subject": "Your quote",
        "from": [{"name": "Sales", "email": "[email protected]"}],
        "to": [{"name": "", "email": "[email protected]"}],
        "receivedAt": "2026-01-01T09:00:00Z",
        "textBody": [{"partId": "1", "type": "text/plain"}],
        "bodyValues": {"1": {"value": "Thanks for your interest.", "isEncodingProblem": false, "isTruncated": false}}
      }],
      "notFound": []
    }, "a"]
  ]
}
Errors
Errors
StatusCodeWhen
β€”notFoundthe id does not exist, or belongs to another mailbox your key cannot reach
Thread

Threads

The set of message ids that make up one conversation.

urn:ietf:params:jmap:mailgetchanges
Properties (2)
Thread properties
NameTypeAccessDefaultDescription
idIdserver-setβ€”The thread id, referenced from Email.threadId.
emailIdsId[]read-onlyβ€”Every message in the thread, oldest first.
POSTThread/get

Get a thread

Fetches the message ids that make up a conversation.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
    "methodCalls": [["Thread/get", {"accountId": "<accountId>", "ids": ["<threadId>"]}, "a"]]
  }'
{
  "methodResponses": [
    ["Thread/get", {"accountId": "<accountId>", "state": "<opaque>", "list": [{"id": "<threadId>", "emailIds": ["<emailId1>", "<emailId2>"]}], "notFound": []}, "a"]
  ]
}
SearchSnippet

Search snippets

Highlighted excerpts around the matching text in a search result, for showing "why this email matched" in a UI.

urn:ietf:params:jmap:mailget
Properties (3)
SearchSnippet properties
NameTypeAccessDefaultDescription
emailIdIdread-onlyβ€”The message this snippet is for.
subjectString|nullread-onlyβ€”Subject with matches highlighted, or null if the subject did not match.
previewString|nullread-onlyβ€”Body excerpt with matches highlighted, or null if the body did not match.
POSTSearchSnippet/get

Get highlighted snippets for a search

Takes the same filter you used on Email/query plus the resulting ids, and returns highlighted excerpts.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
    "methodCalls": [["SearchSnippet/get", {
      "accountId": "<accountId>",
      "filter": {"text": "invoice"},
      "emailIds": ["<emailId>"]
    }, "a"]]
  }'
{
  "methodResponses": [
    ["SearchSnippet/get", {"accountId": "<accountId>", "list": [{"emailId": "<emailId>", "subject": null, "preview": "...your <mark>invoice</mark> is attached..."}], "notFound": []}, "a"]
  ]
}
Identity

Sending identities

The addresses this mailbox may send mail as, used by EmailSubmission below.

urn:ietf:params:jmap:submissiongetchangesset
Properties (6)
Identity properties
NameTypeAccessDefaultDescription
idIdserver-setβ€”The identity id.
nameStringread-writeβ€”Display name used in the From header.
emailStringread-onlyβ€”One of this mailbox's own addresses or aliases.
replyToEmailAddress[]|nullread-writeβ€”Reply-To override.
textSignatureStringread-writeβ€”Plain-text signature.
mayDeleteBooleanread-onlyβ€”Whether this identity can be deleted (the primary identity typically cannot).
POSTIdentity/get

List your sending identities

Returns every address this mailbox can send as β€” you need an identity id to send mail (see EmailSubmission below).

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:submission"],
    "methodCalls": [["Identity/get", {"accountId": "<accountId>", "ids": null}, "a"]]
  }'
{
  "methodResponses": [
    ["Identity/get", {
      "accountId": "<accountId>",
      "state": "<opaque>",
      "list": [{"id": "<identityId>", "name": "Sales", "email": "[email protected]", "replyTo": null, "mayDelete": false}],
      "notFound": []
    }, "a"]
  ]
}
EmailSubmission

Sending mail

How a message actually gets sent β€” create an Email, then submit it with an EmailSubmission pointing at it and an Identity to send from.

urn:ietf:params:jmap:submissiongetqueryset: create, update, destroychangesqueryChanges
Properties (6)
EmailSubmission properties
NameTypeAccessDefaultDescription
idIdserver-setβ€”The submission id.
emailId*Idcreate-onlyβ€”The Email to send β€” usually one you just created in the same request.
identityId*Idcreate-onlyβ€”Which of your identities to send as (see Identity above).
envelopeObject|nullcreate-onlyβ€”Override the SMTP envelope (MAIL FROM / RCPT TO); omit to derive it from the message headers.
sendAtUTCDateTimeserver-setβ€”When the message was (or will be) sent.
undoStatusStringread-onlyβ€”pending, final or canceled.
POSTEmailSubmission/set

Send an email

Creates a draft Email and submits it for sending in one request, using a creation-id reference ("#e1") instead of a second round trip.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:submission"],
    "methodCalls": [
      ["Email/set", {
        "accountId": "<accountId>",
        "create": {
          "e1": {
            "mailboxIds": {"<draftsMailboxId>": true},
            "from": [{"email": "[email protected]"}],
            "to": [{"email": "[email protected]"}],
            "subject": "Your quote",
            "bodyValues": {"body": {"value": "Thanks for your interest.", "charset": "utf-8"}},
            "textBody": [{"partId": "body", "type": "text/plain"}]
          }
        }
      }, "a"],
      ["EmailSubmission/set", {
        "accountId": "<accountId>",
        "create": {
          "s1": {"emailId": "#e1", "identityId": "<identityId>"}
        }
      }, "b"]
    ]
  }'
{
  "methodResponses": [
    ["Email/set", {"accountId": "<accountId>", "created": {"e1": {"id": "<emailId>"}}, "notCreated": {}}, "a"],
    ["EmailSubmission/set", {"accountId": "<accountId>", "created": {"s1": {"id": "<submissionId>", "sendAt": "2026-01-01T09:00:01Z", "undoStatus": "final"}}, "notCreated": {}}, "b"]
  ]
}
Errors
Errors
StatusCodeWhen
β€”invalidPropertiesemailId or identityId is missing
β€”forbiddenidentityId does not belong to this account
β€”notFoundthe referenced emailId does not exist
Notes
  • A rejected submission looks like "notCreated": {"s1": {"type": "invalidProperties", "properties": ["identityId"]}}.
VacationResponse

Vacation responder (out-of-office)

One singleton object per mailbox controlling the automatic out-of-office reply.

urn:ietf:params:jmap:vacationresponsegetset: update only (singleton)
Properties (6)
VacationResponse properties
NameTypeAccessDefaultDescription
idIdread-onlyβ€”Always the fixed value "singleton".
isEnabled*Booleanread-writeβ€”Whether the auto-reply is active.
fromDateUTCDateTime|nullread-writeβ€”Start of the active window, or null for no start bound.
toDateUTCDateTime|nullread-writeβ€”End of the active window, or null for no end bound.
subjectString|nullread-writeβ€”Auto-reply subject.
textBodyString|nullread-writeβ€”Auto-reply plain-text body.
POSTVacationResponse/set

Set your out-of-office reply

Only update on the fixed id "singleton" is accepted β€” create and destroy are rejected with a singleton error.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:vacationresponse"],
    "methodCalls": [["VacationResponse/set", {
      "accountId": "<accountId>",
      "update": {"singleton": {
        "isEnabled": true,
        "fromDate": "2026-08-01T00:00:00Z",
        "toDate": "2026-08-14T00:00:00Z",
        "subject": "Out of office",
        "textBody": "I am away and back on August 14."
      }}
    }, "a"]]
  }'
{
  "methodResponses": [
    ["VacationResponse/set", {"accountId": "<accountId>", "updated": {"singleton": null}, "notUpdated": {}}, "a"]
  ]
}
Errors
Errors
StatusCodeWhen
β€”singletonthe request used create or destroy, or an id other than "singleton"
Notes
  • To turn it off, send {"update": {"singleton": {"isEnabled": false}}} rather than destroying the object.
SieveUserScript

Your mailbox filters (Sieve scripts)

Your own mail-filtering rules for this mailbox β€” unrelated to Forwarders, which are system-managed rules your key cannot write directly.

urn:ietf:params:jmap:sievegetset: create, update, destroyquery
Properties (4)
SieveUserScript properties
NameTypeAccessDefaultDescription
idIdserver-setβ€”The script id.
name*Stringread-writeβ€”A name you choose for the script.
isActiveBooleanread-writefalseWhether this script is the one currently applied to incoming mail. Only one script can be active at a time.
blobId*Idcreate-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
NameTypeAccessDefaultDescription
nameStringβ€”β€”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.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:sieve"],
    "methodCalls": [["SieveUserScript/get", {"accountId": "<accountId>", "ids": null}, "a"]]
  }'
{
  "methodResponses": [
    ["SieveUserScript/get", {
      "accountId": "<accountId>",
      "state": "<opaque>",
      "list": [{"id": "<scriptId>", "name": "vacation-team", "isActive": true}],
      "notFound": []
    }, "a"]
  ]
}
POSTSieveUserScript/set

Upload and activate a filter script

Upload the Sieve source as a blob first (Content-Type application/sieve), then create the script from that blob id.

# 1) upload the script body (see Upload a blob in Mail server API basics)
curl -X POST "https://dash.mailbux.com/api/v1/mail/upload/<accountId>" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/sieve" \
  --data-binary @filters.sieve
# -> {"accountId": "<accountId>", "blobId": "<blobId>", "type": "application/sieve", "size": 214}

# 2) create the script from that blob, and make it active
curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:sieve"],
    "methodCalls": [["SieveUserScript/set", {
      "accountId": "<accountId>",
      "create": {"new": {"name": "vacation-team", "blobId": "<blobId>", "isActive": true}}
    }, "a"]]
  }'

# delete: ["SieveUserScript/set", {"accountId": "<accountId>", "destroy": ["<scriptId>"]}, "a"]
{
  "methodResponses": [
    ["SieveUserScript/set", {"accountId": "<accountId>", "created": {"new": {"id": "<scriptId>"}}, "notCreated": {}}, "a"]
  ]
}
Errors
Errors
StatusCodeWhen
β€”invalidPropertiesblobId does not point at a valid uploaded script, or name is missing
β€”notFounddestroy 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.

urn:ietf:params:jmap:quotagetquerychangesqueryChanges
Properties (5)
Quota properties
NameTypeAccessDefaultDescription
idIdserver-setβ€”The quota id.
resourceTypeStringread-onlyβ€”What is being measured, e.g. "count" or "octets" (bytes).
usedUnsignedIntread-onlyβ€”Current usage, in the unit given by resourceType.
hardLimitUnsignedIntread-onlyβ€”The limit β€” a write that would exceed it fails with overQuota (see Mail server API basics).
scopeStringread-onlyβ€”What the quota applies to, e.g. "account".
POSTQuota/get

Read your storage usage

Returns usage and limit for this mailbox.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:quota"],
    "methodCalls": [["Quota/get", {"accountId": "<accountId>", "ids": null}, "a"]]
  }'
{
  "methodResponses": [
    ["Quota/get", {
      "accountId": "<accountId>",
      "state": "<opaque>",
      "list": [{"id": "<quotaId>", "resourceType": "octets", "used": 524288000, "hardLimit": 5368709120, "scope": "account"}],
      "notFound": []
    }, "a"]
  ]
}
Blob

Blob metadata

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.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["Blob/get", {"accountId": "<accountId>", "ids": ["<blobId>"], "properties": ["size"]}, "a"]]
  }'
{
  "methodResponses": [
    ["Blob/get", {"accountId": "<accountId>", "list": [{"id": "<blobId>", "size": 20480}], "notFound": []}, "a"]
  ]
}
Errors
Errors
StatusCodeWhen
β€”notFoundthe 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
NameTypeAccessDefaultDescription
idIdserver-setβ€”The subscription id.
deviceClientId*Stringcreate-onlyβ€”An id you choose to recognize this device/registration later.
url*Stringcreate-onlyβ€”Your push endpoint URL.
keysObjectcreate-onlyβ€”Encryption keys for the push payload (Web Push p256dh/auth), if your endpoint needs them.
expiresUTCDateTime|nullread-writeβ€”When the subscription expires.
typesString[]|nullread-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.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["PushSubscription/set", {
      "create": {"new": {
        "deviceClientId": "device-123",
        "url": "https://push.example.com/endpoint/abc",
        "types": ["Email", "Mailbox"]
      }}
    }, "a"]]
  }'
{
  "methodResponses": [
    ["PushSubscription/set", {"created": {"new": {"id": "<subscriptionId>", "expires": "2026-04-01T00:00:00Z"}}, "notCreated": {}}, "a"]
  ]
}
Errors
Errors
StatusCodeWhen
β€”invalidPropertiesdeviceClientId or url is missing
Principal

Directory principals

Directory discovery for CalDAV/CardDAV sharing β€” not mailbox management (that is x:Account in Mailboxes & access).

urn:ietf:params:jmap:principalsgetquerychangesqueryChangesgetAvailabilityset
  • 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.

urn:ietf:params:jmap:calendarsCalendar: get, set, query, changes, queryChangesCalendarEvent: get, set, query, changes, queryChanges, copy, parseCalendarEventNotification: get, set, changes, query, queryChangesParticipantIdentity: get, changes, set
  • 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.
AddressBook / ContactCard

Contacts

JSContact-style address books and contact cards.

urn:ietf:params:jmap:contactsAddressBook: get, set, changes, create, update, destroyContactCard: get, changes, query, queryChanges, set, copy, parse
  • 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:

GroupSystem-only object types
Tenant & role administrationx:Tenant, x:Role
Credentials & TLSx:Certificate, x:AcmeProvider, x:AllowedIp, x:BlockedIp, x:OAuthClient, x:OidcProvider, x:PublicKey, x:Authentication, x:Security
Sieve & forwardersx:SieveSystemScript, x:SieveSystemInterpreter, x:SieveUserInterpreter, x:SieveUserScript
Server actionsx:Action
Reportsx:TlsInternalReport, x:TlsExternalReport, x:DmarcInternalReport, x:DmarcExternalReport, x:ArfExternalReport, x:DkimReportSettings, x:DmarcReportSettings, x:DsnReportSettings, x:ReportSettings, x:SpfReportSettings, x:TlsReportSettings
Delivery queue & tracingx:QueuedMessage, x:ArchivedItem, x:Trace
Other server configurationx:AiModel, x:Application, x:Directory, x:MaskedEmail, x:Sharing, x:AddressBook, x:Calendar, x:CalendarAlarm, x:CalendarScheduling, x:Email, x:Enterprise, x:Search, x:SenderAuth, x:SystemSettings
Observabilityx:Alert, x:Log, x:Metric, x:WebHook, x:Metrics
Cluster & coordinationx:ClusterNode, x:ClusterRole, x:EventTracingLevel, x:Task, x:Bootstrap, x:Coordinator, x:TaskManager
Networking & protocolsx:DnsServer, x:NetworkListener, x:Asn, x:DnsResolver, x:Http, x:HttpForm, x:Imap, x:Jmap, x:WebDav
Storage & lookup backendsx:HttpLookup, x:MemoryLookupKey, x:MemoryLookupKeyValue, x:StoreLookup, x:BlobStore, x:Cache, x:DataRetention, x:DataStore, x:FileStorage, x:InMemoryStore, x:MetricsStore, x:SearchStore, x:TracingStore
MTA / routing configx:MtaConnectionStrategy, x:MtaDeliverySchedule, x:MtaHook, x:MtaInboundThrottle, x:MtaMilter, x:MtaOutboundStrategy, x:MtaOutboundThrottle, x:MtaQueueQuota, x:MtaRoute, x:MtaStageAuth, x:MtaStageConnect, x:MtaStageData, x:MtaStageEhlo, x:MtaStageMail, x:MtaStageRcpt, x:MtaSts, x:MtaTlsStrategy, x:MtaVirtualQueue, x:MtaInboundSession, x:MtaExtensions
Spam filterx:SpamDnsblServer, x:SpamFileExtension, x:SpamRule, x:SpamTag, x:SpamTrainingSample, x:SpamClassifier, x:SpamDnsblSettings, x:SpamLlm, x:SpamPyzor, x:SpamSettings

A naming overlap worth knowing

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.

{
  "data": {
    "id": 4821,
    "label": "Example Mail",
    "status": "active",
    "tenant_id": "b9b8...",
    "scopes": ["service:read", "forwarders:read"],
    "api_version": "v1"
  }
}
Notes
  • 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.

{
  "data": {
    "state": "ready",
    "observed_at": "2026-09-17T10:00:00+00:00",
    "storage": {
      "capacity_bytes": 5368709120,
      "allocated_bytes": 5368709120,
      "used_bytes": 219043020
    },
    "mailboxes": { "user": 4, "shared": 1, "limit": 10 },
    "domains": { "count": 2, "limit": 5 }
  }
}
Notes
  • 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.

{
  "data": {
    "observed_at": "2026-09-17T10:00:00+00:00",
    "state": "ready",
    "capacity_bytes": 5368709120,
    "allocated_bytes": 5368709120,
    "used_bytes": 219043020,
    "base_gb": 5.0,
    "effective_gb": 15.0,
    "addons": [
      { "id": 4901, "status": "active", "gb": 10.0, "lifecycle": "active" }
    ]
  }
}
Notes
  • base_gb, effective_gb and addons are null/empty for a service that is not on an LTD plan.
GET/mailboxes

List mailboxes

List your mailboxes with storage use and allocated quota.

Deprecated

Scope: mailboxes:read

Sunset 2026-10-17. Use x:Account/query then x:Account/get β€” see Mailboxes & access. instead.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:Account/query", {"accountId": "YOUR_ACCOUNT_ID"}, "0"]]
  }'
{
  "data": [
    {
      "id": "act_1",
      "address": "[email protected]",
      "kind": "user",
      "used_bytes": 10485760,
      "allocated_bytes": 1073741824
    }
  ]
}
Notes
  • Directly confirmed: this endpoint reads the same x:Account objects the replacement calls return.
GET/domains

List domains

List the domains attached to this service, with activation state and cached verification status.

Deprecated

Scope: domains:read

Sunset 2026-10-17. Use x:Domain/query then x:Domain/get for id, name and enabled state β€” see Domains & DNS. instead.

curl -X POST "https://dash.mailbux.com/api/v1/mail" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["x:Domain/query", {"accountId": "YOUR_ACCOUNT_ID"}, "0"]]
  }'
{
  "data": [
    { "id": "dom_1", "name": "example.com", "is_enabled": true, "verification_status": "verified" }
  ]
}
Notes
  • 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.

{
  "data": [
    { "address": "[email protected]", "domain_id": "dom_1", "mailbox_id": "act_1", "mailbox_address": "[email protected]" }
  ]
}
Notes
  • 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.

{
  "data": [
    {
      "domain_id": "dom_1",
      "domain": "example.com",
      "checked_at": "2026-09-17T10:00:00+00:00",
      "dkim_source": "signature",
      "dkim_unavailable": false,
      "records": [
        { "type": "MX", "status": "pass" }
      ]
    }
  ]
}
Notes
  • 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.

{
  "data": [
    { "domain_id": "dom_1", "domain": "example.com", "selector": "default", "public_key": "v=DKIM1; k=rsa; p=...", "stage": "", "source": "signature" }
  ]
}
Notes
  • The private key is never returned by this endpoint or by x:DkimSignature β€” only the public key and selector.

Ready to Host Your Business Email for Free?

Set up professional email on your own domain in minutes. Free business email hosting, powered by Mailbux.