Package Exports
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (resend-cli) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Resend CLI
The official CLI for Resend.
Built for humans, AI agents, and CI/CD pipelines.
██████╗ ███████╗███████╗███████╗███╗ ██╗██████╗
██╔══██╗██╔════╝██╔════╝██╔════╝████╗ ██║██╔══██╗
██████╔╝█████╗ ███████╗█████╗ ██╔██╗ ██║██║ ██║
██╔══██╗██╔══╝ ╚════██║██╔══╝ ██║╚██╗██║██║ ██║
██║ ██║███████╗███████║███████╗██║ ╚████║██████╔╝
╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═══╝╚═════╝Install
Homebrew (macOS)
brew install resend/cli/resendShell script (macOS and Linux)
curl -fsSL https://resend.com/install.sh | bashLocal development
Use this when you want to change the CLI and run your build locally.
Prerequisites
- Bun (runtime and package manager)
Setup
Clone the repo
git clone https://github.com/resend/resend-cli.git cd resend-cli
Install dependencies
bun installBuild locally
bun run build
Output:
./dist/resend
Running the CLI locally
Use the built binary directly:
./dist/resend --versionOr add the project to your PATH:
export PATH="$(pwd)/dist:$PATH"
resend doctorMaking changes
After editing source files, rebuild:
bun run buildQuick start
# Authenticate
resend login
# Send an email
resend emails send \
--from "you@yourdomain.com" \
--to recipient@example.com \
--subject "Hello from Resend CLI" \
--text "Sent from my terminal."
# Check your environment
resend doctorAuthentication
The CLI resolves your API key using the following priority chain:
| Priority | Source | How to set |
|---|---|---|
| 1 (highest) | --api-key flag |
resend --api-key re_xxx emails send ... |
| 2 | RESEND_API_KEY env var |
export RESEND_API_KEY=re_xxx |
| 3 (lowest) | Config file | resend login |
If no key is found from any source, the CLI errors with code auth_error.
Commands
resend login
Authenticate by storing your API key locally. The key is validated against the Resend API before being saved.
resend loginInteractive mode (default in terminals)
When run in a terminal, the command checks for an existing key:
- No key found — Offers to open the Resend API keys dashboard in your browser so you can create one, then prompts for the key.
- Existing key found — Shows the key source (
env,config) and prompts for a new key to replace it.
The key is entered via a masked password input and must start with re_.
Non-interactive mode (CI, pipes, scripts)
When stdin is not a TTY, the --key flag is required:
resend login --key re_xxxxxxxxxxxxxOmitting --key in non-interactive mode exits with error code missing_key.
Options
| Flag | Description |
|---|---|
--key <key> |
API key to store (required in non-interactive mode) |
Output
On success, credentials are saved to ~/.config/resend/credentials.json with 0600 permissions (owner read/write only). The config directory is created with 0700 permissions.
# JSON output
resend login --key re_xxx --json
# => {"success":true,"config_path":"/Users/you/.config/resend/credentials.json"}Error codes
| Code | Cause |
|---|---|
missing_key |
No --key provided in non-interactive mode |
invalid_key_format |
Key does not start with re_ |
validation_failed |
Resend API rejected the key |
resend emails send
Send an email via the Resend API. Provide all options via flags for scripting, or let the CLI prompt interactively for missing fields.
resend emails send \
--from "Name <sender@yourdomain.com>" \
--to recipient@example.com \
--subject "Subject line" \
--text "Plain text body"Options
| Flag | Required | Description |
|---|---|---|
--from <address> |
Yes | Sender email address (must be from a verified domain) |
--to <addresses...> |
Yes | One or more recipient email addresses (space-separated) |
--subject <subject> |
Yes | Email subject line |
--text <text> |
One of text/html/html-file | Plain text body |
--html <html> |
One of text/html/html-file | HTML body as a string |
--html-file <path> |
One of text/html/html-file | Path to an HTML file to use as body |
--cc <addresses...> |
No | CC recipients (space-separated) |
--bcc <addresses...> |
No | BCC recipients (space-separated) |
--reply-to <address> |
No | Reply-to email address |
Interactive mode
When run in a terminal without all required flags, the CLI prompts for missing fields:
# prompts for from, to, subject, and body
resend emails send
# prompts only for missing fields
resend emails send --from "you@yourdomain.com"Non-interactive mode
When piped or run in CI, all required flags must be provided. Missing flags cause an error listing what's needed:
echo "" | resend emails send --from "you@yourdomain.com"
# Error: Missing required flags: --to, --subjectA body (--text, --html, or --html-file) is also required — omitting all three exits with code missing_body.
Examples
Multiple recipients:
resend emails send \
--from "you@yourdomain.com" \
--to alice@example.com bob@example.com \
--subject "Team update" \
--text "Hello everyone"HTML from a file:
resend emails send \
--from "you@yourdomain.com" \
--to recipient@example.com \
--subject "Newsletter" \
--html-file ./newsletter.htmlWith CC, BCC, and reply-to:
resend emails send \
--from "you@yourdomain.com" \
--to recipient@example.com \
--subject "Meeting notes" \
--text "See attached." \
--cc manager@example.com \
--bcc archive@example.com \
--reply-to noreply@example.comOverriding the API key for one send:
resend --api-key re_other_key emails send \
--from "you@yourdomain.com" \
--to recipient@example.com \
--subject "Test" \
--text "Using a different key"Output
Returns the email ID on success:
{ "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" }Error codes
| Code | Cause |
|---|---|
auth_error |
No API key found or client creation failed |
missing_body |
No --text, --html, or --html-file provided |
file_read_error |
Could not read the file passed to --html-file |
send_error |
Resend API returned an error |
resend doctor
Run environment diagnostics. Verifies your CLI version, API key, domains, and detects AI agent integrations.
resend doctorChecks performed
| Check | Pass | Warn | Fail |
|---|---|---|---|
| CLI Version | Running latest | Update available or registry unreachable | — |
| API Key | Key found (shows masked key + source) | — | No key found |
| Domains | Verified domains exist | No domains or all pending verification | API key invalid |
| AI Agents | Lists detected agents (or none) | — | — |
The API key is always masked in output (e.g. re_...xxxx).
Interactive mode
In a terminal, shows animated spinners for each check with colored status icons:
Resend Doctor
✔ CLI Version: v0.1.0 (latest)
✔ API Key: re_...xxxx (source: env)
✔ Domains: 2 verified, 0 pending
✔ AI Agents: Detected: Cursor, Claude DesktopJSON mode
resend doctor --json{
"ok": true,
"checks": [
{ "name": "CLI Version", "status": "pass", "message": "v0.1.0 (latest)" },
{ "name": "API Key", "status": "pass", "message": "re_...xxxx (source: env)" },
{ "name": "Domains", "status": "pass", "message": "2 verified, 0 pending" },
{ "name": "AI Agents", "status": "pass", "message": "Detected: Cursor" }
]
}Each check has a status of pass, warn, or fail. The top-level ok is false if any check is fail.
Detected AI agents
| Agent | Detection method |
|---|---|
| OpenClaw | ~/clawd/skills directory exists |
| Cursor | ~/.cursor directory exists |
| Claude Desktop | Platform-specific config file exists |
| VS Code | .vscode/mcp.json in current directory |
Exit code
Exits 0 when all checks pass or warn. Exits 1 if any check fails.
Global options
These flags work on every command and are passed before the subcommand:
resend [global options] <command> [command options]| Flag | Description |
|---|---|
--api-key <key> |
Override API key for this invocation (takes highest priority) |
--json |
Force JSON output even in interactive terminals |
--version |
Print version and exit |
--help |
Show help text |
Output behavior
The CLI has two output modes:
| Mode | When | Stdout | Stderr |
|---|---|---|---|
| Interactive | Terminal (TTY) | Formatted text | Spinners, prompts |
| Machine | Piped, CI, or --json |
JSON | Nothing |
Switching is automatic — pipe to another command and JSON output activates:
resend doctor | jq '.checks[].name'
resend emails send --from ... --to ... --subject ... --text ... | jq '.id'Error output
Errors always exit with code 1 and output structured JSON to stdout:
{ "error": { "message": "No API key found", "code": "auth_error" } }Agent & CI/CD usage
CI/CD
Set RESEND_API_KEY as an environment variable — no resend login needed:
# GitHub Actions
env:
RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
steps:
- run: |
resend emails send \
--from "deploy@yourdomain.com" \
--to "team@yourdomain.com" \
--subject "Deploy complete" \
--text "Version ${{ github.sha }} deployed."AI agents
Agents calling the CLI as a subprocess automatically get JSON output (non-TTY detection). The contract:
- Input: All required flags must be provided (no interactive prompts)
- Output: JSON to stdout, nothing to stderr
- Exit code:
0success,1error - Errors: Always include
messageandcodefields
Configuration
| Item | Path | Notes |
|---|---|---|
| Config directory | ~/.config/resend/ |
Respects $XDG_CONFIG_HOME on Linux, %APPDATA% on Windows |
| Credentials | ~/.config/resend/credentials.json |
0600 permissions (owner read/write) |
| Install directory | ~/.resend/bin/ |
Respects $RESEND_INSTALL |
License
MIT