Reference
sendbolt-cli
sendbolt-cli is a single static Go binary (W225) that wraps the SendBolt REST API. Same operations you'd hit with curl, just typed flags + sensible defaults + table/JSON output. Stdlib-only — no runtime dependencies, no libc link.
It is meant for send-time scripting and one-off operator chores. For long-lived application code, prefer the typed TypeScript or Go SDK (W220).
Install
Pre-built binary (recommended)
Download the binary for your platform from the GitHub Releases page and drop it on your $PATH:
# Replace <version> with the release tag you want (e.g. v1.0.0).
VERSION=latest
OS=$(uname -s | tr '[:upper:]' '[:lower:]') # linux | darwin
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
curl -sSL \
"https://github.com/sendbolt/sendbolt/releases/${VERSION}/download/sendbolt-cli_${OS}_${ARCH}" \
-o /usr/local/bin/sendbolt-cli
chmod +x /usr/local/bin/sendbolt-cli
sendbolt-cli --versionFrom source with go install
# Requires Go 1.22+. Installs into $(go env GOBIN) or $(go env GOPATH)/bin.
go install github.com/sendbolt/sendbolt/cmd/sendbolt-cli@latest
sendbolt-cli --versionConfiguration
The CLI resolves its API key and base URL in flag → env → file → default order — each successive source overrides the previous one.
| Source | API key | Base URL |
|---|---|---|
| 1. Flag | --api-key=<key> | --base-url=<url> |
| 2. Environment variable | SENDBOLT_API_KEY | SENDBOLT_BASE_URL |
| 3. Config file | ~/.sendbolt/config.yml → api_key: | ~/.sendbolt/config.yml → base_url: |
| 4. Default | — | https://api.sendbolt.io |
# Two scalar keys; comments allowed. Quotes optional.
api_key: sb_live_a1b2c3d4e5...
base_url: https://api.sendbolt.ioFor CI & integration tests, use a sb_test_ sandbox key (W213) — every send is accepted, queued, then dropped before it leaves the MTA. No reputation or quota impact.
Global flags
Accepted by every subcommand:
--api-key=<key>— overrides env/file--base-url=<url>— overrides env/file--output=table|json— output format (defaulttable)--help/-h— print subcommand help
Top-level: sendbolt-cli --version / sendbolt-cli version prints the binary version and exits.
Exit codes: 0 success; 1 any error (validation, network, server 4xx/5xx); 2 usage error (unknown subcommand, missing required flag). Stable across releases — scripts can branch on them.
Subcommands
send — transactional email
Wraps POST /api/v1/transactional/send. Required scope: transactional:send. Gated by the W174 verified-domain check until at least one domain is green.
sendbolt-cli send \
--to=alice@example.com \
--from=no-reply@acme.com \
--from-name="Acme" \
--reply-to=support@acme.com \
--subject="Confirm your Acme signup" \
--text="Hi Alice, confirm at https://acme.com/verify?t=abc123" \
--html="<p>Hi Alice,</p><p><a href=\"https://acme.com/verify?t=abc123\">Confirm</a></p>"list — mailing lists
sendbolt-cli list ls
sendbolt-cli list ls --output=json | jq '.[] | .name'contact — contacts
Three nouns: add, show, import.
sendbolt-cli contact add \
--email=bob@example.com \
--first-name=Bob \
--last-name=Smith \
--list-id=ls_123domain — sending domains
Two nouns: check (read-only — print expected DNS records) and verify (trigger backend re-verify; writes the green/red transition into sending_domains).
# Domain must already exist on the tenant (POST /domains adds it).
sendbolt-cli domain check --domain=acme.comtemplate — render
# Returns rendered subject + body_text + body_html.
sendbolt-cli template render \
--template-id=tpl_42 \
--json-context='{"first_name":"Alice"}'CI integration
GitHub Action (W226)
sendbolt/sendbolt ships a composite GitHub Action that wraps the CLI. Drop it in a workflow to send a deploy notification, release email, or post-job report without writing any curl glue.
name: Notify on release
on:
release:
types: [published]
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Email the release notes
uses: sendbolt/sendbolt/.github/actions/sendbolt-send@main
with:
api-key: ${{ secrets.SENDBOLT_API_KEY }}
to: announce@acme.com
from: releases@acme.com
subject: "Release ${{ github.event.release.tag_name }}"
text: ${{ github.event.release.body }}The action is a composite (not Docker) — cold start is hundreds-of-ms, no image pull, runs on Linux + macOS + Windows runners. Full input reference + outputs (message-id, status) is in .github/actions/sendbolt-send/action.yml.
Generic CI (any runner)
Two-line install + invoke pattern works on GitLab CI, CircleCI, Jenkins, Buildkite, anywhere bash runs:
#!/usr/bin/env bash
set -euo pipefail
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
curl -sSL \
"https://github.com/sendbolt/sendbolt/releases/latest/download/sendbolt-cli_${OS}_${ARCH}" \
-o /usr/local/bin/sendbolt-cli
chmod +x /usr/local/bin/sendbolt-cli
export SENDBOLT_API_KEY="$SENDBOLT_API_KEY" # injected by CI secrets
sendbolt-cli send \
--to=devops@acme.com \
--from=ci@acme.com \
--subject="Build #${CI_BUILD_NUMBER} succeeded" \
--text="Build passed on ${CI_COMMIT_SHA}."Common workflows
Bulk one-off send from a CSV
For small lists (under a few thousand recipients) where you don't want to wire up the full Campaigns UI:
#!/usr/bin/env bash
# Reads emails.csv (one email per line), sends a template to each.
while IFS= read -r email; do
[[ -z "$email" || "$email" == "email" ]] && continue # skip header / blanks
sendbolt-cli send \
--to="$email" \
--from=hello@acme.com \
--template-id=tpl_newsletter_q4 \
--json-context="{\"email\":\"$email\"}"
done < emails.csvFor anything past a few thousand recipients, use Campaigns instead — it adds per-recipient suppression checks, RFC 8058 List-Unsubscribe headers (W198), and per-domain rate-limit smoothing the CLI loop doesn't.
Daily contact import
# Run nightly: pull yesterday's new signups from your CRM, import to SendBolt.
0 2 * * * /usr/local/bin/sync-signups.shDaily domain-health sync
#!/usr/bin/env bash
# Re-verify every sending domain once a day so the dashboard's
# green/red status reflects yesterday's DNS edits.
sendbolt-cli list ls --output=json | jq -r '.[].id' | while read -r ls_id; do
: # placeholder — wire to your own audit
done
# Iterate domains:
for id in $(sendbolt-cli domain check --domain=acme.com --output=json | jq -r '.id // empty'); do
sendbolt-cli domain verify --id="$id" --output=json
doneScripting tip: JSON output
Pass --output=json to any subcommand to get the full server response (vs. the curated table view). jq + JSON-mode is the supported way to script the CLI — the table layout is for human eyes and may change between releases.
MSG_ID=$(sendbolt-cli send \
--to=a@b.com --from=x@y.com --subject=hi --text=hi \
--output=json | jq -r '.message_id')
echo "queued as $MSG_ID"