Remote CLI (fsmtp)¶
The remote CLI connects to a FastSMTP server over HTTPS for remote management. For running the server itself, see the server CLI.
Every command outside the config group takes --profile/-p <name> to address a server
other than the default one, so a single workstation can manage several deployments.
Update conventions
Every update command sends only the options you name, so an omitted option
leaves that setting untouched. Options backed by a nullable column go one
further: passing an empty string (--option '') clears the stored value -
the CLI sends an explicit JSON null. This applies to users update --email,
recipient update --local and rules rule update --webhook-url. Nullable
boolean flags use true/false/inherit instead - see
Domain Management.
Deletes are recoverable
users delete, domain delete and recipient delete are soft: the entry
disappears from every list and lookup but can be brought back with the matching
restore command. Each delete keeps its usual prompt (Delete domain <id>?),
reports Domain <id> deleted and names the way back
(Restore with: fsmtp domain restore <id>; a recipient's restore takes the
domain ID first: fsmtp recipient restore <domain-id> <id>). restore reports
Domain restored and prints the entry's detail panel. --include-deleted shows
deleted (restorable) entries in list and get: the red Deleted column (or
row, in get) appears only when the output holds a deleted entry, and deleted
rows are dimmed. delete --purge is the permanent removal: superuser only, it
works only on an entry that is already deleted, asks its own confirmation
(Permanently delete domain <id>? This cannot be undone.) and reports
Domain <id> purged. Run it on a live entry and the CLI prints the server's
refusal as-is, Domain must be deleted before it can be purged, and exits 1.
API keys are the exception: auth delete-key cannot be undone, and
auth keys --include-deleted lists deleted keys for auditing only. ops log list
--include-deleted is different again: it reads a deleted domain's delivery
history, it does not list deleted log rows. See
Deletion, restore and purge for what each
delete cascades to.
Configuration¶
# Initialize configuration interactively
fsmtp config init
# Create/update a profile
fsmtp config set myprofile \
--url https://fastsmtp.example.com \
--api-key "your-api-key"
# Adjust the request timeout, or skip TLS verification against a self-signed server
fsmtp config set myprofile --timeout 60 --no-verify-ssl
# Set default profile
fsmtp config use myprofile
# Show current configuration (API keys are masked)
fsmtp config show
# Show it with the API keys in clear
fsmtp config show --show-keys
# Delete a profile
fsmtp config delete myprofile
Profiles live in ~/.fastsmtp/config.toml, or wherever FSMTP_CONFIG points. The file
holds API keys in clear, so fsmtp creates it 0600 inside a 0700 directory instead of
leaving the mode to your umask, and tightens the file again on every save. A config
written by a version before this behaviour existed was created with the umask's mode,
which is world-readable under the common 022: run any fsmtp config set to have it
repaired, or check it yourself with ls -l ~/.fastsmtp/config.toml.
An existing directory is never re-chmod-ed, since FSMTP_CONFIG may point into one
shared with other things. Keys can also be passed per-invocation with FSMTP_API_KEY,
which overrides the profile and writes nothing to disk.
Authentication¶
# Show current user info
fsmtp auth whoami
# List your API keys
fsmtp auth keys
# Also show deleted and retired keys (keys cannot be restored). Keys retired before
# v0.5.0 carry no deletion time and show as "retired" in the Deleted column
fsmtp auth keys --include-deleted
# Create a new API key (optionally expiring after N days)
fsmtp auth create-key "CI/CD" --expires 90
# Attach scopes to the key (repeat the option for several). A key created without
# any scope cannot reach the recipient, rule or delivery-log routes at all
fsmtp auth create-key "CI/CD" --scope recipients:read --scope logs:read
# A key that should be able to do whatever its owner can
fsmtp auth create-key "CI/CD" --scope admin
# Rotate an API key (the old key is deleted)
fsmtp auth rotate-key <key-id>
# Delete an API key. Deleted keys cannot be restored; create or rotate instead
fsmtp auth delete-key <key-id>
User Management¶
Every command in this group requires a superuser API key. Accounts have no password - they authenticate with API keys - so no command here takes one.
# List all users
fsmtp users list
# Include deleted (restorable) users
fsmtp users list --include-deleted
# Get user details (a deleted user is not found without --include-deleted)
fsmtp users get <user-id>
fsmtp users get <user-id> --include-deleted
# Create a user (only the username is required)
fsmtp users create alice --email alice@example.com
# Create a superuser
fsmtp users create alice --email alice@example.com --superuser
# Rename a user or change their email
fsmtp users update <user-id> --username bob --email bob@example.com
# Clear a user's email (nullable column: '' sends an explicit null)
fsmtp users update <user-id> --email ''
# Deactivate an account, or grant/revoke superuser
fsmtp users update <user-id> --inactive
fsmtp users update <user-id> --superuser
fsmtp users update <user-id> --no-superuser
# Delete a user (prompts; --force skips the prompt). Soft: the user's API keys
# are revoked for good, memberships come back on restore. Prints
# "User <id> deleted" and "Restore with: fsmtp users restore <id>"
fsmtp users delete <user-id>
# Restore a deleted user and their domain memberships. Prints "User restored", then
# "API keys revoked at deletion are not restored; create new keys."
fsmtp users restore <user-id>
# Permanently remove an already-deleted user with their keys and memberships (superuser)
fsmtp users delete <user-id> --purge
Options left off are left alone
users update sends only the options you name, so an omitted flag leaves that
column untouched, while --email '' clears the stored address. At least one
option is required.
Domain Management¶
Domains carry three-valued flags: true and false pin the setting for the domain,
inherit clears it so the domain follows the server-wide default, and leaving the option
off entirely leaves the setting untouched.
# List domains you have access to
fsmtp domain list
# Include deleted domains (all of them for a superuser, those you own otherwise)
fsmtp domain list --include-deleted
# Get domain details (a deleted domain is not found without --include-deleted; owner only)
fsmtp domain get <domain-id>
fsmtp domain get <domain-id> --include-deleted
# Create a new domain
fsmtp domain create example.com
# Create a domain that archives every raw message to S3
fsmtp domain create example.com --preserve-raw-message true
# Update domain settings
fsmtp domain update <domain-id> --disabled
fsmtp domain update <domain-id> --verify-dkim true --reject-dkim-fail false
# Stop overriding the server-wide raw-preservation default
fsmtp domain update <domain-id> --preserve-raw-message inherit
# Delete a domain (owner). Soft: its recipients are deleted with it and their
# queued deliveries cancelled; rulesets and members come back on restore. Prints
# "Domain <id> deleted" and "Restore with: fsmtp domain restore <id>"
fsmtp domain delete <domain-id>
# Restore a deleted domain and the recipients deleted with it (owner). Prints
# "Domain restored" and the domain's details
fsmtp domain restore <domain-id>
# Permanently remove an already-deleted domain with its recipients, rulesets and
# members (superuser). Delivery history is kept with its domain link cleared
fsmtp domain delete <domain-id> --purge
# Manage domain members
fsmtp domain member list <domain-id>
fsmtp domain member add <domain-id> <user-id> --role admin
fsmtp domain member update <domain-id> <user-id> --role member
fsmtp domain member remove <domain-id> <user-id>
The authentication flags are superuser only, and apply at receive time
--verify-dkim, --verify-spf, --reject-dkim-fail and --reject-spf-fail each
take true, false or inherit, and only a superuser may set one to true or
false: the server answers 403 to a domain admin, since these decide whether the
operator's own MX checks and rejects mail for that domain. inherit clears the
override so the domain follows the server-wide FASTSMTP_SMTP_* setting and is
open to domain admins; leaving the option off a domain update leaves that setting
alone. Whatever a domain ends up with decides what happens to mail addressed to it:
a check runs for a message if any recipient's domain verifies that mechanism, only
the domains that asked for it see the result, and a domain only rejects on a
mechanism it also verifies, so --reject-dkim-fail true --verify-dkim false
rejects nothing. A message with recipients on several domains is refused for
everyone as soon as one of those domains refuses it. See
Per-domain overrides.
Raw preservation needs S3
--preserve-raw-message true is rejected with a 422 when the server has no S3
storage configured; the CLI prints the missing settings. See
Raw Message Preservation.
Recipient Management¶
Recipients live under a domain, so every command takes the domain ID first.
# List recipients for a domain
fsmtp recipient list <domain-id>
# Include deleted recipients (admin). Works on a deleted domain too, for its owners
fsmtp recipient list <domain-id> --include-deleted
# Get recipient details (a deleted recipient is not found without --include-deleted)
fsmtp recipient get <domain-id> <recipient-id>
fsmtp recipient get <domain-id> <recipient-id> --include-deleted
# Create a recipient with webhook (omit --local for a catch-all)
fsmtp recipient create <domain-id> https://n8n.example.com/webhook/email --local support
# Send extra headers with the webhook request
fsmtp recipient create <domain-id> https://n8n.example.com/webhook/email \
--local support \
--header "X-Token=secret"
# Update recipient
fsmtp recipient update <domain-id> <recipient-id> \
--webhook https://new-webhook.example.com/email
# Stop delivering to a recipient without deleting it, and turn it back on
fsmtp recipient update <domain-id> <recipient-id> --disabled
fsmtp recipient update <domain-id> <recipient-id> --enabled
# Clear the local part to turn a recipient into the domain's catch-all
fsmtp recipient update <domain-id> <recipient-id> --local ''
# Delete recipient. Soft: its pending and failed deliveries are cancelled (they stay
# cancelled after a restore; re-queue them with `fsmtp ops log retry`). Prints
# "Recipient <id> deleted" and "Restore with: fsmtp recipient restore <domain-id> <id>"
fsmtp recipient delete <domain-id> <recipient-id>
# Restore a deleted recipient (the domain itself must not be deleted; restore it
# first otherwise). Prints "Recipient restored" and the recipient's details
fsmtp recipient restore <domain-id> <recipient-id>
# Permanently remove an already-deleted recipient (superuser). Its delivery
# history is kept with the recipient link cleared
fsmtp recipient delete <domain-id> <recipient-id> --purge
Rule Management¶
Rulesets and rules are nested under a domain too. Rules have no name and no per-rule
priority: they are evaluated in the ruleset's own order, which rule reorder sets.
# List rulesets for a domain
fsmtp rules list <domain-id>
# Get ruleset with all rules
fsmtp rules get <domain-id> <ruleset-id>
# Create a ruleset
fsmtp rules create <domain-id> "Spam Filter" --priority 10
# Update ruleset
fsmtp rules update <domain-id> <ruleset-id> --priority 20 --no-stop-on-match
# Delete ruleset
fsmtp rules delete <domain-id> <ruleset-id>
# List the rules in a ruleset
fsmtp rules rule list <domain-id> <ruleset-id>
# Show one rule
fsmtp rules rule get <domain-id> <ruleset-id> <rule-id>
# Create a rule within a ruleset (appended at the end)
fsmtp rules rule create <domain-id> <ruleset-id> \
--field subject \
--operator contains \
--value "[SPAM]" \
--action tag \
--tag spam
# Archive the raw MIME message in S3 whenever the rule matches
fsmtp rules rule create <domain-id> <ruleset-id> \
--field has_attachment \
--operator exists \
--value "" \
--action forward \
--preserve-raw
# Match case-sensitively (the default is --ignore-case)
fsmtp rules rule create <domain-id> <ruleset-id> \
--field subject \
--operator contains \
--value "URGENT" \
--action tag \
--tag urgent \
--case-sensitive
# Update a rule (rules are addressed by domain, not by ruleset)
fsmtp rules rule update <domain-id> <rule-id> --action drop
fsmtp rules rule update <domain-id> <rule-id> --no-preserve-raw
# Remove a rule's webhook override so it falls back to the recipient's URL
fsmtp rules rule update <domain-id> <rule-id> --webhook-url ''
# Set the evaluation order of a ruleset's rules
fsmtp rules rule reorder <domain-id> <ruleset-id> <rule-id-1> <rule-id-2>
# Delete a rule
fsmtp rules rule delete <domain-id> <rule-id>
Valid fields are from, to, subject, body, has_attachment,
attachment_names, attachment_types, dkim_result, spf_result, or
header:X-Custom-Header. The two attachment fields hold one value per part and match
when any single part satisfies the condition; see
Matching on attachments. Valid operators are equals, contains,
regex, starts_with, ends_with and exists. Valid actions are forward, drop,
tag and quarantine. regex patterns use
RE2 syntax (no backreferences or
lookaround); the server rejects patterns RE2 cannot compile.
Operations¶
# Check server health
fsmtp ops health
# Check server readiness (includes DB)
fsmtp ops ready
# Test a webhook URL with a synthetic payload
fsmtp ops test-webhook https://webhook.site/xxx
# Control what the test payload says
fsmtp ops test-webhook https://webhook.site/xxx \
--from billing@sender.com \
--to invoices@example.com \
--subject "Invoice 2025-01" \
--body "Payload under test"
# View delivery logs
fsmtp ops log list <domain-id>
fsmtp ops log list <domain-id> --status failed --limit 50
# Deliveries cancelled because their recipient or domain was deleted
fsmtp ops log list <domain-id> --status cancelled
# Read the history of a deleted domain (owner or superuser). This resolves the
# deleted domain; it does not list deleted log rows
fsmtp ops log list <domain-id> --include-deleted
# Get delivery log details
fsmtp ops log get <log-id>
# Retry a failed, exhausted or cancelled delivery. A cancelled one is accepted
# only once its recipient and domain are live again. 409 details: "Domain is
# deleted; restore it before retrying", "Recipient is deleted; restore it
# before retrying", or "Delivery is no longer retryable" (the status changed
# under the request, or the recipient was purged - nothing left to send as)
fsmtp ops log retry <log-id>
See Delivery statuses for the status vocabulary.