uSend Docs
Resources

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
curl -fsSL https://usend.et/install.sh | bash

The 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:

Shell
usend version

Sign in

Create an API key in the dashboard, then:

Shell
usend login

usend 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:

Shell
usend login --key us_xxxxxxxxx
op read "op://Dev/usend/key" | usend login

A 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:

Shell
usend login --profile staging
usend domains list --profile staging

The 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

Shell
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
--fromSender, with an optional display name. The domain must be verified.
--toRecipient. Repeat the flag for more (up to 50 in total).
--subjectSubject line
--html, --html-fileHTML body, inline or from a file (- reads standard input)
--text, --text-filePlain-text body, inline or from a file
--cc, --bcc, --reply-toRepeatable
--scheduled-atSend later: an ISO 8601 time or a phrase such as "in 1 hour" or "tomorrow at 9am"
--tag name=valueTag the email (repeatable)
--template, --var KEY=valueSend a published template instead of a body
--idempotency-keyRetries with the same key send only once

Test without sending real mail by writing to [email protected] or [email protected].

Follow sent email

Shell
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 scheduled

Lists show 50 rows by default; when there are more, the tool prints the command for the next page (--after <id>).

Domains

Shell
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 list

usend domains get prints the records as a table you can copy into your DNS host:

Text
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

Shell
usend inbound list
usend inbound list --to [email protected] --search invoice
usend inbound get 9b1c…                 # headers, attachments and the text body

Webhooks

Shell
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

Shell
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

Shell
usend logs list                         # the last 15 days of API requests
usend logs list --status 4xx --days 2
usend logs list --method POST --search /emails

Use it to see why a request from your application failed.

Check your setup

Shell
usend doctor
Text
  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:

Shell
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.

JSON
{"message":"The `from` domain is not verified.","name":"validation_error","statusCode":422}
Exit statusMeaning
0Success
1The API refused the request, or it could not be reached
2Wrong flags or arguments

Environment variables

Variable
USEND_API_KEYAPI key to use instead of the saved profile (an explicit --profile still wins). Handy in CI.
USEND_BASE_URLAPI 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.