Skip to main content
Get testing addresses, inject or wait for messages, read their bodies, OTP codes, and links, and manage the domains they arrive on. For how testing mailboxes work, see Email testing.
These commands manage testing mailboxes that receive mail. To have DevHelm send alert emails, use an email alert channel: see alert-channels.

Setup

The testing API needs an API key and a workspace ID. Export the key, look up the workspace, then export its ID:
The CLI and Python SDK read both variables. The TypeScript SDK reads DEVHELM_WORKSPACE_ID and takes the key in its constructor. With curl, send the workspace as a header:
--api-token and --api-url override DEVHELM_API_TOKEN and DEVHELM_API_URL per command. There is no workspace flag. Without DEVHELM_WORKSPACE_ID the CLI sends workspace 1 and fails with 404 Workspace doesn't exist. Every command takes -o table|json|yaml (default table) and -v. JSON and YAML print the bare object, without the API’s data envelope. Message commands take the mailbox as --to <address>, the full address (local@domain). The domain part selects the mail domain; the local part selects the mailbox.

Commands

email address

Print a fresh address. Any local part on a domain receives mail, so no mailbox is created; use one address per test.
CLI
  • Table output prints the address only. JSON prints {email, domain, localPart}.
  • Without --domain, the CLI lists the workspace’s domains and uses the active assigned one, creating it if there is none. No call is made for the address itself.
  • The domain cap counts every domain in the organization. On Free or Developer, a second workspace gets 403 INBOUND_EMAIL_DOMAIN_LIMIT_REACHED.
  • Read the domain from this output or email domains list. Do not build it by hand.

email receive

Inject a message without a mail provider. It runs the same parsing as real mail, including OTP codes and links.
CLI
  • The API answers 202 with eventId, inbox, and receivedAt: accepted, not yet stored. Use email wait to see the message.
  • The CLI has no headers flag. To set Message-ID, Date, or custom headers, inject through the SDK or the API.
  • Inject cannot add attachments.
  • A disabled domain returns 400 INBOUND_INJECT_DOMAIN_DISABLED. A custom domain still pending DNS accepts inject.

email wait

Block until a message to the address has been stored, then print it.
CLI
Always pass --received-after with a time taken before your app sends the mail. The default is the local clock when wait starts, so a message that arrived first is skipped and the wait times out.
  • Wait returns the oldest match and does not consume it. For the next message, pass the previous receivedAt plus 1 ms.
  • The result has otp, links, bodyPreview, and headers. text and html are null; run email messages get with the message ID for the body.
  • The CLI matches the full address only. To wait on a local part plus a domain, use the SDK or the API.
  • With no match before the timeout, the CLI prints No matching inbound item arrived before timeout (<address>) and exits with code 11. A wait on a disabled domain times out the same way.
  • All waits in the organization share 40 requests per minute, across email, inboxes, and every workspace. Over the limit returns 429.
Read the code from the JSON with jq -r '.otp[0].value' when the email has one code. See Wait for an email in a test and Read OTP codes and links.

email clear

Delete every message for one address, in teardown.
CLI
The CLI cannot delete a single message. Use message.delete() in the SDKs, or DELETE /api/v1/email/domains/<domain>/messages/<message-id>.

email messages list

List messages for one address, newest first. The table shows ID, FROM, SUBJECT, OTP, and RECEIVED.
CLI
The CLI prints the rows of one page and not the next cursor. Page through with the SDK or the API. As with wait, text and html are null in list rows.

email messages get

Get one message. Use -o json for the body; the table shows only the summary columns.
CLI
  • text and html are clipped at 256 KB; bodyTruncated says whether they were cut.
  • html is returned unsanitized. Render it only in a sandboxed frame.
  • links[].href keeps &amp; from the HTML today. Replace &amp; with & before you open a link.

email messages source

Print the stored message as RFC822 text, clipped at 256 KB. Table output prints the source and warns when it was cut; JSON prints {source, truncated}.
CLI

email messages raw

Download the full message as .eml. The signed link lasts 60 seconds; the CLI downloads it at once. The file’s SHA-256 matches the message’s sha256.
CLI

email attachments get

Download one attachment by ID.
CLI
Inject cannot add attachments, and the attachments list is not filled for real mail today.

email domains list

List the workspace’s mail domains. The table shows NAME, KIND (assigned or custom), STATUS, and MX.
CLI

email domains get

Table output prints the DNS records; -o json prints the whole domain, including status and each record’s found.
CLI

email domains create

Create a custom domain and print the two DNS records to publish: a TXT record at _devhelm-verification.<domain> and an MX record to mx.devhelmmail.com at priority 10.
CLI
  • Custom domains need the Team testing plan or above. Otherwise create returns 403 INBOUND_CUSTOM_MX_PLAN_REQUIRED.
  • The domain starts as pending_dns.
  • create makes custom domains only. The assigned domain comes from email address.
See Receive on your own domain.

email domains verify

Check the DNS records now. Prints <name> <status>, then any records that still do not match.
CLI
When both records match, the domain becomes active. Verify also re-enables a disabled custom domain whose records match.

email domains update

Disable a domain. disabled is the only status the CLI accepts.
CLI
A disabled domain refuses inject, and waits on it time out. To turn it back on, run email domains verify for a custom domain. For the assigned domain, PATCH {"status": "active"} through the SDK or the API.

email domains delete

Delete a domain and every message on it.
CLI
Deleting the assigned domain breaks every address on it. The next email address creates a domain with a new random name.

email domains activity

Message counts in 24 hourly UTC buckets. The table shows the total per domain; JSON has the buckets. Get domain IDs from email domains list -o json.
CLI

Common errors

Every code and limit is in Limits, quotas, and errors.

Next steps

Email testing

Domains, addresses, inject, and real mail.

Wait for an email in a test

One address per test, waits that do not flake.

Read OTP codes and links

Pull the code or link out of a message.

inboxes commands

The CLI for webhook testing inboxes.