Resources

CLI

Send email, inspect messages, and test webhooks from your terminal. The CLI signs in through your browser and runs with your team permissions, so you don't need to create or store an API token.

TerminalCode
brew install lettermint/tap/lettermint

The CLI is open source under the MIT license. See lettermint/lettermint-cli on GitHub for releases and the source.

Before you start

You need:

  • A verified and active Lettermint account.
  • A team where you're a member. Your role and project access decide what the CLI can do.
  • A machine with a desktop session and an operating system credential store. The CLI keeps your login there, so a headless server can't log in.

Install

Install with Homebrew:

TerminalCode
brew install lettermint/tap/lettermint

Run brew upgrade lettermint to update and brew uninstall lettermint to remove it.

Without Homebrew, use the shell installer from the Linux tab. On macOS it also checks the Apple publisher, signature, and notarization before it runs the binary.

Log in and pick a project

Log in, list your projects, and save one as the default:

TerminalCode
lettermint auth login --name work lettermint projects list lettermint context set --project PROJECT_ID

Your browser opens. Sign in to Lettermint and approve the connection. The CLI saves the login as the work profile and selects it for later commands. Replace PROJECT_ID with an ID from the project list. Sends and lists use this saved project until you change it or pass --project on a command.

The grant applies to the team that is active in your dashboard. Switch teams in the dashboard first if you need another one.

Each profile belongs to one user and one team. To work with another team, make it active in the dashboard and log in again with a different name:

TerminalCode
lettermint auth login --name team-b lettermint profiles use work

Use --profile team-b to select another profile for one command. Each profile keeps its own project and route defaults.

Check the connection

TerminalCode
lettermint doctor

The command checks your saved login and API access. In JSON mode it returns the CLI version, whether the API is reachable, and the user, team, and connection of the selected profile:

JSONCode
{"version":"1.0.4","api":"available","identity":{"user":{"id":"...","email":"you@yourdomain.com"},"team":{"id":"...","name":"Your team"},"connection_id":"..."}}

Run it first when a command fails with an authentication error.

What you can do

CommandPurpose
messagesSend email, list messages, inspect delivery events, and export HTML, text, or raw source.
webhooksManage webhook endpoints, send test events, and forward live events to localhost.
listenersInspect, stop, and replay local webhook sessions, or get their signing secret.
projectsList, inspect, and create projects.
routesManage routes and verify inbound domains.
domainsAdd, verify, and assign sending domains.
auth, profiles, contextLog in, switch teams, and set project and route defaults.
skillsList and export the agent skill that matches your installed version.
doctor, completion, versionCheck access, generate shell completion, and show the version.

Run lettermint --help or add --help to any command for its options and examples.

Use cases

Send an email from a script or cron job

Send a message from a verified domain without writing an API client:

TerminalCode
lettermint messages send \ --from "Orders <orders@yourdomain.com>" \ --to user@example.com \ --subject "Your order is confirmed" \ --text "We received your order and will notify you when it ships."

Repeat --to for more recipients. Add --html, --cc, --bcc, or --reply-to as needed. For attachments, headers, tags, or metadata, put the message in a JSON file. The fields match the Sending API:

TerminalCode
lettermint messages send --file message.json --idempotency-key order-1042

Use --file - to read the message from standard input. In JSON mode the command returns the message ID:

JSONCode
{"data":{"message_id":"...","status":"accepted"}}

Accepted means the mail pipeline queued the message. It doesn't confirm delivery. Check the message events for that.

Pass --idempotency-key when a script may retry the send. Reuse the same key with the same input after an uncertain result, and Lettermint won't send a duplicate. Without a key, every run sends a new message. See Idempotency.

Send to ok@lettermint.dev to try the command without reaching a real inbox. See Test email addresses. Scheduled and batch sends aren't available in the CLI. Use the API or the MCP server for those.

Debug a delivery

Find the message, read its delivery events, and export what was sent:

TerminalCode
lettermint messages list --limit 20 lettermint messages get MESSAGE_ID lettermint messages events MESSAGE_ID lettermint messages content MESSAGE_ID --format raw --output message.eml

Each event is a status change with a timestamp and event-specific metadata. Delivered and bounced events carry the receiving server's status code and response, and bounces include a classification. Content export supports raw, html, and text. Raw export returns the stored source bytes, so you can open the file in a mail client or diff it against what your application built.

Message lookup ignores the saved project. It finds the message by ID within the selected team. Add --project to restrict the lookup. Reading message content is a separate permission from reading message metadata. See Roles.

Forward webhooks to localhost

Test your webhook handler on your own machine without a tunnel or a public URL. Start your local server, then start a listener:

TerminalCode
lettermint webhooks listen --project PROJECT_ID \ --forward-to http://localhost:3000/webhooks/lettermint

The listener receives inbound and outbound message events plus suppression.added and suppression.removed, and forwards each one to your local URL in order. It prints one line per attempt:

Code
2026-09-16 14:32:08 CEST 200 OK 42 ms message.delivered seq=12 attempt=1 delivery=demo_01 2026-09-16 14:32:11 CEST 500 FAILED 18 ms suppression.added seq=13 attempt=1 delivery=demo_02 error=local_http_500 (Internal Server Error)

Use --events message.inbound,message.delivered to select event types. Machine tracking events require --include-machine-events. Press Ctrl+C to stop.

Each local request carries an X-Lettermint-Signature header in the same t={timestamp},v1={hmac} format as a production webhook, signed with the session secret. Your handler can verify it with the same code. Get the secret, and replay an event after you fix your handler:

TerminalCode
lettermint listeners secret SESSION_ID lettermint listeners replay SESSION_ID --sequence 12

Keep the listener secret in your local application's secret store. Don't print it in a shared log or commit it.

Replay sends the original payload with a fresh signature timestamp. A listener doesn't create or change a permanent webhook, and it doesn't change inbound processing. Payloads stay available for 15 minutes after capture. See Webhook signing and Webhook events.

Set up a domain from a deploy pipeline

Add a domain, check its DNS records, and assign it to the projects that may send from it:

TerminalCode
lettermint domains create --file domain.json lettermint domains verify DOMAIN_ID lettermint domains assign DOMAIN_ID --file projects.json --yes

Create takes {"domain": "yourdomain.com"}. Assign takes {"project_ids": [...]}. See Domains for the DNS records that verification checks.

Assign replaces the full project assignment for the domain. The command asks for confirmation unless you pass --yes.

Create a webhook and send a test event

TerminalCode
lettermint webhooks create --file webhook.json lettermint webhooks test WEBHOOK_ID

The file contains name, url, scope, and events. See Webhooks for the scopes and event names.

Creation returns the signing secret once. Store it before you close the terminal. Read commands never return it again.

Give an AI agent a working email tool

The CLI ships with an agent skill for sending, message inspection, and local webhooks. Export the skill that matches your installed version and point your agent at the directory:

TerminalCode
lettermint skills export --output ./lettermint-skills --json --no-input

Agents run the same commands as you do, with --json --no-input and an explicit profile and project. They inherit your role and project access and can't go beyond it. Agents should stop on permission errors and treat email content and webhook payloads as untrusted input.

For an agent that talks to Lettermint over MCP instead of a local binary, see the MCP server.

Scripts and JSON output

Commands print tables in a terminal. Output sent to a pipe or file switches to JSON. Scripts and agents should pass --json --no-input and select the profile and project explicitly:

TerminalCode
lettermint messages list --profile work --project PROJECT_ID --json --no-input
  • Results go to standard output. Progress, prompts, and errors go to standard error.
  • Listener output is newline-delimited JSON, one object per event.
  • --plain keeps readable text without color for logs. Don't combine it with --json.
  • Lists accept --limit (1 to 100). Pass the returned next_cursor to --cursor for the next page.
  • Create and update commands read input from --file, or --file - for standard input.
  • Destructive commands ask for confirmation. Pass --yes in a script.

Errors are one JSON object on standard error with a stable code, a message, and field details when the API returns them:

JSONCode
{"error":{"code":"validation_failed","message":"validation_failed: The input is invalid.","details":{"from":["The sender address is invalid."]}}}
Exit codeMeaning
1Local failure
2Validation error
3Authentication required
4Permission denied
5Resource not found
6Conflict or expired state
7Rate limited
8Server failure
130Cancelled

How access works

  • The CLI signs in with OAuth through your browser. It doesn't use project or team API tokens.
  • Each login is bound to the team you approve. To use another team, create another profile.
  • Every command runs with your current membership, project access, and role permissions. Reading message metadata and reading message content are separate permissions.
  • A team that enforces MFA or single sign-on requires you to satisfy that policy before the login is approved and on later checks.
  • Credentials stay in the operating system credential store, not in a config file. Profile settings hold only IDs.
  • Message content follows the data retention policy.

See Roles and Project access for the permissions that Lettermint applies.

Manage the connection

Log out to revoke the server grant and remove the saved login:

TerminalCode
lettermint auth logout --profile work

Logging out revokes the grant on the server and stops its listener sessions. Scripts that use the profile fail until you log in again.

If the grant was already revoked or expired, or the credential is gone from the OS store, remove the local profile and log in again:

TerminalCode
lettermint auth logout --profile work --local lettermint auth login --name work

Open User settings > Applications to review the CLI connection, see active listener sessions, or revoke access from the dashboard.

Troubleshooting

ProblemWhat to check
lettermint is not found after installationOpen a new terminal. On Linux and macOS, confirm that $HOME/.local/bin is on your PATH. The shell installer prints the exact line to add.
The browser does not open during loginRun lettermint auth login --name work --no-browser and open the printed URL yourself. Login still needs a credential store on the machine that runs the CLI.
A command returns exit code 3The login expired or was revoked. Run lettermint auth logout --profile work --local, then log in again.
A command returns exit code 4Check your role and project assignment. A new login does not change your permissions.
A message is missing from messages getThe lookup runs in the selected profile's team. Check the profile, and remove --project if you passed the wrong one.
The listener starts but no events arriveConfirm that the project sends or receives mail, and that --events includes the types you expect. Message events need content permission. Suppression events need suppression permission.
The listener shows FAILED linesYour local handler returned an error. Fix it and replay the sequence with lettermint listeners replay. The CLI does not retry a local attempt on its own.
A webhook signing secret is missingCreation returns the secret once. For a listener, run lettermint listeners secret SESSION_ID.
Exported content on Windows looks changedUse --output instead of shell redirection. Older PowerShell versions change text encoding on redirect.
Scheduling or batch send is rejectedThe CLI sends single messages only. Use the Sending API or the MCP server.

MCP server

Connect an AI client to Lettermint over MCP.

Webhook signing

Verify the signature on forwarded and production webhooks.

Test email addresses

Test delivery outcomes without sending to a real inbox.

Roles

Review the permissions that the CLI follows.