CLI
npm install -g spacepost # or run without installing: npx spacepost <command>, bunx spacepost <command>spacepost loginspacepost send --from "Acme <hi@acme.com>" --to you@example.com --subject Hello --text "It works"Run spacepost --help for all commands and spacepost <command> --help for one command’s options.
Authentication
Section titled “Authentication”spacepost login asks for your API key, checks it against the API and stores it in ~/.config/spacepost/config.json (mode 0600, honors XDG_CONFIG_HOME). Piped input works too: echo $KEY | spacepost login. In CI or scripts set the environment variable instead:
export SPACEPOST_API_KEY="sp_live_YOUR_API_KEY"spacepost whoamiThe key comes from --api-key, then SPACEPOST_API_KEY, then the stored login. The API base URL comes from --base-url, then SPACEPOST_BASE_URL, then the stored login, then https://api.spacepost.dev.
Commands
Section titled “Commands”| Command | What it does |
|---|---|
login, logout, whoami |
Manage credentials. whoami shows the masked key, base URL and domain count |
send |
Send an email |
emails list, emails get <id>, emails cancel <id> |
Inspect emails, cancel scheduled ones |
domains add <name>, list, get <id|name>, verify <id|name>, doctor <id|name>, rm <id|name> |
Manage domains by id or name |
keys create, keys list, keys rm <id> |
API keys |
webhooks list, webhooks create, webhooks rm <id> |
Webhook endpoints |
tenants list, create, get <ref>, pause <ref>, unpause <ref>, rm <ref> |
Platform mode tenants |
open |
Print (and open) the dashboard URL |
Global flags: --api-key, --base-url, --json, -y/--yes (skip confirmations), -h/--help, -v/--version. Colors honor NO_COLOR.
spacepost send \ --from "Acme <hello@acme.com>" \ --to user@example.com \ --subject "Hello from the CLI" \ --html "<p>It works.</p>"
spacepost send --from hello@acme.com --to a@example.com --to b@example.com --subject "Newsletter" --html-file ./email.htmlspacepost send --from hello@acme.com --to user@example.com --subject "Reminder" --text "Call at 3" --schedule "in 1 hour"echo "body from stdin" | spacepost send --from hello@acme.com --to user@example.com --subject Hi --text -| Flag | Description |
|---|---|
--from |
Sender on a verified domain (required) |
--to |
Recipient (required; repeat the flag for several) |
--subject |
Subject (required) |
--text, --html, --html-file |
Body: plain text (- reads stdin), inline HTML, or an HTML file. At least one is required; --html and --html-file cannot be combined |
--cc, --bcc, --reply-to |
Repeatable |
--tag name=value |
Repeatable tag |
--schedule "in 1 hour" |
ISO 8601 time or natural language |
--attach <file> |
Repeatable file attachment |
--tenant <id|external-id> |
Send on behalf of a tenant |
On success it prints ✓ Queued: <id> (with --json, the API response { "id": "..." }).
Emails
Section titled “Emails”spacepost emails list [--limit 20] [--after <id>] [--before <id>]spacepost emails get <id>spacepost emails cancel <id> # scheduled emails onlyDomains
Section titled “Domains”spacepost domains add acme.com [--region eu-central-1] [--tenant cust_123]spacepost domains list [--limit 20]spacepost domains get acme.com # id or name, includes DNS recordsspacepost domains verify acme.comspacepost domains rm acme.com [--yes]domains add prints the DNS records to create. --tenant makes the domain tenant-owned (see Platform mode).
domains doctor
Section titled “domains doctor”spacepost domains doctor <id|name> looks up every expected DNS record (CNAME, TXT, MX) in public DNS, compares it with what SpacePost expects and prints what is missing or wrong, with a fix for each. It detects the domain name appended twice by a DNS provider, several SPF records, a wrong MX priority and Cloudflare proxying.
$ spacepost domains doctor acme.comacme.com API status: pending
RECORD TYPE HOST RESULT✓ DKIM CNAME abc._domainkey.acme.com matches✗ SPF MX send.acme.com not found✗ SPF TXT send.acme.com found v=spf1 include:_spf.google.com ~all! DMARC TXT _dmarc.acme.com not found (recommended)
How to fix ✗ MX send.acme.com Add a MX record: name "send", value "feedback-smtp.eu-central-1.amazonses.com", priority 10. ✗ TXT send.acme.com Your SPF record lacks the SpacePost include. ...Use --dns-server 1.1.1.1 to bypass a stale local resolver cache. The exit code is 1 when a required record is missing or wrong, so it works as a CI check.
API keys and webhooks
Section titled “API keys and webhooks”spacepost keys create --name worker-prod --permission sending_access --domain <domain-id>spacepost keys listspacepost keys rm <id> [--yes]
spacepost webhooks listspacepost webhooks create --endpoint https://acme.com/webhooks/spacepost --event email.delivered --event email.bouncedspacepost webhooks rm <id> [--yes]keys create prints the token once. webhooks create prints the signing_secret. --event is repeatable (there is no comma-separated form), and --domain requires --permission sending_access.
Tenants (platform mode)
Section titled “Tenants (platform mode)”spacepost tenants create --name "Acme Inc" --external-id cust_123 --daily-limit 500spacepost tenants list [--limit 20]spacepost tenants get cust_123 # <ref> is our id or your external idspacepost tenants pause cust_123 --reason "Investigating spam reports"spacepost tenants unpause cust_123 # 403 for reputation pauses unless your team is trustedspacepost tenants rm cust_123 [--yes] # 422 while the tenant still has domainsSee Platform mode (tenants) for what tenants isolate and how domains and webhooks fit in.
Scripting
Section titled “Scripting”spacepost emails list --json | jq -r '.data[] | select(.last_event=="bounced") | .to[]'id=$(spacepost send --from hello@acme.com --to delivered@test.spacepost.dev --subject Test --text hi --json | jq -r .id)spacepost emails get "$id" --json | jq .last_eventDestructive commands (domains rm, keys rm, webhooks rm, tenants rm) ask for confirmation; in scripts and with --json pass --yes.
Exit codes and errors
Section titled “Exit codes and errors”| Code | Meaning |
|---|---|
0 |
Success |
1 |
API or runtime error (also domains doctor when a required record is missing) |
2 |
Usage error: unknown command or flag, missing required option |
Errors go to stderr with a hint where there is one. With --json they are printed as JSON on stderr: { "error": { "name", "message", "statusCode", "hint" } }.
Environment variables
Section titled “Environment variables”| Variable | Purpose |
|---|---|
SPACEPOST_API_KEY |
API key (overrides the stored login) |
SPACEPOST_BASE_URL |
Alternate API base URL |
SPACEPOST_APP_URL |
Dashboard URL printed by spacepost open |
XDG_CONFIG_HOME |
Where the login is stored ($XDG_CONFIG_HOME/spacepost/config.json) |
NO_COLOR |
Disable colors |