elastic-email-cli
v0.3.0
Published
Interactive CLI for the Elastic Email API
Maintainers
Readme
Key Features
- Interactive mode. Run
elastic-emailwith no arguments to get a keyboard-driven TUI: browse everything, pick with arrows, and send email through a guided wizard. - Script-friendly. Every command supports
--json, exit codes are stable, and piped/CI output carries no banner or decorations. - Full sending toolkit. From transactional emails, saved templates, and campaigns to whole lists or segments.
Requirements
- Node.js 20 or newer
- An Elastic Email account and API key (dashboard → Settings → API)
- A verified sending domain. Elastic Email only sends from verified domains, so your
--fromaddress must use one.
Getting Started
Install the elastic-email-cli package from npm:
npm install -g elastic-email-cli
elastic-email --helpor run it without installing:
npx elastic-email-cli --helpInstall from source
git clone https://github.com/ElasticEmail/elasticemail-cli.git
cd elasticemail-cli
npm install
npm run build
npm link # makes the elastic-email command available globally
elastic-email --helpAuthentication
The API key is resolved in this order: --api-key flag → ELASTIC_EMAIL_API_KEY environment variable → local config file.
elastic-email auth set-key # interactive, masked prompt
elastic-email auth set-key <key> # non-interactive (CI)
elastic-email auth set-key <key> --default-from [email protected]
elastic-email auth status # where the key comes from + live connection test
elastic-email auth clear # remove the local config⚠️
auth set-keystores the key in plaintext at~/.elastic-email-cli/config.jsonwith0600permissions. Convenient, but not an OS keychain - treat the file as a secret and prefer theELASTIC_EMAIL_API_KEYenvironment variable in shared or CI environments. The key is never printed to logs or errors (only a masked form).
Interactive mode
A bare invocation in a terminal opens the interactive mode (also available as elastic-email tui):
elastic-email- ↑/↓ move · Enter open/select · ←/→ pages · Space multi-select · Esc back · q quit.
- Browse templates, lists (and their contacts), contacts, segments (and their contacts), delivery events, verification results, sender domains, statistics, and account info.
- Guided send wizard: sender (with verified-domain hints), recipients - addresses, or multi-selected lists/segments on Marketing-plan accounts - content (text, HTML, or a saved template), review, send.
- Verify an email address interactively; forget me (
fin Account info) removes the local config after confirmation. - Prompts for an API key on first run.
In pipes, redirects, and CI (no TTY), a bare invocation prints plain help instead - interactive mode never breaks scripted usage.
Demo
elastic-email auth set-key
elastic-email emails send --to [email protected] --subject "Hello" --text "It works!" --from [email protected]
elastic-email emails status <transactionid>
elastic-email templates create "Welcome" --html-file welcome.html --subject "Welcome!"
elastic-email emails send --to-list Newsletter --template "Welcome" --from [email protected]JSON output
Add --json to any command for clean, machine-readable output:
elastic-email account info --json
elastic-email emails send --to [email protected] --subject Hi --text Hello --from [email protected] --json | jq '.messageId'Dry run
emails send --dry-run validates the input and prints the exact request that would be sent — no API call is made:
elastic-email emails send --to [email protected] --subject Hi --text Hello --from [email protected] --dry-runCommands
Auth
elastic-email auth set-key [key] # store the API key (interactive without [key])
elastic-email auth status # key source + live connection test
elastic-email auth clear # remove the local config (asks for confirmation)Sending
elastic-email emails send --to <email> --subject <s> --text <t> --from <email>
# send a transactional email
elastic-email emails send --to <email> --subject <s> --html "<b>Hi</b>" --from <email>
# HTML body (combine with --text for both parts)
elastic-email emails send --to "[email protected],[email protected]" ...
# multiple recipients (or repeat --to)
elastic-email emails send --to <email> --template <name> --from <email>
# send a saved template (--subject optional)
elastic-email emails send --to-list <list> --template <name> --from <email>
# send to a whole list (creates an Active campaign)
elastic-email emails send # interactive compose form on a TTY
elastic-email emails send ... --dry-run # validate + preview without sending
elastic-email emails status <transactionid>
# delivery counts for a sent email
elastic-email emails status <transactionid> --recipients
# include per-recipient address lists--from is required for every send unless you configured a default sender
(elastic-email auth set-key <key> --default-from [email protected]).
Templates
elastic-email templates list # sendable email templates
elastic-email templates list --all # every type (landing pages etc.)
elastic-email templates get <name> # metadata (--body prints the full body)
elastic-email templates create <name> --html-file <path>
# create an HTML template ("-" reads stdin)
elastic-email templates create <name> --html "<h1>Hi</h1>" --subject <s>
elastic-email templates delete <name> # asks for confirmation (--yes to skip)Contacts & lists
elastic-email contacts list # all contacts
elastic-email contacts get <email>
elastic-email contacts add <email> --first-name <n> --list <list>
elastic-email contacts add "[email protected],[email protected]" --list <list>
# comma-separated batch
elastic-email contacts delete <email> # asks for confirmation (--yes to skip)
elastic-email lists # your contact lists
elastic-email lists contacts <name> # contacts that belong to a list
elastic-email lists create <name> --allow-unsubscribe
elastic-email lists delete <name> # contacts are kept; asks for confirmationSegments
elastic-email segments list # segments with their rules
elastic-email segments contacts <name> # contacts matching a segmentSuppressions
elastic-email suppressions list # all suppressed addresses
elastic-email suppressions list --type bounces --search <text>
elastic-email suppressions add <email> --type unsubscribes
elastic-email suppressions delete <email>
# allow sending again; asks for confirmationDelivery events & verification
elastic-email events list # recent sends, opens, clicks, bounces...
elastic-email events list --from 2026-06-01T00:00:00 --limit 50
elastic-email verify <email> # deliverability check for one address
elastic-email verifications list # past verification resultsDomains & account
elastic-email domains list # sender domains + SPF/DKIM/MX/DMARC status
elastic-email domains get <domain>
elastic-email account info # account name, plan, limits, account-wide BCC
elastic-email account stats # sending statistics (default: last 30 days)
elastic-email account stats --days 7Pagination
Every list command (contacts list, templates list, segments list, events list,
suppressions list, lists, ...) paginates the same way - fetch the next page by
incrementing --page:
elastic-email contacts list --page 1 --page-size 50 # first page (default: page 1, 20 items)
elastic-email contacts list --page 2 --page-size 50 # next page
elastic-email templates list --page 3 --page-size 10
elastic-email contacts list --limit 10 --offset 20 # or raw limit/offset styleAn empty result means you've paged past the last item. In interactive mode use ←/→ to switch pages.
Destructive commands
contacts delete, lists delete, templates delete, suppressions delete and
auth clear ask before they act:
$ elastic-email contacts delete [email protected]
Delete contact [email protected]? This cannot be undone. [y/N]Enter or n cancels, y proceeds. Pass --yes (or -y) to skip the question.
When there is no terminal to ask — CI, a pipe, or --json — the command
refuses rather than guessing, and nothing is deleted:
$ elastic-email contacts delete [email protected] # in CI
Error: Refusing to delete contact [email protected] without confirmation
— not an interactive terminal. Pass --yes to confirm.So automation has to state the intent explicitly:
elastic-email contacts delete [email protected] --yesThis also makes destructive steps greppable in a pipeline: every place that
deletes something carries a visible --yes.
Exit codes
| Exit | error.code (JSON) | Meaning |
| ---- | ----------------------- | ---------------------------------------------------- |
| 0 | — | Success |
| 1 | general | Unexpected error |
| 2 | missing_api_key | No API key found |
| 3 | invalid_input | Invalid input: bad email, missing field, unknown flag |
| 4 | api_error | Elastic Email API / network error |
| 5 | confirmation_required | Destructive command blocked (no --yes, no terminal) |
| 130| interrupted | Interrupted: Ctrl+C (at a prompt or while running) or SIGINT |
| 143| terminated | Terminated by SIGTERM (e.g. a CI timeout) |
A destructive command refused for lack of confirmation exits 5, so automation can
stop and escalate for human approval instead of treating it as bad input and retrying.
With --json, errors are printed to stdout in a fixed shape, and the process
exits with the same code:
{
"error": {
"code": "confirmation_required",
"exitCode": 5,
"message": "Refusing to delete template \"Old\" without confirmation — not an interactive terminal. Pass --yes to confirm."
}
}code names are stable — branch on them rather than on message. A signal in
--json mode also produces this body (interrupted / terminated), so stdout
is never left empty.
Configuration via env
ELASTIC_EMAIL_API_KEY # API key (overridden only by --api-key)
ELASTIC_EMAIL_CLI_CONFIG_DIR # config directory (default ~/.elastic-email-cli)Shell autocomplete
elastic-email autocomplete # install instructions
elastic-email autocomplete zsh # or bash / fishContributing
npm install
./bin/dev.js --help # run from TypeScript source, no build needed
npm run dev -- <command> # same via npm (the -- is required)
npm testArchitecture notes, project layout, and development gotchas live in CLAUDE.md. See CONTRIBUTING.md for how to report bugs and open pull requests, and please follow our Code of Conduct.
Issues & Feedback
Feel free to contact us if you encounter any issues with the library. Please leave comments, concerns and requests on the Issues page.
License
MIT
