Domains
`domains.list`, `list_all`, `iterate`, `get` and `update`.
Every method
page = client.domains.listpage.items.each { |row| puts "#{row[:domain]} #{row.dig(:sending, :canSend)}" } domain = client.domains.get("b3e1f0a4-6c2d-4e8a-9f17-2d5c8a0b4e6f")puts domain.dig(:receiving, :verified), domain.dig(:sending, :status) domain[:addresses].each do |entry| puts "#{entry[:address]} #{entry[:enabled]}"endReceiving and sending are two independent facts and are returned as two Hashes. 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, read as domain.dig(:sending, :canSend), rather than on status.
list returns one OpenEmail::Page of domains in alphabetical order, and list_all returns them all in one Array. iterate yields them one at a time to a block. Without a block it returns an Enumerator.
domain_id = "b3e1f0a4-6c2d-4e8a-9f17-2d5c8a0b4e6f" updated = client.domains.update(domain_id, trackingHost: "links.acme.com")puts updated.dig(:tracking, :status), updated.dig(:tracking, :record, :name), updated.dig(:tracking, :record, :value) client.domains.update(domain_id, trackingHost: nil)update sets, checks again or removes the domain’s custom tracking domain, a subdomain such as links.acme.com, and returns the same Hash 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: the addresses this key may put in a From header, which is narrower, each with a canSend verdict. It returns an OpenEmail::AddressBookPage, which holds them in addresses rather than items, beside domains and unrestricted. Its list_all returns one OpenEmail::AddressBook.
app_host is a namespace of its own, client.app_host. get, set, verify and delete read and change the web app address of the workspace, a subdomain such as mailbox.acme.com on one of these domains or any other domain the workspace controls, where its people sign in under the workspace brand. set returns the DNS records to publish, in record and, on a domain outside the workspace, ownershipRecord. delete, and a set that replaces an address, ask an OAuth app for a verification code: until it has one, the call raises a 403 whose step_up_required? is true.
branding sets that brand. get reads the links to the mark, the logo, the logo for dark mode and the sign-in photo, the two fonts and the sign-in background. update changes the fonts and the background, upload_image(variant, data, content_type: nil) uploads one of the four images, and remove_image(variant) removes one. variant is mark, wordmark, wordmark-dark or login-background, and OpenEmail::BRAND_IMAGE_VARIANTS names them. data is a binary String, an IO or a Pathname. A Pathname such as Pathname("logo.svg"), a File or a Rails upload brings its type with it. Other bytes need content_type:, and an image without a type is refused with a 422 invalid_image. The logo is what brands the web app address and, on a paid plan, the emails sent for the workspace.
Parameters: domains.get
idStringrequired- 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 workspace as well as to the id, so another workspace’s domain is a 404, raised as `OpenEmail::NotFoundError`, rather than a 403. A nil or empty id raises ArgumentError before anything is sent.
Parameters: domains.update
idStringrequired- The same domain id `get` takes. `domains:write` is the scope it needs.
trackingHostString or nil- 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. Pass nil or an empty String to remove the tracking domain, and leave the field out to leave it alone.
A refused host raises an OpenEmail::ApiError naming trackingHost in param: a 422 invalid_tracking_host for a name that cannot be used, such as one outside the domain, a 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 a 409 tracking_host_in_use for a name another domain already uses, or when the tracking domain is managed by a different OpenEmail server. The 422 arrives as OpenEmail::ValidationError and each 409 as OpenEmail::ConflictError. A key limited to specific addresses gets a 422 capability_unsupported, because a tracking domain applies to every address on the domain.
The patch is keyword arguments or one Hash, and its fields keep the API’s camelCase names, so tracking_host: is sent as written and refused with a 422 unknown_parameter. update also takes catchAll, storageHost for a files domain such as files.acme.com, and dmarcPolicy. Every field is optional and the method reference covers each one. The gem retries update like a read, because a repeat finds the host already set and at most checks it again.
Response: a domain (domains.get)
objectString- 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 or nil- When verification passed, as an ISO 8601 String. It is nil 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. It is 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 or nil- When DNS was last asked about this domain. It is nil when DNS was never asked, which reads very differently from a failure to somebody who added a domain a minute ago. Reading an unverified domain asks DNS again once the last check is more than 20 seconds old, so polling `get` is one way to wait for verification, and `verify` checks straight away.
receiving.errorString or nil- 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. It is nil once the check passes, and it is stored rather than derived, so a reload and the scheduled re-check say the same thing.
sending.statusString- The outbound signing state as the last check saw it: `verified`, `pending`, `failed`, `no_identity` or `unknown`. It is read from the stored check, 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 or nil- When the signing state was last checked, as an ISO 8601 String. It is nil when it never was, which reads very differently from a failure.
sending.errorString or nil- The last signing failure in words, or nil 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. It is prose for a human to read, so branch on `sending.canSend` rather than on this.
trackingHash- The domain’s custom tracking domain, on `list` rows and on this one alike, and what `update` changes.
tracking.hostString or nil- The tracking domain, such as `links.acme.com`, or nil when none is set.
tracking.statusString- `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. It is an empty String while `host` is nil, and while the address for a new host is still being prepared.
tracking.recordHash or nil- The record to publish, a Hash with `type` (always `CNAME`), `name` and `value`, named after `host` with `target` as its value. It is nil when there is no tracking domain, and while the address for a new host is still being prepared, so `dig(:tracking, :record, :value)` reads it safely.
tracking.checkedAtString or nil- When the host was last checked, as an ISO 8601 String. It is nil until the first check.
tracking.verifiedAtString or nil- When a check last passed, as an ISO 8601 String. It is nil for a host that has never passed one.
tracking.errorString or nil- What the last check found, in words the domain owner can act on. It is nil 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<Hash>- 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, as an ISO 8601 String. It is not when the domain verified: that is `receiving.verifiedAt`, which can be nil while this is set.