SendSetsDocs

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 --json

Commands

CommandPurpose
sendsets login, logout, whoamiSign in, sign out, and see the workspace, credential, scopes and agent policy
sendsets auth status|token|switch|refreshManage the signed-in hosts
sendsets doctorCheck auth, API, app connections, product events, mailboxes and the campaign runtime; a fix per gap; exit 1 when not ready
sendsets statusWhat is happening in the workspace right now
sendsets connection list|create|get|update|delete|rotate-secret|testThe app your campaigns call (app connections)
sendsets events send <name>|list|get|tailProduct events, and the live event stream
sendsets mailbox add|get|update|delete|enable|disable|testConnect, inspect and control mailboxes
sendsets mailbox warmup enable|disable|pause|resume|statusThe 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|identityDomain authentication, sending hold, sync state, send-as
sendsets campaign create|edit|status|steps|add-step|edit-step|delete-stepBuild a campaign, including app_action and wait_for_event steps
sendsets campaign senders|set-senders|validate|preflight|test-email|start|stop|logsCheck, test and run it
sendsets campaign import campaign.yaml and sendsets runA run file as a durable run
sendsets inbox list|read|draft|replyUnified inbox operations
sendsets statsWorkspace sending summary
sendsets policy showInspect the calling credential's effective policy and budget
sendsets api <path>Any endpoint the table does not name
sendsets alias, config, upgrade, browseThe 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 gmail

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

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

The 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.json

All retryable launches carry an Idempotency-Key. The server returns the original run for an exact replay and rejects reuse with different input.

On this page