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.
Code
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:
Code
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:
Code
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:
Code
Use --profile team-b to select another profile for one command. Each profile keeps its own project and route defaults.
Check the connection
Code
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:
Code
Run it first when a command fails with an authentication error.
What you can do
| Command | Purpose |
|---|---|
messages | Send email, list messages, inspect delivery events, and export HTML, text, or raw source. |
webhooks | Manage webhook endpoints, send test events, and forward live events to localhost. |
listeners | Inspect, stop, and replay local webhook sessions, or get their signing secret. |
projects | List, inspect, and create projects. |
routes | Manage routes and verify inbound domains. |
domains | Add, verify, and assign sending domains. |
auth, profiles, context | Log in, switch teams, and set project and route defaults. |
skills | List and export the agent skill that matches your installed version. |
doctor, completion, version | Check 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:
Code
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:
Code
Use --file - to read the message from standard input. In JSON mode the command returns the message ID:
Code
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:
Code
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:
Code
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
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:
Code
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:
Code
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
Code
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:
Code
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:
Code
- Results go to standard output. Progress, prompts, and errors go to standard error.
- Listener output is newline-delimited JSON, one object per event.
--plainkeeps readable text without color for logs. Don't combine it with--json.- Lists accept
--limit(1 to 100). Pass the returnednext_cursorto--cursorfor the next page. - Create and update commands read input from
--file, or--file -for standard input. - Destructive commands ask for confirmation. Pass
--yesin a script.
Errors are one JSON object on standard error with a stable code, a message, and field details when the API returns them:
Code
| Exit code | Meaning |
|---|---|
| 1 | Local failure |
| 2 | Validation error |
| 3 | Authentication required |
| 4 | Permission denied |
| 5 | Resource not found |
| 6 | Conflict or expired state |
| 7 | Rate limited |
| 8 | Server failure |
| 130 | Cancelled |
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:
Code
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:
Code
Open User settings > Applications to review the CLI connection, see active listener sessions, or revoke access from the dashboard.
Troubleshooting
| Problem | What to check |
|---|---|
lettermint is not found after installation | Open 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 login | Run 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 3 | The login expired or was revoked. Run lettermint auth logout --profile work --local, then log in again. |
| A command returns exit code 4 | Check your role and project assignment. A new login does not change your permissions. |
A message is missing from messages get | The 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 arrive | Confirm 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 lines | Your 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 missing | Creation returns the secret once. For a listener, run lettermint listeners secret SESSION_ID. |
| Exported content on Windows looks changed | Use --output instead of shell redirection. Older PowerShell versions change text encoding on redirect. |
| Scheduling or batch send is rejected | The CLI sends single messages only. Use the Sending API or the MCP server. |
Related documentation
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.