CLI

sendoka — command-line companion for the API. Great for one-off sends, local webhook dev, tailing activity, firing synthetic events.

Install

npm install -g sendoka

Requires Node 20+. Repo source: packages/cli/.

Configure

export SENDOKA_API_KEY=sok_test_...
export SENDOKA_BASE_URL=https://api.sendoka.com  # default

For internal commands (logs tail, listen, events trigger) that hit /api/internal/*, you also need your session cookie from the dashboard:

# In a browser logged into the dashboard, open devtools → Application → Cookies.
# Copy the value of `next-auth.session-token`.
export SENDOKA_SESSION_COOKIE=...

Alternatively, run sendoka login — opens a browser, does the OAuth handshake, writes the token to ~/.sendoka/credentials. Subsequent commands read from there automatically.

Commands

sendoka send email

Send a one-off email.

sendoka send email \
  --from hello@yourdomain.com \
  --to user@example.com \
  --subject "Hello" \
  --html "<p>From the CLI</p>"

Flags:

Flag Required Description
--from <email> yes Verified sender address
--to <email> yes Recipient. Repeat for multiple
--subject <s> yes (unless --template) Subject line
--html <html> one of HTML body
--text <text> one of Plain text body
--template <slug> one of Template slug
--var KEY=VALUE Template variable. Repeat
--tag <t> Tag. Repeat
--attach <path> Local file, base64-encoded. Repeat
--scheduled-at <iso> ISO UTC future time
--idempotency-key <uuid> Idempotency key
--json Emit JSON response instead of pretty output

sendoka send sms

sendoka send sms --from +15551234567 --to +15559876543 --body "Test"

Flags: --from | --from-pool, --to, --body | --template --var, --scheduled-at, --json.

sendoka logs tail

Poll /api/v1/activity every 5s and print new rows.

sendoka logs tail                    # last 1 day
sendoka logs tail --days 3           # last 3 days
sendoka logs tail --channel email    # filter
sendoka logs tail --status bounced   # filter

Output:

2026-04-22T10:14:22.113Z  msg_01HN...  sent       email  from=you@… to=user@…
2026-04-22T10:14:24.501Z  msg_01HN...  delivered  email

sendoka listen

Forward webhook deliveries to a local URL — skip ngrok for webhook dev.

sendoka listen --forward-to http://localhost:3001/hooks

What it does:

  1. Polls your org's webhook_deliveries table.
  2. For each new delivery, re-POSTs the payload to --forward-to.
  3. Prints status + response.

The forwarded request is not signed with the original secret — your local handler should skip signature verification in dev, or use events trigger below for a signed payload.

sendoka events trigger

Fire a synthetic signed event at one of your webhook endpoints. Great for integration-testing your webhook handler end-to-end.

sendoka events trigger \
  --event message.bounced \
  --endpoint whk_01HN...

The payload includes data.test: true so your handler can distinguish a synthetic event from a real one if needed.

Rate limit: 20/min per endpoint (TEST_FIRE_RATE_LIMITED).

sendoka keys list

sendoka keys list
key_01HN...  sok_live_•••abc1  live  scopes=emails:send,sms:send
key_01HP...  sok_test_•••xyz9  test  scopes=*

sendoka domains verify <domain>

Kicks off DNS verification and polls until status flips.

sendoka domains verify yourdomain.com

Prints the DKIM/SPF/DMARC records you need to publish, then polls every 10s.

sendoka keys rotate <key-id>

Generates a new secret for the key, prints it once, invalidates the old secret after a 24h grace window.

sendoka keys rotate key_01HN...

Exit codes

  • 0 — success
  • 1 — generic error
  • 2 — bad input / flags
  • 3 — auth (401/403)
  • 4 — validation (422)
  • 5 — rate limit (429)
  • 6 — server error (5xx)

Useful for scripts:

sendoka send email --from ... --to ... --subject hi --html '<p>hi</p>' || {
  case $? in
    3) echo "auth broke, rotate key"; exit 1 ;;
    5) sleep 60; exec "$0" ;;  # retry after rate limit
    *) echo "unknown failure"; exit 1 ;;
  esac
}

Common recipes

Send a test email from CI

sendoka send email --from "ci@yourdomain.com" --to "qa@yourdomain.com" \
  --subject "Build $GITHUB_SHA passed" \
  --html "<p><a href='$BUILD_URL'>$BUILD_URL</a></p>"

Tail-and-grep during an incident

sendoka logs tail --json | jq -r 'select(.status == "failed") | .id'

Local webhook dev loop

# Terminal 1 — your handler
node my-webhook-handler.js  # listens on :3001

# Terminal 2 — forwarder
sendoka listen --forward-to http://localhost:3001/hooks

# Terminal 3 — trigger a real send
sendoka send email --from ... --to ... --subject test --html '<p>hi</p>'

Delivery rows flow: real send → webhook endpoint → CLI poll → localhost POST. You see the event in your handler within ~5 seconds.

See also