CLI
Send email, follow delivery and manage domains, webhooks and API keys from a terminal or a script with the usend CLI.
usend is the command-line tool for uSend. It sends email, shows what happened to it, adds and verifies domains, and manages webhooks and API keys. It prints readable tables in a terminal and JSON everywhere else, so the same commands work for people, shell scripts, CI jobs and AI agents.
Installation
curl -fsSL https://usend.et/install.sh | bashbrew install usend/tap/usendgo install usend.et/cli/cmd/usend@latestirm https://usend.et/install.ps1 | iexThe script installs the latest release for macOS or Linux (Intel or ARM, Alpine included) to ~/.usend/bin, refuses a download whose SHA-256 does not match the release's checksums.txt, and adds the folder to your shell's PATH. For a specific version, pass it: curl -fsSL https://usend.et/install.sh | bash -s v1.0.0. Every release, with its checksums, is at github.com/usend/usend-cli/releases.
Check it works:
usend versionSign in
Create an API key in the dashboard, then:
usend loginusend login asks for the key (the input is hidden), checks it against the API and saves it in your user configuration directory (~/.config/usend/config.json on Linux, ~/Library/Application Support/usend/config.json on macOS, %AppData%\usend\config.json on Windows), readable only by you. You can also pass it directly, or pipe it in from a secret store:
usend login --key us_xxxxxxxxx
op read "op://Dev/usend/key" | usend loginA key with Sending access can send email but nothing else; the other commands need Full access. usend login tells you which one you saved.
Profiles
Keep several keys (two organizations, or production and staging) as named profiles:
usend login --profile staging
usend domains list --profile stagingThe last profile you logged in with is the default. usend whoami shows which key (masked), profile and API address are in use, and usend logout removes a saved key from this computer (--all removes every profile). Logging out does not revoke the key: delete it with usend api-keys delete or in the dashboard.
Send email
usend send \
--from "Acme <[email protected]>" \
--to [email protected] \
--subject "Your order has shipped" \
--html-file shipped.html \
--text "Your order is on its way."| Flag | |
|---|---|
--from | Sender, with an optional display name. The domain must be verified. |
--to | Recipient. Repeat the flag for more (up to 50 in total). |
--subject | Subject line |
--html, --html-file | HTML body, inline or from a file (- reads standard input) |
--text, --text-file | Plain-text body, inline or from a file |
--cc, --bcc, --reply-to | Repeatable |
--scheduled-at | Send later: an ISO 8601 time or a phrase such as "in 1 hour" or "tomorrow at 9am" |
--tag name=value | Tag the email (repeatable) |
--template, --var KEY=value | Send a published template instead of a body |
--idempotency-key | Retries with the same key send only once |
Test without sending real mail by writing to [email protected] or [email protected].
Follow sent email
usend emails list # newest first
usend emails list --to [email protected] --limit 20
usend emails get 4ef9a417-… # status, recipients, opens, clicks, text body
usend emails events 4ef9a417-… # delivery timeline with bounce reasons
usend emails cancel 4ef9a417-… # only while it is scheduledLists show 50 rows by default; when there are more, the tool prints the command for the next page (--after <id>).
Domains
usend domains add acme.com # prints the DNS records to publish
usend domains get acme.com # status and records, by name or ID
usend domains verify acme.com # check the DNS again after publishing
usend domains listusend domains get prints the records as a table you can copy into your DNS host:
RECORD TYPE NAME PRIORITY STATUS VALUE
DKIM TXT usk3m9q2xa._domainkey - verified p=MIIBIjANBgkqh…
SPF CNAME send - verified send.umta.et
SPF CNAME send-ses - pending send-ses.umta.et
DMARC TXT _dmarc - pending v=DMARC1; p=none; rua=mailto:[email protected];Names are relative to your domain (@ is the domain itself). Verification runs in the background; run usend domains get again to see the result. See Verify a domain for what each record does.
Received email
usend inbound list
usend inbound list --to [email protected] --search invoice
usend inbound get 9b1c… # headers, attachments and the text bodyWebhooks
usend webhooks list
usend webhooks create --endpoint https://acme.com/hooks/usend \
--event email.delivered --event email.bounced
usend webhooks delete 2c7e…--event takes one event type per flag or several separated by commas. The signing secret is printed when the webhook is created; see Verify signatures.
API keys
usend api-keys list
usend api-keys create --name "CI" --permission sending_access
usend api-keys create --name "Staging" --save-profile staging
usend api-keys delete 7a0d…A new key's secret is shown once. With --save-profile it is saved as a profile instead and never printed. Deleting a key stops every program that uses it at once; in a terminal the tool asks first (--yes skips the question).
Request logs
usend logs list # the last 15 days of API requests
usend logs list --status 4xx --days 2
usend logs list --method POST --search /emailsUse it to see why a request from your application failed.
Check your setup
usend doctor OK Config /home/ann/.config/usend/config.json
OK API key us_abc...9f2e from profile default
OK API URL https://api.usend.et (default)
OK Network api.usend.et answered in 84 ms
OK Authentication key accepted (full access)
All good.It checks the configuration file and its permissions, that an API key is set, that the API can be reached, and that the key is accepted. It exits with status 1 when a check fails.
JSON output
Every command prints JSON when you pass --json, or automatically when its output is not a terminal (piped into another program or captured by a script or an AI agent). The JSON is the API's own response:
usend emails list --json | jq -r '.data[] | select(.last_event == "bounced") | .id'
id=$(usend send --from [email protected] --to [email protected] --subject Hi --text Hi | jq -r .id)Errors go to standard error with a non-zero exit status: the API's error object in JSON mode, a one-line message in a terminal.
{"message":"The `from` domain is not verified.","name":"validation_error","statusCode":422}| Exit status | Meaning |
|---|---|
0 | Success |
1 | The API refused the request, or it could not be reached |
2 | Wrong flags or arguments |
Environment variables
| Variable | |
|---|---|
USEND_API_KEY | API key to use instead of the saved profile (an explicit --profile still wins). Handy in CI. |
USEND_BASE_URL | API address, default https://api.usend.et |
--base-url overrides both for one command. The full API key is never printed: whoami, doctor and login show only its first and last characters.
Every command has --help, for example usend send --help.