---
title: "Hand-written commands"
description: "The commands written for people, with every argument, flag and example."
url: "https://openemail.uk/docs/cli/reference/hand-written"
area: "CLI"
category: "Reference"
---

# Hand-written commands

The commands written for people, with every argument, flag and example.

## Commands

### `openemail login`

Sign in with your browser, or save an API key

```bash
openemail login
openemail login --no-browser
openemail login --scopes <list>
openemail login --with-token < key.txt
openemail login --token <key>
openemail login --profile <name>
```

With no flags in an interactive terminal it asks how you want to sign in. The browser sign-in opens the OpenEmail approval page, where you pick the workspace, the access and how long it lasts, then brings the approval back to the terminal on its own. Over SSH, or with `--no-browser`, it prints the link and you paste back the code the browser shows. A browser sign-in asks for a verification code before sensitive changes, exactly like the web app. An API key never asks for a code, which makes it the choice for scripts and servers. The sign-in is saved in `~/.openemail/config.json` as a profile, and becomes the active profile only when no other profile is active, so `--profile <name>` adds a second sign-in without changing what other commands use.

- No sign-in needed.

**Flags**

- `--token <key>`: Save this API key (oe_live_ or oe_test_). It lands in your shell history, so prefer `--with-token`
- `--with-token`: Read an API key from stdin and save it
- `--no-browser`: Print the sign-in link and paste the code back instead of opening a browser. Chosen for you over SSH
- `--scopes <list>` (repeatable): Permissions to preselect on the approval page, comma separated. You can still change them there
- `--force`: Replace the sign-in already saved on this profile without asking

**Examples**

Asks how to sign in, then opens your browser

```bash
openemail login
```

Over SSH: open the link anywhere and paste the code back

```bash
openemail login --no-browser
```

Preselects these permissions on the approval page

```bash
openemail login --scopes emails:send,threads:read
```

Saves an API key without it touching your shell history

```bash
openemail login --with-token < ~/.config/openemail/key
```

Signs a second profile in. The active profile stays as it is, so pass `--profile work` or run `openemail profile use work`

```bash
openemail login --profile work
```

### `openemail whoami`

Show who you are signed in as, and what you may do

```bash
openemail whoami
openemail whoami --json
openemail whoami --profile <name>
```

Reads the credential the next command would use, a saved profile, `OPENEMAIL_API_KEY` or `--api-key`, and asks the API what it may do: the workspace, the mode, the scopes, any address or domain restriction, and when a browser sign-in expires.

- Needs a sign-in.

**Examples**

```bash
openemail whoami
```

Every field, for scripts

```bash
openemail whoami --json
```

Checks another saved profile

```bash
openemail whoami --profile work
```

Checks a key without saving it

```bash
OPENEMAIL_API_KEY=oe_test_... openemail whoami
```

### `openemail status`

Show your sign-in, sender addresses and domains

```bash
openemail status
openemail status --json
```

Everything `whoami` shows, plus the addresses you can send from and the verification state of every domain, read in parallel. A section your credential may not read, for example domains without the `domains:read` scope, says so instead of failing the whole command.

- Needs a sign-in.

**Examples**

```bash
openemail status
```

The account, addresses and domains as one JSON object

```bash
openemail status --json
```

```bash
openemail status --profile work
```

### `openemail send`

Send, schedule or translate and send an email

```bash
openemail send --to <address> --subject <text> --text <body> [flags]
openemail send --to <address> --subject <text> --body-file <path> [flags]
openemail send --to <address> --template <id> [--props <json>] [flags]
```

Sends one email through `emails.send`. The body comes from `--text`, `--html` or both, then `--body-file`, then anything piped on stdin, and in an interactive terminal from your editor (`$VISUAL` or `$EDITOR`). Piped input that starts with an HTML tag is sent as HTML. Without `--from` you pick one of the addresses you can send from, or the only one is used. A piped body counts as unattended, so the picker is off and `--from` is needed when you can send from more than one address. `--draft` sends a saved draft's body, and its subject unless you pass `--subject`, to the recipients you name. In an interactive terminal you see a summary and confirm before anything goes, unless you pass `--yes`. Files attached with `--attach` travel inline while they come to 5 MB or less in total, and are uploaded to Files first and sent by reference when they come to more.

- Scopes: `emails:send`.
- Needs a sign-in.

**Flags**

- `--to <address>` (repeatable): Recipient, or several comma separated. `Ada Lovelace <ada@example.com>` works too
- `--cc <address>` (repeatable): Copy recipient, or several comma separated
- `--bcc <address>` (repeatable): Blind copy recipient, or several comma separated
- `--from <address>`: Address to send from. Left out, the only address you can send from is used, or you pick one in a terminal. A piped body turns the picker off
- `--reply-to <address>`: Address written into the Reply-To header
- `-s, --subject <text>`: Subject line
- `--text <text>`: Plain text body
- `--html <html>`: HTML body
- `-f, --body-file <path>`: Read the body from a file. A `.html` or `.htm` file is sent as HTML and anything else as text. `-` reads stdin
- `-a, --attach <path>` (repeatable): Attach a file. Up to 5 MB in total travels inline, and a larger set is uploaded to Files first
- `--template <id-or-slug>`: Send a stored template instead of a body
- `--props <json|@file>`: Template props as a JSON object, `@file` or `-` for stdin
- `--at <when>`: Schedule the send: an ISO date such as `2026-10-01T09:00:00Z`, or a delay such as `10m`, `2h`, `1d` or `PT1H`
- `--undo <seconds>`: Hold an immediate send for 0 to 900 seconds so it can still be cancelled
- `--translate <language>`: Translate the email before it goes, to a code or name such as `de` or `German`
- `--tag <key=value>` (repeatable): Tag the email. Tags are echoed back on every read
- `--draft <id>`: Send the body of a saved draft, and its subject unless you pass `--subject`. `--to` and `--from` still apply
- `--thread <id>`: File the sent email into this thread
- `--idempotency-key <key>`: Your own idempotency key, so running the command again never sends twice

**Examples**

```bash
openemail send --to ada@example.com --subject "Lunch?" --text "Thursday at noon works for me."
```

The body is read from stdin, so nothing can ask which address to send from

```bash
cat report.md | openemail send --from you@acme.com --to team@acme.com --subject "Weekly report" --attach chart.png
```

Translated to German and sent in two hours

```bash
openemail send --to ada@example.de --subject "Invoice" --body-file invoice.html --translate de --at 2h
```

```bash
openemail send --to ada@example.com --template welcome --props '{"name":"Ada"}' --tag campaign=onboarding
```

Running it again with the same key never sends twice

```bash
openemail send --from billing@acme.com --to ada@example.com --subject "Receipt" --text "Thanks" --idempotency-key order-4192 --json
```

### `openemail inbox`

List the threads in your inbox or another folder

```bash
openemail inbox [folder] [flags]
```

Lists one page of threads, newest first, and reads each one (six at a time) to show when it last moved, who wrote last, the subject, its labels and its id. The folder defaults to the inbox, and `sent`, `archive`, `starred`, `snoozed`, `spam`, `trash`, `draft` or a label id such as `USER_RECEIPTS` work too. A dot marks a thread with unread mail. Pass the id to `openemail read` to open it.

- Scopes: `threads:read`.
- Needs a sign-in.
- Aliases: `ls`.

**Arguments**

- `<folder>`: Folder or label id to list (default inbox)

**Flags**

- `-u, --unread`: Only threads with unread mail
- `-q, --query <search>`: Mailbox search such as `from:ada has:pdf newer_than:7d`
- `-l, --label <label-id>` (repeatable): Only threads carrying this label id, or every one of several comma separated
- `-n, --limit <n>`: Threads per page, 1 to 100 (default 25)
- `--cursor <cursor>`: Carry on from the cursor an earlier page printed
- `--all`: Read every page instead of the first one
- `--from-contacts`: Only threads whose newest message came from a saved contact
- `--sort <order>` (one of `"newest"`, `"oldest"`, `"sender"`, `"subject"`): Order of the threads, newest first unless you pick another

**Examples**

```bash
openemail inbox
```

```bash
openemail inbox --unread --limit 50
```

Threads you sent to Ada

```bash
openemail ls sent --query "to:ada"
```

```bash
openemail inbox archive --from-contacts --sort oldest
```

Every thread id in the inbox

```bash
openemail inbox --all --json | jq -r ".items[].id"
```

### `openemail read`

Read a thread, message by message

```bash
openemail read <thread-id> [flags]
```

Prints every message on the thread, oldest first, with who sent it, who it went to, when, the subject, the attachments with their sizes and ids, and the body. A plain text part is shown as it is, and an HTML-only message is turned into readable text: paragraphs, lists, quotes, headings and links as `text (url)`. With `--html` stdout gets only the raw HTML of one message, so it can be saved to a file, and the headers go to stderr. Reading marks the thread read unless you pass `--no-mark-read`.

- Scopes: `threads:read`.
- Needs a sign-in.
- Aliases: `show`.

**Arguments**

- `<thread-id>` (required): Thread id from `openemail inbox`

**Flags**

- `-m, --message <n>`: Show only this message, counting from 1. A negative number counts from the newest
- `--html`: Print only the raw HTML of one message on stdout, with the headers on stderr
- `--mark-read`: Mark the thread read once it is shown, which is the default. Pass `--no-mark-read` to leave it unread

**Examples**

```bash
openemail read CAHk7pQ2x9LmZ4
```

Only the newest message

```bash
openemail read CAHk7pQ2x9LmZ4 --message -1
```

```bash
openemail show CAHk7pQ2x9LmZ4 --html --message 1 > first.html
```

```bash
openemail read CAHk7pQ2x9LmZ4 --no-mark-read --json
```

### `openemail search`

Search your mail with the same syntax as the app

```bash
openemail search <query...> [flags]
```

Searches the inbox, or the folder in `--folder`, and shows the matches like `openemail inbox`. Plain words must all appear and match loosely, a quoted phrase must appear as written, and operators such as `from:`, `to:`, `subject:`, `has:pdf`, `is:unread`, `after:2026/01/31` and `newer_than:7d` narrow it. A query that names a folder with `in:` or `is:` searches there, and `in:anywhere` searches every folder.

- Scopes: `threads:read`.
- Needs a sign-in.

**Arguments**

- `<query...>` (required): What to look for. Quote it or pass several words

**Flags**

- `--folder <folder>`: Folder to search (default inbox). A query that names one with `in:` or `is:` searches there instead
- `-u, --unread`: Only threads with unread mail
- `-l, --label <label-id>` (repeatable): Only threads carrying this label id, or every one of several comma separated
- `-n, --limit <n>`: Threads per page, 1 to 100 (default 25)
- `--cursor <cursor>`: Carry on from the cursor an earlier page printed
- `--all`: Read every page instead of the first one
- `--from-contacts`: Only threads whose newest message came from a saved contact
- `--sort <order>` (one of `"newest"`, `"oldest"`, `"sender"`, `"subject"`): Order of the threads, newest first unless you pick another

**Examples**

```bash
openemail search invoice from:ada
```

```bash
openemail search "quarterly report" has:pdf newer_than:30d
```

```bash
openemail search in:anywhere from:me to:ada --limit 50
```

```bash
openemail search receipt --folder archive --sort oldest --json
```

### `openemail reply`

Reply to the last message on a thread

```bash
openemail reply <thread-id> --text <body> [flags]
openemail reply <thread-id> --all --body-file <path> [flags]
```

Replies to whoever sent the last message on the thread, or to its Reply-To address, and files the reply into the same thread. When the last message is one you sent, the reply goes to the people it went to. `--all` copies everyone else on that message, leaving out your own addresses. The subject gets `Re:` unless it already has it, and the reply is sent from the address the thread was delivered to when you can send from it. The body flags work as they do for `openemail send`, including stdin and your editor.

- Scopes: `threads:read`, `emails:send`.
- Needs a sign-in.

**Arguments**

- `<thread-id>` (required): Thread id from `openemail inbox`

**Flags**

- `--text <text>`: Plain text body
- `--html <html>`: HTML body
- `-f, --body-file <path>`: Read the body from a file. A `.html` or `.htm` file is sent as HTML and anything else as text. `-` reads stdin
- `--all`: Reply to everyone on the last message, leaving out your own addresses
- `-a, --attach <path>` (repeatable): Attach a file. Up to 5 MB in total travels inline, and a larger set is uploaded to Files first
- `--from <address>`: Address to send from. Left out, the only address you can send from is used, or you pick one in a terminal. A piped body turns the picker off
- `--to <address>` (repeatable): Recipient, or several comma separated. `Ada Lovelace <ada@example.com>` works too
- `--cc <address>` (repeatable): Copy recipient, or several comma separated
- `--bcc <address>` (repeatable): Blind copy recipient, or several comma separated
- `-s, --subject <text>`: Subject line
- `--at <when>`: Schedule the send: an ISO date such as `2026-10-01T09:00:00Z`, or a delay such as `10m`, `2h`, `1d` or `PT1H`
- `--undo <seconds>`: Hold an immediate send for 0 to 900 seconds so it can still be cancelled
- `--translate <language>`: Translate the email before it goes, to a code or name such as `de` or `German`
- `--tag <key=value>` (repeatable): Tag the email. Tags are echoed back on every read
- `--idempotency-key <key>`: Your own idempotency key, so running the command again never sends twice

**Examples**

```bash
openemail reply CAHk7pQ2x9LmZ4 --text "Thanks, that works for me."
```

Opens your editor for the body

```bash
openemail reply CAHk7pQ2x9LmZ4 --all --attach notes.pdf
```

```bash
echo "Confirmed." | openemail reply CAHk7pQ2x9LmZ4 --yes
```

```bash
openemail reply CAHk7pQ2x9LmZ4 --body-file reply.html --undo 30
```

### `openemail archive`

Archive threads out of the inbox

```bash
openemail archive <thread-id...>
```

Adds `ARCHIVE` and removes `INBOX`, the same pair the app uses, so the threads leave the inbox and stay searchable. Several ids are handled in one run, and each one is reported.

- Scopes: `threads:write`.
- Needs a sign-in.

**Arguments**

- `<thread-id...>` (required): One or more thread ids from `openemail inbox`

**Examples**

```bash
openemail archive CAHk7pQ2x9LmZ4
```

```bash
openemail archive CAHk7pQ2x9LmZ4 CAJx0dW1bQe7Tn --json
```

Archive every recent alert

```bash
openemail inbox --query "newer_than:30d from:alerts" --json | jq -r ".items[].id" | xargs openemail archive
```

### `openemail unarchive`

Move archived threads back to the inbox

```bash
openemail unarchive <thread-id...>
```

Adds `INBOX` and removes `ARCHIVE`, so the threads show in the inbox again.

- Scopes: `threads:write`.
- Needs a sign-in.

**Arguments**

- `<thread-id...>` (required): One or more thread ids from `openemail inbox`

**Examples**

```bash
openemail unarchive CAHk7pQ2x9LmZ4
```

```bash
openemail unarchive CAHk7pQ2x9LmZ4 CAJx0dW1bQe7Tn
```

### `openemail trash`

Move threads to the Bin

```bash
openemail trash <thread-id...>
```

Adds `TRASH` and takes the threads out of the inbox, spam, snoozed and archive in one step, like delete in the app. Nothing is deleted, and a snoozed thread loses its wake time. No API call takes a thread back out of the Bin, so you are asked to confirm unless you pass `--yes`.

- Scopes: `threads:write`.
- Needs a sign-in.
- Asks you to confirm.

**Arguments**

- `<thread-id...>` (required): One or more thread ids from `openemail inbox`

**Examples**

```bash
openemail trash CAHk7pQ2x9LmZ4
```

```bash
openemail trash CAHk7pQ2x9LmZ4 CAJx0dW1bQe7Tn --yes
```

### `openemail star`

Star threads

```bash
openemail star <thread-id...>
```

Adds the `STARRED` label, so the threads show under Starred.

- Scopes: `threads:write`.
- Needs a sign-in.

**Arguments**

- `<thread-id...>` (required): One or more thread ids from `openemail inbox`

**Examples**

```bash
openemail star CAHk7pQ2x9LmZ4
```

List what you starred

```bash
openemail inbox starred
```

### `openemail unstar`

Take the star off threads

```bash
openemail unstar <thread-id...>
```

Removes the `STARRED` label.

- Scopes: `threads:write`.
- Needs a sign-in.

**Arguments**

- `<thread-id...>` (required): One or more thread ids from `openemail inbox`

**Examples**

```bash
openemail unstar CAHk7pQ2x9LmZ4
```

```bash
openemail unstar CAHk7pQ2x9LmZ4 CAJx0dW1bQe7Tn
```

### `openemail mark read`

Mark threads read

```bash
openemail mark read <thread-id...>
```

Removes the `UNREAD` label from each thread.

- Scopes: `threads:write`.
- Needs a sign-in.

**Arguments**

- `<thread-id...>` (required): One or more thread ids from `openemail inbox`

**Examples**

```bash
openemail mark read CAHk7pQ2x9LmZ4
```

Mark the whole first page read

```bash
openemail inbox --unread --json | jq -r ".items[].id" | xargs openemail mark read
```

### `openemail mark unread`

Mark threads unread

```bash
openemail mark unread <thread-id...>
```

Adds the `UNREAD` label to each thread, so it shows as new again.

- Scopes: `threads:write`.
- Needs a sign-in.

**Arguments**

- `<thread-id...>` (required): One or more thread ids from `openemail inbox`

**Examples**

```bash
openemail mark unread CAHk7pQ2x9LmZ4
```

```bash
openemail mark unread CAHk7pQ2x9LmZ4 CAJx0dW1bQe7Tn --json
```

### `openemail snooze`

Hide threads until a later time

```bash
openemail snooze <thread-id...> --until <when>
```

Adds `SNOOZED`, takes the threads out of the inbox and stores when they come back. Threads are woken by an hourly sweep, so one returns up to about an hour after the time you set, always into the inbox. Snoozing a snoozed thread replaces its wake time.

- Scopes: `threads:write`.
- Needs a sign-in.

**Arguments**

- `<thread-id...>` (required): One or more thread ids from `openemail inbox`

**Flags**

- `--until <when>`: When the thread returns: an ISO date such as `2026-10-01T09:00:00Z`, or a delay such as `3h`, `2d` or `1w`

**Examples**

```bash
openemail snooze CAHk7pQ2x9LmZ4 --until 3h
```

```bash
openemail snooze CAHk7pQ2x9LmZ4 CAJx0dW1bQe7Tn --until 1w
```

```bash
openemail snooze CAHk7pQ2x9LmZ4 --until 2026-10-01T09:00:00+02:00
```

### `openemail unsnooze`

Bring snoozed threads back now

```bash
openemail unsnooze <thread-id...>
```

Adds `INBOX`, removes `SNOOZED` and clears the wake time, so the threads return straight away. A thread that is not snoozed stays where it is.

- Scopes: `threads:write`.
- Needs a sign-in.

**Arguments**

- `<thread-id...>` (required): One or more thread ids from `openemail inbox`

**Examples**

```bash
openemail unsnooze CAHk7pQ2x9LmZ4
```

List what is snoozed

```bash
openemail inbox snoozed
```

### `openemail label add`

Put labels on threads

```bash
openemail label add <thread-id...> --label <label-id>
```

Adds every label in `--label` to each thread. A label id that names no label is refused and nothing on that thread changes, so create it first with `openemail labels create`. `TRASH`, `SNOOZED` and `DRAFT` cannot be set here: use `openemail trash` or `openemail snooze`.

- Scopes: `threads:write`.
- Needs a sign-in.

**Arguments**

- `<thread-id...>` (required): One or more thread ids from `openemail inbox`

**Flags**

- `-l, --label <label-id>` (repeatable): Label id such as `USER_RECEIPTS` or `STARRED`, or several comma separated

**Examples**

```bash
openemail label add CAHk7pQ2x9LmZ4 --label USER_RECEIPTS
```

```bash
openemail label add CAHk7pQ2x9LmZ4 CAJx0dW1bQe7Tn --label USER_TAXES,USER_2026
```

### `openemail label remove`

Take labels off threads

```bash
openemail label remove <thread-id...> --label <label-id>
```

Removes every label in `--label` from each thread. A label the thread does not carry is not an error.

- Scopes: `threads:write`.
- Needs a sign-in.

**Arguments**

- `<thread-id...>` (required): One or more thread ids from `openemail inbox`

**Flags**

- `-l, --label <label-id>` (repeatable): Label id such as `USER_RECEIPTS` or `STARRED`, or several comma separated

**Examples**

```bash
openemail label remove CAHk7pQ2x9LmZ4 --label USER_RECEIPTS
```

```bash
openemail label rm CAHk7pQ2x9LmZ4 --label IMPORTANT --json
```

### `openemail temp new`

Create a disposable inbox

```bash
openemail temp new [--name <local-part>] [--domain <domain>] [--ttl <minutes>]
```

Creates a disposable address that needs no account and no API key, and keeps its token in `~/.openemail/temp-mail.json` so the other `temp` commands can reach it. The address is printed on stdout on its own, so `ADDRESS=$(openemail temp new)` works in a script. It lives for 60 minutes unless you pass `--ttl`, up to 24 hours, and `openemail temp-mail extend` adds up to an hour at a time within 24 hours of its creation, saving the new token it returns. A name you choose with `--name` is issued to anyone who asks for it too, so leave it out when the mail should reach you alone.

- No sign-in needed.
- Aliases: `create`.

**Flags**

- `--name <local-part>`: The part before the @, up to 64 letters, digits, dots, dashes or underscores, starting and ending with a letter or digit. Left out, one is generated. Anyone who asks for the same name reads its mail too
- `--domain <domain>`: Domain from `openemail temp-mail list-domains`. Left out, the first one in that list is used
- `--ttl <minutes>`: How long the inbox lives, 1 to 1440 minutes (default 60)

**Examples**

```bash
openemail temp new
```

```bash
openemail temp new --name signup-test --ttl 120
```

Keep the address in a shell variable

```bash
ADDRESS=$(openemail temp new --ttl 15)
```

Prints the inbox with its id, expiry and token

```bash
openemail temp new --json
```

### `openemail temp list`

List the disposable inboxes this CLI created

```bash
openemail temp list
```

Lists the inboxes kept in `~/.openemail/temp-mail.json`, soonest to expire last. Expired inboxes are forgotten as they are found, because their tokens no longer open anything. An inbox extended with `openemail temp-mail extend` shows its later expiry. Nothing is read from the network.

- No sign-in needed.

**Examples**

```bash
openemail temp list
```

Ids, addresses and expiry times, never the tokens

```bash
openemail temp ls --json
```

### `openemail temp read`

List the mail in a disposable inbox, or read one message

```bash
openemail temp read [inbox] [message-id] [flags]
```

Without a message id it lists what has arrived, newest first, with a short preview that is often enough to read a one time code. With one it prints the whole message, turning HTML into readable text, and lists its attachments by name and size, without their bytes. The inbox is its id or address, and can be left out when you have only one, so a lone `thr_` id reads that message. Reading a message does not mark it seen, and its body is never cut. The sender of mail in a temporary inbox is never verified.

- No sign-in needed.
- Aliases: `show`, `open`.

**Arguments**

- `<inbox>`: Inbox id or address from `openemail temp list`
- `<message-id>`: A `thr_` id from the list

**Flags**

- `--html`: Print only the raw HTML on stdout, with the headers on stderr
- `--inbox-token <token>`: The `oe_inbox_` token of an inbox this CLI did not create

**Examples**

```bash
openemail temp read
```

```bash
openemail temp read quiet-otter-12@example-temp.com
```

Reads one message from your only inbox

```bash
openemail temp read thr_9e3b7c1a5f2d8e40b6a9c3f1
```

```bash
openemail temp read tinb_k7m2q9xw4bdp thr_9e3b7c1a5f2d8e40b6a9c3f1
```

```bash
openemail temp read tinb_k7m2q9xw4bdp thr_9e3b7c1a5f2d8e40b6a9c3f1 --html > message.html
```

### `openemail temp watch`

Wait for mail to arrive in a disposable inbox

```bash
openemail temp watch [inbox] [--first]
```

Prints what is already in the inbox, then checks every 3 seconds and prints each new message as it lands, until you press Ctrl+C or the inbox expires. When `openemail temp-mail extend` saves a new token for the inbox meanwhile, the watch carries on with it until the later expiry. With `--first` it stops as soon as there is a message, which suits a script waiting for a sign-up code. With `--json` each message is one JSON line on stdout.

- No sign-in needed.

**Arguments**

- `<inbox>`: Inbox id or address from `openemail temp list`, optional when you have only one

**Flags**

- `--first`: Stop as soon as the inbox holds a message, which suits a script waiting for a code
- `--inbox-token <token>`: The `oe_inbox_` token of an inbox this CLI did not create

**Examples**

```bash
openemail temp watch
```

```bash
openemail temp watch quiet-otter-12@example-temp.com
```

Wait for one message and print its preview

```bash
openemail temp watch --first --json | jq -r .snippet
```

### `openemail temp delete`

Move the mail in a disposable inbox to the bin and forget its token

```bash
openemail temp delete [inbox] [--yes]
```

Moves every message the inbox shows to the bin at once, then removes the inbox and its token from `~/.openemail/temp-mail.json`. Nothing about the lease is stored on the server, so there is nothing to revoke: it runs on until its expiry, and mail that arrives in the meantime still reaches whoever holds the token. Nothing holds the address back either, so it can be issued again at once, to anybody. You are asked to confirm unless you pass `--yes`.

- No sign-in needed.
- Asks you to confirm.
- Aliases: `rm`.

**Arguments**

- `<inbox>`: Inbox id or address from `openemail temp list`, optional when you have only one

**Flags**

- `--inbox-token <token>`: The `oe_inbox_` token of an inbox this CLI did not create

**Examples**

```bash
openemail temp delete
```

```bash
openemail temp rm quiet-otter-12@example-temp.com --yes
```

```bash
openemail temp delete tinb_k7m2q9xw4bdp --yes --json
```

### `openemail ai translate`

Translate a subject and body without sending anything

```bash
openemail ai translate --to <language> [--text <text>|--html <html>|--body-file <path>] [--subject <subject>]
openemail ai translate --to <language> < message.txt
```

Runs the same translation a translated send performs and prints the result, so you can read it before anything leaves. Nothing is stored and nothing is sent. The body comes from `--text`, `--html`, `--body-file` (a .html file is sent as HTML, anything else as text) or stdin when it is piped. `--text` and `--html` also read a file with `@path` or stdin with `-`. Each call spends one AI action, and the credential needs the `emails:send` scope.

- Scopes: `emails:send`.
- Needs a sign-in.

**Flags**

- `--to <language>` (required): Target language as a code (`de`), an English name (`German`) or its own name (`Deutsch`)
- `--from <language>`: The language you wrote in. Stating it skips detection
- `--subject <subject>`: A subject line to translate
- `--text <text|@file|->`: A plain text body to translate
- `--html <html|@file|->`: An HTML body to translate
- `--body-file <path>`: Read the body from a file. A .html or .htm file is HTML, anything else is text
- `--original`: Keep your original text below the translation (the default). Pass `--no-original` to leave it out

**Examples**

```bash
openemail ai translate --to de --subject "Your invoice" --text "The invoice is attached."
```

Translates an HTML body and leaves your original out

```bash
openemail ai translate --to Japanese --body-file reply.html --no-original
```

Reads the body from stdin

```bash
cat notes.txt | openemail ai translate --to fr
```

Prints the full translation object, including the detected source language

```bash
openemail ai translate --to es --text "See you soon" --json
```

### `openemail ai languages`

List every language translation accepts

```bash
openemail ai languages [--search <text>]
```

Prints the language table the server translates into, in the order a picker should show it. Any row's code, English name or own name works as `--to` on `openemail ai translate` and `openemail send --translate`. Signed out, it prints the table bundled with this version of the CLI.

- Works signed in or not.
- Aliases: `langs`.

**Flags**

- `--search <text>`: Keep only languages whose code, English name or own name contains this text

**Examples**

```bash
openemail ai languages
```

Finds Portuguese and Brazilian Portuguese

```bash
openemail ai languages --search port
```

Lists the right-to-left codes

```bash
openemail ai languages --json | jq -r '.[] | select(.rtl) | .code'
```

### `openemail ai compose`

Write an email body with AI from a short prompt

```bash
openemail ai compose "<prompt>" [--to <address>] [--subject <subject>] [--thread <thread-id>] [--tone <tone>]
```

Asks the OpenEmail assistant to write an email body in your own writing style and prints it. Nothing is sent or saved, so pipe it into `openemail send --from <address>` when you are happy with it. With `--thread` the assistant reads the thread first and writes a reply to it. The prompt can also come from stdin. This uses the MCP tool `composeEmail`, so it needs a browser sign-in (`openemail login`) and spends one AI action, plus one for each web search it runs.

- Scopes: `drafts:write`.
- Needs a browser sign-in.
- Aliases: `write`.

**Arguments**

- `<prompt...>`: What the email should say

**Flags**

- `--to <address>` (repeatable): Who it is for. Repeat the flag or separate addresses with commas
- `--cc <address>` (repeatable): Who is copied in
- `--subject <subject>`: The subject the email will have, which helps the assistant stay on topic
- `--thread <thread-id>`: Write a reply to this thread. The assistant reads its latest messages first
- `--tone <tone>` (one of `"formal"`, `"friendly"`, `"casual"`, `"concise"`, `"direct"`, `"warm"`): The tone to write in

**Examples**

```bash
openemail ai compose "thank Ada for the invoice and ask for a PDF copy" --to ada@example.com
```

Writes a reply after reading the thread

```bash
openemail ai compose "say yes to Tuesday at 3pm" --thread thr_123 --tone friendly
```

Pipes the text into a send, which reads its body from stdin and needs `--from` to know the sender

```bash
openemail ai compose "decline politely" --thread thr_123 | openemail send --from you@acme.com --to ada@example.com --subject "Re: Proposal"
```

Prints the body with the inputs as JSON

```bash
openemail ai compose "remind the team about Friday" --json
```

### `openemail ai summarize`

Print the AI summary of a thread

```bash
openemail ai summarize <thread-id>
```

Prints the short summary OpenEmail keeps for a thread, with its subject, sender and date. Summaries are written when mail arrives, and a thread holding an encrypted message is never summarised. This uses the MCP tool `getThreadSummary`, so it needs a browser sign-in (`openemail login`).

- Scopes: `threads:read`.
- Needs a browser sign-in.
- Aliases: `summarise`, `summary`.

**Arguments**

- `<thread-id>` (required): The thread to summarise, as `openemail inbox` lists it

**Examples**

```bash
openemail ai summarize thr_123
```

Prints the summary, subject, sender and date as JSON

```bash
openemail ai summarize thr_123 --json
```

Reads the summary through the workspace your work profile signed in to

```bash
openemail ai summarize thr_123 --profile work
```

### `openemail mcp config`

Print the snippet that connects an AI client to OpenEmail

```bash
openemail mcp config [--client <client>]
```

Prints what to add to an MCP client so it can read and act on your mail, and which file it goes in. There are two ways to connect. The remote server URL is the simplest: the client opens your browser, you approve it on the OpenEmail consent page, and it appears in Account, Connected apps. Each client runs its own browser sign-in for the remote URL. The local bridge (`openemail mcp serve`, started through npx) is for clients that only start local servers, and it reuses this CLI's sign-in, so run `openemail login` first. With no `--client`, every client is printed in short form.

- No sign-in needed.

**Flags**

- `--client <client>` (one of `"claude-code"`, `"claude-desktop"`, `"cursor"`, `"vscode"`, `"windsurf"`, `"codex"`): The client to print the full snippet for

**Examples**

Every client in short form

```bash
openemail mcp config
```

```bash
openemail mcp config --client claude-code
```

The local bridge serves the workspace your work profile signed in to

```bash
openemail mcp config --client cursor --profile work
```

The snippets as JSON, for a setup script

```bash
openemail mcp config --client vscode --json
```

### `openemail mcp tools`

List the MCP tools this sign-in can use

```bash
openemail mcp tools [tool]
```

Lists every tool the OpenEmail MCP server offers to this sign-in, with the first sentence of its description. The list already leaves out tools your role or the app grant cannot use. Name one tool to see its full description and arguments. It needs a browser sign-in (`openemail login`).

- Needs a browser sign-in.
- Aliases: `list`.

**Arguments**

- `<tool>`: Show the description and arguments of this tool

**Examples**

```bash
openemail mcp tools
```

Shows what composeEmail takes

```bash
openemail mcp tools composeEmail
```

Prints only the tool names

```bash
openemail mcp tools --json | jq -r '.[].name'
```

### `openemail mcp call`

Call one MCP tool and print its result

```bash
openemail mcp call <tool> [--args <json|@file|->] [--arg key=value ...]
```

Calls a tool on the OpenEmail MCP server with your browser sign-in and prints the text it returns. `--args` gives every argument as one JSON object, and each `--arg key=value` sets or overrides one of them. A value is read as JSON when it parses (`--arg maxResults=5` is a number, `--arg labelIds='["Label_1"]'` a list) and as text otherwise, so quote a number you mean as text (`--arg 'id="123"'`). A result that starts with `Refused (` exits with code 4, and usually means your role or the app grant does not allow the tool. A tool that makes a sensitive change, such as creating a rule or removing a domain, first needs a verification code, as the same change does through the REST API. In an interactive terminal the command asks for the code and then calls the tool again. Unattended it exits with code 4, so run `openemail verify` beforehand. The MCP server never asks for confirmation, so a tool that sends or deletes acts at once. Run `openemail mcp tools <tool>` to see what a tool takes.

- Needs a browser sign-in.

**Arguments**

- `<tool>` (required): The tool to call, as `openemail mcp tools` lists it

**Flags**

- `--args <json|@file|->`: Every argument as one JSON object, from inline JSON, a file (`@args.json`) or stdin (`-`)
- `--arg <key=value>` (repeatable): Set one argument. The value is JSON when it parses, text otherwise

**Examples**

```bash
openemail mcp call whoAmI
```

```bash
openemail mcp call listThreads --arg folder=inbox --arg maxResults=5
```

```bash
openemail mcp call getThread --args '{"threadId":"thr_123"}'
```

Prints the full MCP result, including every content part

```bash
openemail mcp call listTemplates --json | jq .content
```

### `openemail mcp serve`

Run a local stdio MCP server that forwards to OpenEmail

```bash
openemail mcp serve
```

Reads newline-delimited JSON-RPC from stdin, sends each message to the OpenEmail MCP server with your saved browser sign-in, and writes each answer to stdout as one line. Notifications produce no output, a batch is split into single requests and answered as one array, and an expired access token is refreshed without the client noticing. Nothing but protocol reaches stdout, and nothing reaches stderr unless you pass `--debug`, except one hint the first time a tool is refused because it needs a verification code. That refusal reaches the client unchanged, because the bridge cannot ask for a code over stdio. Run `openemail verify` in a terminal with the same profile, and tools that make a sensitive change run without a code for the next 60 minutes. It stops when stdin closes. Use it for MCP clients that only start local servers; `openemail mcp config` prints the snippet. It needs a browser sign-in (`openemail login`), because API keys cannot reach the MCP server.

- Needs a browser sign-in.

**Examples**

MCP clients start it as `npx -y @openemail/cli mcp serve`. Sign in once with `openemail login` first

```bash
openemail mcp serve
```

Serves the workspace your work profile signed in to

```bash
openemail mcp serve --profile work
```

Lists the tools by hand, one JSON line in and one out

```bash
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | openemail mcp serve
```

Logs each forwarded method and its status to a file

```bash
openemail mcp serve --debug 2> mcp.log
```

### `openemail docs ask`

Ask the docs assistant a question

```bash
openemail docs ask "<question>"
```

Sends your question to the OpenEmail docs assistant and prints the answer as it is written, followed by the numbered pages it came from. The assistant answers only from the documentation, so it cannot see or change your mailbox. It needs no sign-in and never sends your credentials. Questions are limited to 600 characters and to a daily number per network, so for longer research open the docs with `openemail docs open`.

- No sign-in needed.

**Arguments**

- `<question...>` (required): What you want to know, in plain words

**Examples**

```bash
openemail docs ask "how do I verify a domain?"
```

Quotes are optional

```bash
openemail docs ask what does step-up mean for the CLI
```

Collects the answer and its sources into one JSON object

```bash
openemail docs ask "which scopes does sending need?" --json | jq -r .answer
```

### `openemail docs open`

Open a docs page in your browser

```bash
openemail docs open [slug] [--print]
```

Opens the OpenEmail documentation, or the page you name. A slug is the part of the address after /docs/, such as `cli/ai-and-mcp`, and a full docs link works too. Over SSH, or with `--print`, it prints the link instead.

- No sign-in needed.

**Arguments**

- `<slug>`: The page to open, such as `get-started/welcome` or `cli/authentication`

**Flags**

- `--print`: Print the link instead of opening a browser

**Examples**

Opens the docs home

```bash
openemail docs open
```

```bash
openemail docs open cli/authentication
```

```bash
openemail docs open cli/commands --print
```

### `openemail docs read`

Print a docs page as Markdown

```bash
openemail docs read [slug]
```

Prints the Markdown version of a docs page, the same text the website serves at the page address with .md on the end. It is handy for reading in the terminal or handing a page to another tool. With no slug it prints llms.txt, the index of every page. It needs no sign-in.

- No sign-in needed.

**Arguments**

- `<slug>`: The page to print, such as `cli/commands`. Leave it out for the index of every page

**Examples**

```bash
openemail docs read cli/commands
```

Prints the index of every docs page

```bash
openemail docs read
```

```bash
openemail docs read get-started/welcome | less
```

```bash
openemail docs read cli/scripting --json | jq -r .markdown
```

### `openemail agents`

A guide for AI agents and scripts that drive this CLI

```bash
openemail agents
openemail agents --json
```

Prints a short guide, in Markdown, for an AI agent such as Claude Code or Codex, or a script in CI: how to sign in without a person, why to pass `--json` and how to read errors and exit codes, how to find commands with `--help --json`, how to preview a change with `--dry-run` and when to pass `--yes`, how lists and streams page, what happens when a verification code or a scope is missing, and a few recipes to copy. With `--json` it prints the same guide as one JSON document. It needs no sign-in and sends nothing.

- No sign-in needed.
- Aliases: `agent`.

**Examples**

The guide as Markdown, ready to hand to an agent

```bash
openemail agents
```

Save it next to your agent instructions

```bash
openemail agents > OPENEMAIL.md
```

Only the recipes

```bash
openemail agents --json | jq -r '.recipes[].commands[]'
```

### `openemail verify`

Enter a verification code now, so sensitive commands run for 60 minutes

```bash
openemail verify
openemail verify --status
openemail verify --force
```

With a browser sign-in, a sensitive change such as adding a webhook, creating or turning on a rule, or removing a domain asks for a verification code first, the way the web app does. Commands ask for it when they need it. `openemail verify` asks for it now instead: it emails you a code, or asks for one from your authenticator app or a backup code when two-factor sign-in is on, and once the code is right, sensitive commands, `openemail mcp call` and `openemail mcp serve` run without a code for the next 60 minutes. Run it before a script or an AI client does something sensitive, because neither can type a code. It covers this profile only. An AI client connected through the remote server URL is a connected app of its own, so this does not cover it: allow that app with Allow changes for 60 minutes in its menu under Connected apps on the website (`openemail open apps`). `--status` shows whether this sign-in is verified and until when. A code allows 5 tries. After 10 wrong codes for this sign-in within 24 hours, or 20 across all your connected apps, verification is paused, so the command says when it resumes and exits 4. API keys never need a code.

- Needs a browser sign-in.

**Flags**

- `--status`: Show whether this sign-in is verified and until when, without asking for a code
- `--force`: Ask for a new code even when this sign-in is already verified, which starts a fresh 60 minutes

**Examples**

Asks for a code, then sensitive commands run without one for 60 minutes

```bash
openemail verify
```

```bash
openemail verify --status
```

Prints elevated, elevatedUntil, method and minutes for scripts

```bash
openemail verify --status --json
```

Verifies the sign-in of another saved profile

```bash
openemail verify --profile work
```

### `openemail logout`

Sign out and forget a saved profile

```bash
openemail logout
openemail logout --profile <name>
openemail logout --all
```

A browser sign-in is revoked on the server first, which removes it from Account settings, Command line, and then forgotten on this device. The profile is forgotten even when the server cannot be reached, and the command says so. An API key is only forgotten: the key itself keeps working until you revoke it.

- No sign-in needed.

**Flags**

- `--all`: Sign out of every saved profile

**Examples**

Signs out of the active profile

```bash
openemail logout
```

```bash
openemail logout --profile work
```

Signs out of every profile on this device

```bash
openemail logout --all
```

Prints what was removed and whether the server confirmed it

```bash
openemail logout --json
```

### `openemail profile list`

List the saved profiles

```bash
openemail profile list
openemail profile list --json
```

Shows every saved sign-in with its kind, workspace and user or key. The active profile is marked with a dot.

- No sign-in needed.

**Examples**

```bash
openemail profile list
```

Never prints tokens or keys

```bash
openemail profile ls --json
```

### `openemail profile use`

Make a saved profile the active one

```bash
openemail profile use <name>
```

Every command without `--profile` then uses this sign-in. With no name in an interactive terminal it lets you pick one.

- No sign-in needed.
- Aliases: `switch`.

**Arguments**

- `<name>` (required): The profile to activate

**Examples**

```bash
openemail profile use work
```

Pick from a list

```bash
openemail profile use
```

```bash
openemail profile use default --json
```

### `openemail profile remove`

Sign out of a profile and forget it

```bash
openemail profile remove <name>
```

The same as `openemail logout --profile <name>`: a browser sign-in is revoked on the server, then the profile is forgotten on this device either way.

- No sign-in needed.

**Arguments**

- `<name>` (required): The profile to remove

**Examples**

```bash
openemail profile remove work
```

```bash
openemail profile rm staging --json
```

### `openemail profile current`

Print the profile the next command would use

```bash
openemail profile current
openemail profile current --json
```

Prints the profile name on stdout, so `$(openemail profile current)` works in scripts. When `OPENEMAIL_API_KEY` or `--api-key` is in effect no profile is used, and it says so on stderr.

- No sign-in needed.

**Examples**

```bash
openemail profile current
```

```bash
OPENEMAIL_PROFILE=work openemail profile current
```

```bash
openemail profile current --json
```

### `openemail open`

Open a page of the OpenEmail web app in your browser

```bash
openemail open [page] [--print]
openemail open forwarding <address> [--print]
```

Opens the web app at the page you name, signed in as whoever is signed in to that browser. Use it for what the CLI does not do yet, such as billing, workspaces, account security, data export, forwarding and DNS providers. With no page it opens the mailbox. `forwarding` takes the address whose forwarding you want to change. A path that starts with `/` is opened as it is. Over SSH, or with `--print`, it prints the link instead.

- No sign-in needed.

**Arguments**

- `<page>`: One of the pages below, or a path such as `/mail/workspace/domains`
- `<address>`: The address a `forwarding` page belongs to

**Flags**

- `--print`: Print the link instead of opening a browser

**Examples**

Opens the mailbox

```bash
openemail open
```

```bash
openemail open billing
```

Command line, where each terminal sign-in is listed and can be signed out

```bash
openemail open cli
```

Forwarding for one address

```bash
openemail open forwarding you@acme.com
```

Link a DNS provider, so the records of a new domain are written for you

```bash
openemail open providers
```

Prints the link instead of opening it

```bash
openemail open domains --print
```

### `openemail update`

Check npm for a newer version of the CLI

```bash
openemail update
```

Checks the npm registry now, ignoring the daily cache, and prints the command that installs the latest release with the package manager you used. The CLI never updates itself. When npm has no release of the CLI yet it says so and exits 0, and only a registry it could not reach exits 9.

- No sign-in needed.

**Examples**

```bash
openemail update
```

Prints current, latest, published, updateAvailable and the install command

```bash
openemail update --json
```

### `openemail completion`

Print a shell completion script for bash, zsh or fish

```bash
openemail completion <bash|zsh|fish>
```

Prints a completion script built from every command, subcommand and flag this version of the CLI knows. Load it in your shell profile, and run the command again after an update.

- No sign-in needed.

**Arguments**

- `<shell>` (required): bash, zsh or fish

**Examples**

Add this line to ~/.bashrc

```bash
eval "$(openemail completion bash)"
```

Then start a new shell, or run compinit

```bash
openemail completion zsh > "${fpath[1]}/_openemail"
```

```bash
openemail completion fish > ~/.config/fish/completions/openemail.fish
```

### `openemail api`

Call any REST endpoint with your sign-in

```bash
openemail api <method> <path> [--data <json|@file|->] [--query <key=value>] [--header <key=value>]
openemail api <path>
```

Sends one request to the OpenEmail REST API through the same transport the other commands use, so your profile or API key, token refresh and verification codes all apply. The path is relative to the API origin, for example `/threads` or `/keys/self`, and a path on its own is a GET. A JSON response is printed as formatted JSON, and a file is saved with `--out`. A failed request prints the API error and exits with the matching code.

- Needs a sign-in.

**Arguments**

- `<method>`: GET, POST, PUT, PATCH, DELETE or HEAD (default GET)
- `<path>` (required): Path on the API, such as `/threads` or `/emails/msg_123`

**Flags**

- `-d, --data <json|@file|->`: Request body: inline JSON, `@file` to read a file, or `-` for stdin
- `-q, --query <key=value>` (repeatable): Query parameter. Parameters written into the path are kept too
- `-H, --header <key=value>` (repeatable): Extra request header. `Key: value` works too. The CLI sets Authorization itself
- `-o, --out <path>`: Save the response body to this file as it came, which suits a download. `-` writes it to stdout

**Examples**

```bash
openemail api /keys/self
```

```bash
openemail api GET /threads --query folder=inbox --query limit=5
```

```bash
openemail api POST /labels --data '{"name":"Receipts"}'
```

```bash
openemail api PATCH /threads/CAHk7pQ2x9LmZ4 --data @patch.json
```

Saves the download as it came

```bash
openemail api GET /files/file_123/content --out report.pdf
```

A DELETE asks to confirm first unless you pass --yes

```bash
openemail api DELETE /webhooks/wh_123 --yes
```
