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:
- Polls your org's
webhook_deliveriestable. - For each new delivery, re-POSTs the payload to
--forward-to. - 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— success1— generic error2— bad input / flags3— 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.