Skip to main content
@openmail/cli is how agents that live in a shell talk to OpenMail: Claude Code, Cursor, Codex, or a cron job. On OpenClaw the plugin bundles it, so nothing on this page needs installing there.

Install

Needs Node 20 or newer.
init creates an inbox, stores it as the default, and saves the key you gave it to ~/.openmail-cli/state.json (mode 600). Every command that takes --inbox-id falls back to that default.

Authentication

Key resolution, first match wins: --api-key, OPENMAIL_API_KEY (environment or a .env in the working directory), then the key init saved. In CI or anywhere commands get logged, use the environment variable. --api-key shows up in process lists and shell history. Key scope decides what works:

Commands

openmail help <command> prints the full flag list for any group. What follows is one working example per thing you’d do.

Send

One --to, on purpose: the CLI refuses a second so an agent can’t spray. Repeat --cc for the others. HTML in --body is detected. With --thread-id the subject is optional and the API quotes the previous message under your text the way a mail client does; --no-quote sends your text alone, handy when the chain is long. --reply-to on the free plan has to be an inbox you own; --idempotency-key makes retries safe.

Read

Run threads list --is-read false, handle what comes back, mark each thread read.

Attachments

text returns extracted text for PDF, DOCX, XLSX, PPTX, and images (OCR), so the agent reads the document without parsing it. get downloads the raw file. Ids and filenames come from threads get.

Inboxes

--domain needs a verified custom domain. A pod-scoped key always creates in its own pod; account keys pass --pod-id. Inbox keys and webhooks live here too:
Key tokens print once. Minting needs an account or pod key. Webhooks need an account key; if you only want to notice new mail, threads list --is-read false does it without one.

Pods

One pod per agent or per tenant, each with its own pod-scoped key, is the usual shape. --client-id is your own id for the pod and works anywhere a pod_id does. Pod management needs an account key. See Pods.

Domains

--pod-id on add ties the domain to one pod; leave it off for account-wide.

Policy

Who may email an inbox (inbound) and who it may email (outbound, checked on To, Cc, and Reply-To). Scope defaults to the account; --pod-id or --inbox-id narrows it. Modes are none, allowlist, and inherit; an empty allowlist denies everyone. Inbox keys can’t touch policy. See Sender rules.

Feedback

Goes to the OpenMail team. Types: bug, friction, feature_request.

Global flags

Environment variables

Read from the environment first, then a .env in the working directory. The matching flag wins over both.

Exit codes

0 on success, 1 on anything else: bad flags, missing key, API errors. With --json the error is a JSON object on stderr, so scripts can branch on the code and parse the reason.

Updating

The CLI checks npm at most once a day and prints a one-line notice after any command when a newer version exists. update runs npm install -g for you; upgrade is an alias.

Limits

  • One page per list call. --limit and --offset only. Nothing auto-paginates.
  • No confirmation on delete or revoke. inbox delete and keys revoke run as soon as you press enter, which is what an agent wants and what a typo doesn’t.

Source and support

The CLI is open source at openmailsh/cli; bugs and feature requests go in its issues, or straight from the terminal with openmail feedback.