Authentication · MachineMail Docs

Sign in with the browser

The CLI authenticates every request with an API key (mm_live_...), sent as Authorization: Bearer. There are three ways to get one into it:

Situation Use
You, at a terminal, with a browser machinemail login: approve a code in the dashboard, get a new key.
CI, scripts, containers An existing key in MACHINEMAIL_API_KEY, or saved with login --key -.
An agent with no human and no account machinemail register: a sandbox inbox and its own credentials. See Agent workflows.

Sign in with the browser

machinemail login uses the OAuth 2.0 device authorization grant (RFC 8628), the same flow as gh auth login: the CLI never sees your password or session, and you approve on a device where you are already signed in.

  1. Start the sign-in

Terminal

machinemail login

The CLI asks the API for a code and shows it:

MACHINEMAIL Sign in
Confirm this code in the browser, signed in to MachineMail:

┌───────────┐
│ MLGH-JHRC │
└───────────┘

URL  https://machinemail.dev/cli?code=MLGH-JHRC

Press Enter to open the browser, or open the URL on any device. Ctrl-C cancels.
⠋ Waiting for approval
  1. Approve the code

Press Enter to open the link, or open it on any device: your laptop when the CLI runs over SSH, your phone, another browser. If you are not signed in to the dashboard, it asks you to sign in first (see the Quickstart if you have no account yet).

The page shows the code, the access the key will get and when it was requested. Check that the code matches the one in your terminal, then choose Approve. Deny stops the CLI and creates nothing. Approve only a code you asked for yourself.

  1. The key is saved

The API creates a new organization key named CLI on <hostname> and hands it to the waiting CLI, once. The CLI saves it to the current profile:

✓ Signed in as ada@acme.dev (Acme) — key 'CLI on ada-mbp' saved
PROFILE   default (current)
API       https://api.machinemail.dev
KEY       mm_live_…x7Qe (key_01m4g463jy0002nz2drf5r5bvx)
SAVED TO  /home/ada/.config/machinemail/config.json

The key has every permission except inbound_raw, and it is listed on the dashboard’s API keys page, where you can revoke it. Codes expire after 10 minutes and work once. Connect an agent without code lists everything you can review and revoke in the dashboard.

Outcome What the CLI does Exit code
Approved Saves the key and prints who you are signed in as. 0
Denied in the browser Sign-in was denied in the browser. Nothing is saved. 3
Code expired (10 minutes) The sign-in code expired before it was approved. 8
Ctrl-C Interrupted; nothing was saved. 130

Without a browser

When the CLI finds no browser to open (over SSH, in a container), it shows the code box without the Enter prompt: open the URL on any device. When stdin or stderr is not a terminal, or CI is set, it prints the link and the code as plain lines on stderr and waits:

To sign in, open https://machinemail.dev/cli?code=HCQT-WVHJ
and confirm the code HCQT-WVHJ (expires in 10 minutes).
Waiting for approval...

machinemail login --no-browser never opens a browser, even when it could.

Use an existing API key

Nobody is there to approve a code in CI. Create a key on the dashboard’s API keys page (or with machinemail api-keys create), store it as a secret, and give it to the CLI in one of these ways. Each keeps the key out of your shell history and out of ps:

Environment variable, read on every command

export MACHINEMAIL_API_KEY=mm_live_...
machinemail inboxes list

Saved to a profile, from stdin

printf %s "$MACHINEMAIL_API_KEY" | machinemail login --key - --profile ci

One command, from stdin

printf %s "$MACHINEMAIL_API_KEY" | machinemail inboxes list --api-key -

Pasted into a hidden prompt

machinemail login --paste

login --key, --with-key (the same as --key -) and --paste check the key against GET /v0/auth/me before saving it, and print the same summary as whoami. A key the API rejects is not saved (exit 3). --no-verify saves without calling the API, for setting up a machine offline.

--api-key <key> also takes a literal key, but the CLI warns that it shows in ps and your shell history. Prefer the environment variable or stdin.

Note

An organization key can do everything in your organization. For an agent or a CI job that works with one inbox, create a key scoped to that inbox, or to a pod. See API keys.

Which key is used

The first one found wins:

  1. --api-key
  2. MACHINEMAIL_API_KEY
  3. the api_key of the profile in use

MACHINEMAIL_API_KEY therefore shadows a key saved by login. machinemail doctor warns when that happens, and whoami says where the key came from:

Terminal

machinemail whoami --json | jq '{key, key_source, organization_name}'

json

{
  "key": "mm_live_…hdYv",
  "key_source": "env",
  "organization_name": "Acme"
}

Profiles

A profile is a named API key. Use one per organization or agent, for example a second organization’s key next to your own:

Terminal

machinemail login --profile work
printf %s "$CI_KEY" | machinemail login --key - --profile ci
machinemail inboxes list --profile work      # one command
export MACHINEMAIL_PROFILE=ci                # this shell
machinemail profiles use work                # from now on
machinemail profiles list
PROFILE    API                          KEY
──────────────────────────────────────────────────────────
  ci       https://api.machinemail.dev  mm_live_…Rf2k
* default  https://api.machinemail.dev  mm_live_…hdYv
  work     https://api.machinemail.dev  mm_live_…a91Q

* marks the current profile. The profile in use is --profile, then MACHINEMAIL_PROFILE, then the config file’s current_profile, then default. See Configuration for what else a profile holds.

machinemail profiles delete <name> removes a profile and its stored key (on a terminal you type the name to confirm; elsewhere pass --yes). It does not revoke the key.

Log out

Terminal

machinemail logout

logout deletes the stored key from the profile; the profile itself stays. On a terminal it first asks whether to revoke the key on the server too (DELETE /v0/api-keys/{id}), so it stops working everywhere:

Command Removes the local copy Revokes the key on the server
machinemail logout yes asks on a terminal; no otherwise
machinemail logout --yes yes yes
machinemail logout --revoke yes yes
machinemail logout --no-revoke yes no
✓ Removed key mm_live_…hdYv from profile default and revoked it

If revoking fails, nothing is removed, so you can retry or revoke the key on the dashboard. logout only touches the config file: when MACHINEMAIL_API_KEY is set it says so, and you unset the variable yourself. For a profile created by register, logout also removes the agent credentials that renew its token.

Piped, logout prints what it did:

json

{
  "profile": "default",
  "removed": true,
  "key": "mm_live_…hdYv",
  "revoked": true
}

Where the key is stored

Keys saved by login, register and config set api_key live in the config file, by default ~/.config/machinemail/config.json ($XDG_CONFIG_HOME/machinemail/config.json when that is set; MACHINEMAIL_CLI_CONFIG points at another file).

See Configuration for the file’s format.