Configuration File
ahasend auth login creates the configuration file at ~/.ahasend/config.yaml (Windows: %USERPROFILE%\.ahasend\config.yaml). It holds your profiles and the name of the default profile. In this example, each YOUR_... value stands for an API key or the ID of its account:
- The file holds your API keys in plain text. The CLI creates
~/.ahasendwith mode0700and the file with mode0600, so only your user can read them. Keep the file out of source control. - The CLI reads only
default_profileandprofiles, and in each profileapi_key,api_urlandaccount_id. It ignores other keys and keeps them when it saves the file. It does not keep comments. - Before any request, the CLI checks the profile it uses:
api_keymust not be empty and must hold only printable ASCII characters,account_idmust be a UUID, andapi_urlmust be empty or an HTTP or HTTPS URL with a host and no path. An emptyapi_urluses the production API,https://api.ahasend.com. A profile that fails a check exits with code 3. - No environment variable chooses a profile or gives credentials. Use
--profile, or--api-keywith--account-id.
Profiles
A profile is a saved API key, account ID and API URL. Commands use the default profile unless you pass--profile:
auth loginsaves the profile under the namedefaultwhen you give no--profile.auth loginmakes the profile the default when there is no default profile, so the first profile you save becomes the default. Otherwise the default stays;ahasend auth switch <profile>changes it.- Profile names keep their case and dots. A name that matches no profile exactly finds the one profile that matches it ignoring case, so
--profile workfindsWork. - A profile that does not exist, or a missing default profile, exits with code 3 before any request.
CI Credentials
In CI, store the API key and account ID as secrets and pass them with flags:--api-key takes the place of a profile and always uses the production API, https://api.ahasend.com. It needs --account-id, and you cannot combine it with --profile. An empty value, as "$AHASEND_API_KEY" gives when the variable is not set, fails with exit code 4. The CLI does not fall back to a profile.
Global Flags
These flags work on every command, with one exception: theauth commands work on saved profiles, so auth status, auth switch and auth logout refuse --api-key and --account-id with exit code 4, and auth login has its own --api-key, --account-id and --profile flags, which its page describes.
Colors appear only in log lines and in the
webhooks listen and routes listen output, and only on a terminal. NO_COLOR set to a non-empty value, or TERM=dumb, also turns them off.
--verbose and --debug write their logs to stderr. --debug logs headers that carry credentials, and body values such as api_key, password or secret, as [REDACTED]. It logs at most the first 64 KiB of each body.
Output Formats
Choose a format with--output on each command:
Table and plain output are for reading:
- Times show in your local time zone, with the zone, such as
2026-09-01 12:30:00 CEST. - A field with no value shows
-in a table and is left out of plain output. - A list ends with a line such as
Showing 20 contacts, then(more available)when there is another page, and the--after <cursor>and--before <cursor>flags that fetch the next and the previous page. An empty page prints a line such asNo contacts foundin place of the list and theShowingline.
- Times are in RFC 3339, such as
2026-09-01T10:30:00Z, and a missing value is an empty cell. - CSV output has no pagination line. To page in a script, use
--output jsonand readpagination.next_cursor. - A list with nothing to show prints the header row alone.
JSON Output
With--output json, a command that calls the API prints the API’s response body unchanged: the same keys in the same order, including keys the CLI does not know, indented by two spaces. The fields are those of the API reference. A delete, cancel or wipe whose response has no body prints nothing.
Errors in JSON mode go to stdout:
- An API error whose body is JSON prints that body unchanged.
- An API error whose body is not JSON, such as an HTML page from a proxy, prints an object with the HTTP status and the request ID:
- An error the CLI raises itself, such as an invalid flag, prints an object like this:
messages send to more than 100 recipients, prints a JSON array with one element per request. The auth commands and smtp send print objects of their own, which their pages describe.
webhooks listen and routes listen ignore --output: they always print their event stream as text, for people to read. To process the events in code, forward them to a local endpoint with --forward-to.
Stdout and Stderr
Stdout holds only the command’s output. Prompts, warnings, notices and logs go to stderr in every format, with one exception: in table and plain output, the notices that follow a created API key, SMTP credential, webhook or route, such as the one that tells you to save its secret, go to stdout with the output they belong to. Errors go to stderr in table, plain and CSV output, and to stdout in JSON output.Lists and Pagination
List commands return one page at a time:--limittakes 0 to 100. The default, 0, uses the API’s page size.--after <cursor>fetches the page after a cursor, and--before <cursor>the page before it. You cannot combine them.
YOUR_CURSOR with the cursor that the previous page printed after --after.
Prompts
Prompts go to stderr, and the CLI reads the answers from stdin.Confirmation
These commands ask for confirmation before they act: everydelete command, auth logout --all, campaigns send, lists members remove, messages cancel, subaccounts suspend, suppressions wipe, templates draft discard and templates versions restore. Answer y or yes to go ahead. Any other answer, or no input, cancels the command with exit code 4. --force, or -f, skips the prompt; scripts need it.
webhooks listen and routes listen ask whether to force a reconnect when another connection holds the stream. They have no --force: any answer but y or yes, or no input, exits with code 4.
No other command asks for confirmation. For example, auth logout <profile> removes the profile at once, campaigns unschedule unschedules the campaign at once, and auth login to a profile that exists replaces its credentials.
Missing Values
Commands that ask for a missing required value, such asdomains create and messages send, ask only when stdin is a terminal. Otherwise, or with --non-interactive, a missing value fails with an error such as --name is required and exit code 4.
auth login works differently: without --api-key and --account-id, it reads them from stdin even when stdin is not a terminal, and it has no --non-interactive. Empty stdin cancels it with exit code 4. See auth login.

