@sendly/cli
v3.37.0
Published
Sendly CLI - Send SMS from your terminal
Downloads
384
Maintainers
Readme
@sendly/cli
Official command-line interface for the Sendly SMS API.
Installation
# npm
npm install -g @sendly/cli
# or Homebrew (macOS / Linux)
brew install SendlyHQ/tap/sendlyStaying up to date
The CLI checks npm once a day and prints a one-line banner after your command if a newer version is out. Run sendly upgrade to update — it auto-detects your install path (Homebrew vs npm) and runs the right command. Use sendly upgrade --check to see what it would do without executing.
Banner is silent in CI (CI=true) and offline-safe.
Quick Start
# Login to your Sendly account
sendly login
# Send an SMS
sendly sms send --to "+15551234567" --text "Hello from Sendly CLI!"
# Check your credit balance
sendly credits balanceAuthentication
The CLI supports two authentication methods:
Browser Login (Recommended)
sendly loginThis opens your browser to authenticate via Sendly's secure login flow. After authorization, your credentials are stored locally.
API Key Login
sendly login --api-key sk_test_v1_your_keyOr interactively:
sendly login -iCheck Authentication Status
sendly whoamiLogout
sendly logoutCommands
SMS Commands
Send a Message
sendly sms send --to "+15551234567" --text "Hello!"
# Send from a number you own (E.164) — see Numbers Commands below
sendly sms send --to "+15551234567" --text "Hello!" --from "+447111111111"
# Or an alphanumeric sender ID (international)
sendly sms send --to "+447700900000" --text "Hello!" --from "MyBrand"List Messages
sendly sms list
# Filter by status
sendly sms list --status delivered
# Limit results
sendly sms list --limit 10Get Message Details
sendly sms get msg_abc123Send Batch Messages
# From a JSON file
sendly sms batch --file messages.json
# From a CSV file (phone-only with shared text)
sendly sms batch --file phones.csv --text "Your order is ready!"
# Multiple recipients inline
sendly sms batch --to "+15551234567,+15559876543" --text "Hello everyone!"
# Preview before sending (dry run) - validates without sending
sendly sms batch --file messages.json --dry-run
# Dry run output includes:
# - Per-country breakdown with credit costs
# - Blocked messages and reasons
# - Your messaging access (domestic/international)
# - Credit balance checkSend a Group MMS
Send to 2-8 recipients (US & Canada only) in a single group thread — everyone
sees the group and replies fan out to all participants. Group messaging is an
A2P 10DLC capability, so the sending number must be an MMS-enabled,
10DLC-registered number you own (omit --from to use your default sender).
sendly sms group --to "+14155551234,+14155555678" --text "Team sync at noon?"
# Attach media
sendly sms group --to "+14155551234,+14155555678" --media-url https://example.com/flyer.jpg
# Marketing (applies quiet-hours rules; group MMS defaults to transactional)
sendly sms group --to "+14155551234,+14155555678" --text "Sale!" --type marketingSchedule a Message
sendly sms schedule --to "+15551234567" --text "Reminder!" --at "2025-12-25T10:00:00Z"List Scheduled Messages
sendly sms scheduledCancel a Scheduled Message
sendly sms cancel sched_abc123Numbers Commands
Buy international phone numbers and send from them. (US & Canada use toll-free verification instead — they can't be bought.)
Search Available Numbers
sendly numbers search --country GB --type mobileBuy a Number
sendly numbers buy --country GB --type mobileThe buy is asynchronous: the number starts as provisioning and becomes
active once the carrier confirms it. Some countries need documents or
business details first — you'll get a hosted link to complete them. See
How to buy a number.
List Your Numbers
sendly numbers listPass the phoneNumber of any active number as --from on a send to send
from it (see Send from a number you own):
sendly sms send --to "+15551234567" --text "Hi!" --from "+447111111111"Show a Number
sendly numbers get num_abc123Includes whether it's your workspace default sender and any scheduled release.
Update a Number
Make a number your default sender, or cancel a scheduled release. At least one
of --default / --keep is required:
# Make this number the workspace default sender (must be active)
sendly numbers update num_abc123 --default
# Cancel a scheduled release and keep the number
sendly numbers update num_abc123 --keepRelease a Number
sendly numbers release num_abc123Paid purchases are scheduled to release at the end of the billing period (undo
with sendly numbers update <id> --keep); everything else releases immediately.
Add --yes to skip the confirmation prompt.
10DLC Commands
Register your brand and messaging campaigns for carrier review so you can send from US local (10-digit) numbers. The flow is brand → qualify → campaign → assign number. Requires a live API key.
Register a Brand
sendly 10dlc brands create --legal-name "Acme Inc" --ein "12-3456789" --website https://acme.comCheck Brand Status
Carrier review starts as pending and becomes verified (or failed).
Running get refreshes the status:
sendly 10dlc brands list
sendly 10dlc brands get <brandId>Qualify a Use Case
Pre-check that a use case is accepted for your brand before creating a campaign:
sendly 10dlc qualify <brandId> MIXEDCreate a Campaign
Once the brand is verified:
sendly 10dlc campaigns create \
--brand <brandId> \
--use-case MIXED \
--description "Order updates and promotions" \
--message-flow "Customers opt in at checkout" \
--sample "Your order has shipped!" \
--sample "20% off this weekend"Poll until the campaign is active:
sendly 10dlc campaigns get <campaignId>Assign a Number
Attach a US local number you own to the active campaign to make it sendable:
sendly 10dlc campaigns assign <campaignId> --number "+15551234567"
sendly 10dlc assignments listThen send from it:
sendly sms send --to "+15559876543" --text "Hi!" --from "+15551234567"API Key Commands
List API Keys
sendly keys listCreate a New Key
sendly keys create --name "Production Key" --type liveRevoke a Key
sendly keys revoke key_abc123Rotate a Key
Generate a replacement key while keeping the old one valid for a grace period (24-168 hours, default 24), so you can roll deployments over before the old key expires:
sendly keys rotate key_abc123
# Keep the old key alive for 48 hours
sendly keys rotate key_abc123 --grace-period 48 --yesThe new sk_… secret is shown once — store it immediately.
Credit Commands
Check Balance
sendly credits balanceOutput includes:
- Current balance
- Reserved credits
- Estimated messages remaining
View Transaction History
sendly credits history
# Limit results
sendly credits history --limit 20Webhook Commands
List Webhooks
sendly webhooks listListen for Webhooks Locally
Start a local tunnel to receive webhook events during development (similar to Stripe CLI):
sendly webhooks listen
# Forward to a specific URL
sendly webhooks listen --forward http://localhost:3000/webhook
# Listen for specific events
sendly webhooks listen --events message.delivered,message.failedThis creates a secure tunnel and displays:
- Tunnel URL
- Webhook secret for signature verification
- Real-time event stream
Create Webhook
sendly webhooks create --url https://myapp.com/webhook --events message.delivered,message.failed
# With description and mode
sendly webhooks create \
--url https://myapp.com/webhook \
--events message.delivered,message.failed,message.bounced \
--description "Production webhook" \
--mode liveGet Webhook Details
sendly webhooks get whk_abc123Update Webhook
sendly webhooks update whk_abc123 --url https://newdomain.com/webhook
# Update events
sendly webhooks update whk_abc123 --events message.delivered,message.bounced
# Disable webhook
sendly webhooks update whk_abc123 --active falseDelete Webhook
sendly webhooks delete whk_abc123
# Skip confirmation
sendly webhooks delete whk_abc123 --yesTest Webhook
sendly webhooks test whk_abc123View Delivery History
sendly webhooks deliveries whk_abc123
# Show only failed deliveries
sendly webhooks deliveries whk_abc123 --failed-only --limit 20Rotate Webhook Secret
sendly webhooks rotate-secret whk_abc123Note: Old secret remains valid for 24 hours during migration.
Verification (OTP) Commands
Send OTP
sendly verify send --to "+15551234567"
# With custom app name
sendly verify send --to "+15551234567" --app-name "MyApp"
# With template
sendly verify send --to "+15551234567" --template tpl_preset_2fa
# Custom code length and timeout
sendly verify send --to "+15551234567" --code-length 6 --timeout 300Check OTP Code
sendly verify check ver_abc123 --code 123456Get Verification Status
sendly verify status ver_abc123List Recent Verifications
sendly verify list
# Limit results
sendly verify list --limit 10Resend OTP
sendly verify resend ver_abc123Template Commands
List Templates
sendly templates listGet Template Details
sendly templates get tpl_abc123
# Get a preset template
sendly templates get tpl_preset_2faCreate Template
sendly templates create --name "My OTP" --text "Your code is {{code}}"Supported variables: {{code}}, {{app_name}}
Publish Template
sendly templates publish tpl_abc123Delete Template
sendly templates delete tpl_abc123
# Skip confirmation
sendly templates delete tpl_abc123 --forceList Preset Templates
sendly templates presetsLink Commands (URL Shortening)
Mint branded, owned-domain short links for your destination URLs. Branded short
links improve deliverability (carriers filter public shorteners) and give you
per-link click analytics. URL shortening is gated behind the url_shortener
rollout flag — until it's enabled for your account these commands return
not_enabled.
Create a Short Link
sendly links create https://example.com/spring-saleList Your Short Links
Newest first, with click counts:
sendly links list
# Paginate
sendly links list --limit 20 --offset 20Disable / Re-enable a Short Link
The per-link kill switch — a disabled link's redirect returns 404:
sendly links disable Ab3xY7
# Re-enable
sendly links disable Ab3xY7 --enableLogs Commands
Tail Logs
Stream real-time API activity:
sendly logs tail
# Filter by status
sendly logs tail --status errorConfiguration Commands
Get Configuration Value
sendly config get baseUrlSet Configuration Value
sendly config set baseUrl https://sendly.liveList All Configuration
sendly config listDiagnostics
Run diagnostics to check your setup:
sendly doctorThis checks:
- Authentication status
- API connectivity
- Configuration validity
- Network issues
Utility Commands
Account Status Dashboard
sendly statusShows account overview including:
- Verification status and tier
- Credit balance
- Active API keys and webhooks
- Recent messages
Trigger Test Event
For testing with webhooks listen:
sendly trigger message.delivered
sendly trigger message.bouncedEnvironment Variables
Override CLI configuration with environment variables:
| Variable | Description |
|----------|-------------|
| SENDLY_API_KEY | API key for authentication |
| SENDLY_BASE_URL | API base URL (default: https://sendly.live) |
| SENDLY_OUTPUT_FORMAT | Output format: text or json |
| SENDLY_NO_COLOR | Disable colored output |
| SENDLY_TIMEOUT | Request timeout in milliseconds |
| SENDLY_MAX_RETRIES | Maximum retry attempts |
Output Formats
Text (Default)
Human-readable formatted output with colors.
JSON
Machine-readable JSON output for scripting:
sendly sms list --json
sendly credits balance --jsonCI/CD Usage
For non-interactive environments:
# Set API key via environment variable
export SENDLY_API_KEY=sk_live_v1_your_key
# Or pass directly
sendly sms send --api-key sk_live_v1_your_key --to "+15551234567" --text "Hello!"
# Use JSON output for parsing
sendly credits balance --json | jq '.balance'Configuration Storage
Configuration is stored in:
- macOS/Linux:
~/.sendly/config.json - Windows:
%USERPROFILE%\.sendly\config.json
Webhook Signature Verification
When using sendly webhooks listen, verify signatures in your app:
import crypto from 'crypto';
function verifyWebhook(payload, signature, secret) {
const expectedSig = 'v1=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSig)
);
}Requirements
- Node.js 18.0.0 or higher
- A Sendly account (sign up free)
Documentation
Support
License
MIT
