Repository navigation
AhaSend CLI v0.3.0
This release adds the contacts, lists, templates and campaigns commands, and makes exit codes, input checks and output consistent across commands. Read Breaking changes before upgrading scripts written for v0.2.0.
Breaking changes
Exit codes
- An API error exits by its HTTP status in every format: 401 → 2, 400/422 → 4, 404 → 5, 403 → 6, 429 → 8, others → 1; it exited 1, and 0 with
--output json. See Exit Codes. - A network failure or timeout exits with 7 instead of 1, as do a dropped listen stream and an unreachable SMTP server.
- A command-line mistake, such as an unknown flag, a bad value or a missing argument, exits with 4 instead of 1;
ahasend <group> <unknown>exited 0. - An unknown profile, a missing default profile, or a profile with an invalid
api_url,api_keyoraccount_idexits with 3 before any request (an unknown profile exited 5 and a missing default profile 2);auth statusexits nonzero when the key cannot be verified. - A command with per-item results exits with 1 when any item failed;
messages sendcounts recipients the API does not queue. - Ctrl+C or SIGTERM stops
messages sendandmessages cancelcleanly with exit code 130, and a closed stdout pipe exits with 1 instead of SIGPIPE.
Input checks
- Input the CLI can check fails with exit code 4 before any request: IDs, limits, times, addresses, domains, scopes, lengths, URLs, headers, and files, which must be UTF-8 text.
- An empty value fails instead of meaning "not given", so
--api-key "$KEY"withKEYunset no longer falls back to the profile, andsuppressions wipe --domain ""no longer wipes every domain (Empty Values). - Repeatable flags split on commas and trim each item; quote a value that holds a comma (Repeatable Flags).
- Relative times are a whole number and a lowercase unit (
m,h,d,w,mo,y);30days,7Dand3h30mfail (Time Values). messages sendrefuses--sandbox-resultwithout--sandbox,--max-concurrencyabove 10, a recipients file with no recipients or an unknown key, AMP-only content, and inline content with its file flag.messages send --attachrefuses attachments over 29 MB encoded, and an HTTP 413 on a send with attachments stops the rest of the send.- Flags that cannot be combined fail, such as
--api-keywith--profile, ordomains create --formatwith--output json. - Security:
messages sendandsmtp sendrefuse a--headerthat the API or the command sets itself, such asTo,BccorAhaSend-Sandbox. - Security:
smtp sendrefuses a server without STARTTLS, except onlocalhost.
Prompts
- A missing required value is asked for only on a terminal, and otherwise fails; the
routes createandwebhooks createwizards ask only for required values. - Destructive commands,
auth logout --allandcampaigns sendshare one prompt on stderr:yoryesgoes ahead, anything else exits with 4 (Confirmation Prompts).
Flags and defaults
--limitdefaults to the API's page size on every list command.domains list --statusandwebhooks list --enabledfilter on the API side, and--enabled=falselists only disabled webhooks and routes.messages sendleaves tracking to the account's settings unless--track-opensor--track-clicksis given.messages send --idempotency-keyis sent as given for one request, and as<key>-Nfor each request of a larger send.webhooks createdefaults to--scope global, and--domainsneeds--scope scoped;routes createrequires--recipientand enables the route.domains verifyandcheck-dnstake--show-recordsinstead of--verbose.smtp send --testneeds only--server, and reports the connection and the login apart.auth loginkeeps the existing default profile,auth logout --profilelogs out that profile, andAHASEND_DEFAULT_PROFILEis no longer read.- Deprecated on/off flags, such as
--disable, take no value and cannot be combined with their replacements.
Output
domains createwrites its DNS instructions and--formatrecords, anddomains verifyandcheck-dnstheir tips, to stderr instead of stdout:> zone.txtsaves the domain details, and2>the records (ahasend domains create).- Errors go to stderr outside
--output json; prompts, warnings and logs always go to stderr, and errors no longer print the usage text. --output jsonprints the API's response body unchanged, including for delete,pingandsuppressions check, and a CLI error as{"error": true, "message": "..."}(JSON Output).authcommands print new JSON shapes, and no format prints the API key.messages sendoutput counts recipients, and a send of more than 100 prints one result per request.- Table and plain output show local times with their zone and no
N/A-style placeholders; CSV shows RFC 3339 times and empty cells for missing values. - CSV columns change across most commands (CSV Format).
Added
- Install with Homebrew (
brew install ahasend/tap/ahasend), Scoop, npm (npm install -g @ahasend/cli), theinstall.shandinstall.ps1scripts, or the .deb and .rpm packages on the release page (installation instructions). WinGet, Chocolatey and AUR packages follow. contactsandlistscommands manage contacts, contact lists and list members.templatesandcampaignscommands manage transactional templates, campaigns and campaign templates, with API key scopes for each.messages send --template-id,--from-nameand--reply-to.--ip-allow-liston API keys restricts the IPs that can use them.--idempotency-keyon thecreatecommands of most groups.domains createandupdatetake the sending type, DKIM and subdomain settings, and domain output shows them.subaccounts unpause-domain, and the aliasessubaccounts pause,resumeandunpause.- Suppressions show whether they are protected, and
suppressions listfilters by time. messages getshows delivery attempts and takes a Message-ID;messages list --statustakesopenedandclicked.--scheduletakes relative times such as2h.- Webhook events can be given by key (
delivered) or type (message.delivered). routes createshows the signing secret in every format, androutes list --domainfilters by domain.smtp send --password-stdin.--non-interactiveon more commands,-ffor--force, and--beforeon list commands.stats --sender-domainis repeatable, andstats delivery-timeshows delivered counts.- Shell completion of flag values and profile names.
- ARM64 builds for Linux, Windows and Docker,
THIRD_PARTY_NOTICESin each release, and static binaries built with Go 1.27.2.
Changed
- AhaSend Go SDK 0.3.2; JSON output gains the API's new fields.
- Flags with fixed values take any case.
- DELETE requests are sent once, retries honour
Retry-After, andmessages send --max-retriesis the total number of resends (Rate Limiting). messages sendprints where it saved the failed recipients, and saves those of unknown outcome, such as after a timeout, to a separate file instead of resending them (ahasend messages send).- Table and plain output share one layout per item, name before ID (Output Values).
- List output ends with an item count and the page flags; output has no emoji, and success messages share one wording.
- A plain-HTTP API, webhook or route URL prints a warning, and so does
auth switchto a profile whose key fails its check. - Errors say what to check on a network failure and how long a
Retry-Afterasks to wait. - Log colors appear only on a terminal and respect
NO_COLOR;--debuglogs at most 64 KiB of a body. statsandmessages listsend only the filters you give.- Hidden prompts handle Backspace, Ctrl+U and a pasted CRLF line ending, and restore the terminal when stopped by a signal;
smtp sendhides its password. ahasend --versionshows the build time in RFC 3339, such as2026-10-14T09:00:00Z. It is the time of the release's commit, so every download of a release prints the same version.
Deprecated
These names still work for one release, hidden from the help, with a notice on stderr (Deprecated Flags and Commands):
--tags(now--tag) and--cursor(now--after).messages send --text-template,--html-templateand--amp-template(now--text-file,--html-fileand--amp-file).--interactive(prompting is the default;--non-interactiveturns it off).--enable,--disableand--disabled(now--enabledand--enabled=false).webhooks update --no-eventsand--clear-domains(now--clear-eventsand--scope global), and the--no-*flags ofroutes update(now=false).domains edit(nowdomains update).stats --chart,--raw,--show-totals,--classification,--trendsand--show-domains, andsmtp send --credential-id, which do nothing.
Removed
- Confirmation prompts no longer fetch the resource or ask you to type a word.
webhooks getandupdateno longer show an always-emptySecret.auth statusandauth loginno longer save the account name or a profilename, and the configuration file no longer gets apreferencesblock; existing keys stay.routes update --clear-recipient, which the API refused.
Fixed
subaccounts --websitetakes a domain, the format the API requires.- SMTP credential output lists ports 587, 25 and 2525, not 465.
auth login,statusandswitchcheck that the key belongs to the account, andauth logoutpicks the next default profile predictably.- Profile names keep their case and dots, and concurrent
authcommands no longer lose profiles. - DNS instructions mark records as required or optional, and
domains create --formatwrites valid BIND, Terraform and Cloudflare records. - Listen commands forward events in order and unchanged, close cleanly, skip unreadable messages, and say when a temporary webhook or route outlives them;
routes listen --slim-outputshows the sender, recipient and subject. smtp sendtimes out on an unresponsive server and shows the server's reply on a refused send.smtp sendprints the Message-ID of the message it sent, whichmessages gettakes, and its JSON output has the keys of its CSV output.- Large attachments on slow connections no longer time out (Network Limits).
- Files with a UTF-8 byte order mark are read correctly.
messages send --recipientsreadssubstitution_dataand short CSV rows, and keeps large numbers exact.messages send --max-concurrencyabove 1 stays within the API's rate limit instead of getting HTTP 429s.messages send --show-metricsprints its metrics, and the progress bar fits the terminal.- Valid addresses such as
o'brien@example.com, and non-ASCII--reasontext up to the API's limit, are accepted. suppressions checkshows every suppression of the address.routes getCSV output has its values.- Commands work with an
account_idin any accepted UUID form. - Requests send the CLI's version in
User-Agent, and release builds report it. - Help texts list each flag's allowed values and describe what the API does.
Security
~/.ahasendand its files are readable by your user only.--debugredacts secrets in request and response bodies.- Listen commands send the API key in a header, over
wss://only. - Listen
--skip-verifyapplies only to, and requires,--forward-to, which refuses URLs with credentials. - Table and plain output escape control characters from the API.
- Forwarded events are signed with a cryptographically random secret, which the listen banner labels
Forwarding secret (signs forwarded events).
Installation
See the installation instructions.
Checking a download
checksums.txt has the SHA-256 of every asset. Download it next to the files, then run sha256sum --ignore-missing -c checksums.txt on Linux, or shasum -a 256 --ignore-missing -c checksums.txt on macOS.