CLI
Run safe outbound campaigns and inspect SendSets from a terminal or agent.
sendsets is the client for the public agent API: everything a campaign needs
can be set up from it, with no dashboard. It stores credentials under
~/.config/sendsets, prints readable screens on a terminal, and emits stable
JSON when piped or when --json is set. Non-interactive execution never prompts.
The agent quickstart walks the commands in order.
sendsets login
sendsets doctor
sendsets run campaign.yaml --wait
sendsets events tail --jsonCommands
| Command | Purpose |
|---|---|
sendsets login, logout, whoami | Sign in, sign out, and see the workspace, credential, scopes and agent policy |
sendsets auth status|token|switch|refresh | Manage the signed-in hosts |
sendsets doctor | Check auth, API, app connections, product events, mailboxes and the campaign runtime; a fix per gap; exit 1 when not ready |
sendsets status | What is happening in the workspace right now |
sendsets connection list|create|get|update|delete|rotate-secret|test | The app your campaigns call (app connections) |
sendsets events send <name>|list|get|tail | Product events, and the live event stream |
sendsets mailbox add|get|update|delete|enable|disable|test | Connect, inspect and control mailboxes |
sendsets mailbox warmup enable|disable|pause|resume|status | The mailbox's warmup |
sendsets mailbox provision [providers|quote|status|list|renewals|cancel] | Order managed mailboxes on new domains, and see when they renew (SendSets Cloud) |
sendsets mailbox health|recheck|hold|release|sync|identity | Domain authentication, sending hold, sync state, send-as |
sendsets campaign create|edit|status|steps|add-step|edit-step|delete-step | Build a campaign, including app_action and wait_for_event steps |
sendsets campaign senders|set-senders|validate|preflight|test-email|start|stop|logs | Check, test and run it |
sendsets campaign import campaign.yaml and sendsets run | A run file as a durable run |
sendsets inbox list|read|draft|reply | Unified inbox operations |
sendsets stats | Workspace sending summary |
sendsets policy show | Inspect the calling credential's effective policy and budget |
sendsets api <path> | Any endpoint the table does not name |
sendsets alias, config, upgrade, browse | The CLI itself |
Checklists and exit codes
doctor, connection test, mailbox test and campaign validate print a
checklist: a tick or cross per row, a dimmed fix: line under a failure, and a
closing verdict. Each exits 1 when the thing it checked is not ready. Piped or
with --json they emit {checks: [{name, status, detail, fix}], ready}.
An API error prints its code, request_id and, when the API knows one, the
fix line. Exit 4 is a credential problem, 2 a prompt with no terminal.
Connecting a mailbox
sendsets mailbox add --provider smtp_imap --email hello@acme.com --name Acme \
--smtp-host smtp.acme.com --smtp-port 587 --smtp-pass "$APP_PASSWORD" \
--imap-host imap.acme.com --imap-port 993 --daily-limit 20
sendsets mailbox add --provider gmailA Google or Microsoft mailbox needs a person to approve a consent screen. The
command prints the link (and opens it unless --no-browser), then waits for
the consent to land; the code never leaves the API. Piped, it prints the link
and the session id to poll instead.
Managed mailboxes
sendsets mailbox provision providers
sendsets mailbox provision renewals
sendsets mailbox provision quote --provider google --count 5 --domain acme-outreach.com --warmup
sendsets mailbox provision --provider google --count 5 --domain acme-outreach.com --warmup --wait
sendsets mailbox provision --provider google --count 25 --domains a.com,b.com,c.com --warmupThe quote prints every line and the totals; the order asks once (--yes skips
it), then prints the checkout link for a person (and opens it unless
--no-browser). A credential whose policy does not cover the spend gets an
approval link instead: hand it over and run the command again. With --wait
the checklist (order accepted, domain configured, mailboxes created, added to
SendSets, warmup enabled) updates until the mailboxes are ready. Exit status 1
when the order fails. Not offered on self-hosted instances.
Durable steps from the terminal
sendsets campaign add-step <id> --kind app_action --connection <connection_id> \
--path /audit --output-key audit --inputs '{"domain":"{{.Company}}"}'
sendsets campaign add-step <id> --kind wait_for_event --event report.opened --timeout-minutes 4320
sendsets campaign edit-step <id> <step_id> --next <other_step_id>--kind names the step type; --next links it to the step that follows.
campaign validate reports a wait with no timeout, a timeout that is not
routed, an app action without a connection, and a template that reads an
.App key no earlier step produces.
The separate sendsetsctl binary is for instance operators and database-level
recovery. It runs inside the backend container and is not an agent interface.
Run files
Run YAML maps directly to POST /v1/runs. A csv or leads_csv value is
resolved relative to the YAML file and parsed through the same header detection
and column normalization used by dashboard contact imports.
name: September founders
intent: launch
senders:
mode: auto
csv: founders.csv
daily_limit: 25
schedule:
mode: auto
timezone: America/Los_Angeles
stop_on_reply: true
opt_out_enabled: true
steps:
- name: Introduction
variants:
- subject: Quick question for {{company}}
body_plain: |-
Hi {{first_name}},
Would this be relevant? Reply no and I will not follow up.--dry-run changes the intent to draft; it still creates a durable record and
runs validation without launching. --wait follows the run until it is active,
waiting for approval, complete, denied, cancelled, or failed.
A step is an email unless type says otherwise. The three other types connect
the campaign to your product; listed steps are linked in order, and a durable
step routes by outcome (an app action stops on error, a wait continues or
stops on timeout per on_timeout):
steps:
- name: Intro
variants:
- subject: Quick question for {{.Company}}
body_plain: Hi {{.FirstName}}, ...
- name: Audit
type: app_action
app_action:
connection: app # an app connection's name or id
path: /audit
output_key: audit
inputs:
domain: "{{.Company}}"
- name: Wait for the report
type: wait_for_event
wait_for_event:
event: report.opened
timeout_minutes: 4320
on_timeout: stop # or continue (the default)
- name: Breathe
type: wait
wait:
minutes: 60
- name: Follow up
variants:
- subject: Your audit
body_plain: "Score: {{.App.audit.score}}"Testing a campaign with one prospect
sendsets campaign test CAMPAIGN_ID --prospect you@example.com --wait
sendsets events send report.opened --email you@example.com # from another shell, when it waitsThe walk-through runs on the real runtime: each email step is sent to the
prospect from the campaign's mailbox, each app action calls your app, and each
wait holds until the event arrives. Delays are skipped, the prospect never
becomes a lead, and the checklist updates as steps complete, printing the
captured .App.* values under the step that produced them. Exit status 1 when
the run fails. sendsets campaign test-runs CAMPAIGN_ID lists past runs.
Authentication and output
Use SENDSETS_TOKEN=ssk_... in CI. A token from the environment overrides the
saved host credential and is never written to disk. The run and direct-send
scopes authorize access to the endpoint; a matching unexpired agent policy is
an additional requirement for autonomous execution.
sendsets run campaign.yaml --json | jq -r '.state'
sendsets mailbox list > mailboxes.jsonAll retryable launches carry an Idempotency-Key. The server returns the
original run for an exact replay and rejects reuse with different input.