Skip to the documentation
SDK

Domains

`domains.list`, `get` and `update`.

Every method

usage.ts
const domains = await openemail.domains.list()const domain = await openemail.domains.get('b3e1f0a4-6c2d-4e8a-9f17-2d5c8a0b4e6f') console.log(domain.receiving.verified, domain.sending.status)for (const address of domain.addresses) console.log(address.address, address.enabled) const updated = await openemail.domains.update(domain.id, { trackingHost: 'links.acme.com' })console.log(updated.tracking.status, updated.tracking.record?.name, updated.tracking.record?.value) await openemail.domains.update(domain.id, { trackingHost: null })

Receiving and sending are two independent facts and are returned as two objects. receiving.verified means the domain’s MX brings its mail here and its ownership challenge is published. sending reports the outbound signing check: status is verified, pending, failed, no_identity or unknown, and canSend says whether a send from the domain would be accepted right now. A negative verdict older than a day is treated as unknown rather than as a refusal, so branch on canSend rather than on status.

update sets, checks again or removes the domain’s custom tracking domain, a subdomain such as links.acme.com, and resolves to the same DomainDetailResource as get. tracking reports it on every read. Until a check passes, tracking.status is pending and tracked links and the open pixel keep using the default OpenEmail host. Once one passes, it is active and new mail from the domain uses the tracking domain for both.

get also lists the addresses on the domain. addresses.list() is the related call: every address THIS KEY may put in a From header, which is narrower.

Parameters: domains.get

domainIdstringrequired
The id from `domains.list`, a UUID minted when the domain was added, not the hostname, so `get('example.com')` finds nothing. The lookup is scoped to the key's own connection as well as to the id, so another workspace's domain is a 404 rather than a 403.

Parameters: domains.update

idstringrequired
The same domain id `get` takes. `domains:write` is the scope it needs.
patch.trackingHoststring | nullrequired
A subdomain of the domain, at most 512 characters, such as `links.acme.com`. It is trimmed and lowercased, and a leading `https://` or `http://`, a path and a trailing dot are stripped. A new value is validated, saved and checked in the same call. The value the domain already has runs the check again, unless the last one was less than 30 seconds ago. `null` or an empty string removes the tracking domain.

A refused host throws an OpenEmailApiError naming trackingHost in param: 422 invalid_tracking_host for a name that cannot be used, such as one outside the domain, 409 domain_not_verified for a new host while receiving.verified is false and the domain’s _openemail-challenge TXT record is not published yet, and 409 tracking_host_in_use for a name another domain already uses, or when the tracking domain is managed by a different OpenEmail server. A key limited to specific addresses gets 422 capability_unsupported, because a tracking domain applies to every address on the domain.

Response: DomainDetailResource

object'domain'
Always the string `domain`, on `list` rows and on this one alike.
idstring
The domain's UUID. Stable for the life of the row, and the only handle the other domain calls accept.
domainstring
The bare hostname, lowercased: `example.com`. Unique across the whole product, one owner per domain, so two workspaces cannot both claim it.
receiving.verifiedboolean
True once DNS showed the domain’s MX naming a host that brings its mail here and, where the row carries a challenge token, the matching `_openemail-challenge` TXT record. MX alone proves nothing, since every domain we receive for publishes the same hostnames, which is why the token exists, and why this flag is the gate inbound delivery checks before accepting mail.
receiving.verifiedAtstring | null
When verification passed, ISO-8601. Null while it has not, and `verified` is derived from exactly this column, so the two can never disagree.
receiving.catchAllboolean
Whether any local-part is accepted. On by default for domains added since this became the rule; with it off only addresses named on the domain are accepted and the rest are rejected at SMTP time, so the sender gets a bounce rather than silence.
receiving.lastCheckedAtstring | null
When DNS was last asked about this domain. Null means never looked, which reads very differently from a failure to somebody who added a domain a minute ago. This endpoint reports the stored result, never running a check of its own.
receiving.errorstring | null
Why the last check did not pass, in words the owner can act on: `No MX records yet. DNS changes can take a few minutes to spread.` is a typical one. Null once it passes, and stored rather than derived so a reload and the scheduled re-check say the same thing.
sending.status'verified' | 'pending' | 'failed' | 'no_identity' | 'unknown'
The outbound signing state as the last check saw it. Read from the stored check rather than probed on this request, so `sending.checkedAt` says how old it is.
sending.canSendboolean
Whether a send from this domain would be accepted right now. A negative verdict older than a day is treated as unknown rather than as a refusal, so this can be true while `status` is `pending`. Branch on this before a send: a false one means `emails.send` from this domain is refused with a 409 `domain_not_sendable`.
sending.checkedAtstring | null
When the signing state was last checked, ISO-8601. Null means never, which reads very differently from a failure.
sending.errorstring | null
The last signing failure in words, or null once it passes.
sending.notestring
One of five sentences, chosen by `sending.status`, saying what that state means in words a domain owner can act on. Prose for a human to read. Branch on `sending.canSend` rather than on this.
trackingDomainTracking
The domain’s custom tracking domain, on `list` rows and on this one alike, and what `update` changes.
tracking.hoststring | null
The tracking domain, such as `links.acme.com`, or null when none is set.
tracking.status'none' | 'pending' | 'active' | 'failed'
`none` means no tracking domain is set, `pending` means it has never passed a check, `active` means new mail uses it, and `failed` means it passed before and has since dropped out of use. An active host drops out after three failed checks in a row, or once its last passed check is more than 2 hours old.
tracking.activeboolean
True exactly when `status` is `active`, which is when tracked links and the open pixel in new mail from the domain use the host.
tracking.targetstring
The address the CNAME record points at, prepared for this tracking domain alone. An empty string while `host` is null, and while the address for a new host is still being prepared.
tracking.record{ type: 'CNAME'; name: string; value: string } | null
The record to publish, named after `host` with `target` as its value. Null when there is no tracking domain, and while the address for a new host is still being prepared.
tracking.checkedAtstring | null
When the host was last checked, ISO-8601. Null until the first check.
tracking.verifiedAtstring | null
When a check last passed, ISO-8601. Null for a host that has never passed one.
tracking.errorstring | null
What the last check found, in words the domain owner can act on. Null when the last check passed or none has run yet. A host that has failed one or two checks is still `active` and carries the reason here.
addressesArray<{ address: string; enabled: boolean }>
Every address row on the domain, which is what `get` adds over a `list` row. It includes the rows delivery wrote itself under catch-all, and those stop being accepted the moment catch-all is turned off, so the array is not a list of what will receive.
addresses[].addressstring
The full address, rebuilt from the stored local-part and the hostname and lowercased, so it always matches the `domain` above rather than drifting from it.
addresses[].enabledboolean
False disables the address, and a disabled one is rejected even when catch-all is on. Every row is listed either way, so filter on this rather than reading the array as the set of working addresses.
createdAtstring
When the domain row was added, ISO-8601. Not when it verified: that is `receiving.verifiedAt`, which can be null while this is set.