SMS
Send transactional SMS, top up a prepaid wallet, and inspect delivery history. All endpoints are scoped to your API key's supplier.
Required scopes:
sms:read— list messages, view wallet balance + transactions, view pricingsms:write— send SMS, initiate Stripe top-ups
List SMS Messages
GET /api/v1/sms-messages
Returns paginated SMS history for your supplier, newest first.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: queued, sending, sent, delivered, failed, undelivered, insufficient_balance |
initiated_by | string | Filter by who triggered the send: user, ai, system |
booking_id | int | Filter to one booking |
from | ISO 8601 date | Start of date range |
to | ISO 8601 date | End of date range |
per_page | int | Default 25 |
Response
{
"data": [
{
"id": 142,
"supplier_id": 1,
"customer_id": 56,
"booking_id": 789,
"notification_name": "upcoming_booking",
"to_phone": "+1•••••2671",
"destination_country": "US",
"body_preview": "Reminder: your Coastal Adventures booking…",
"status": "delivered",
"segments": 1,
"twilio_sid": "SM…",
"charged_cents": 3,
"twilio_cost_cents": 2,
"initiated_by": "system",
"sent_at": "2026-05-05T14:00:00+00:00",
"delivered_at": "2026-05-05T14:00:03+00:00"
}
],
"meta": { "request_id": "...", "current_page": 1, "total": 142 }
}
Phone numbers are masked by default — only the country code and last 4 digits are exposed.
Send Ad-Hoc SMS
POST /api/v1/sms-messages
Sends a custom SMS to a customer. Wallet is debited immediately on success.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
customer_id | int | yes | Customer in your supplier scope |
body | string | yes | Message body (max ~10 segments after auto-trim per sms.max_segments_per_message) |
notification_name | string | no | Audit-log tag, default api_adhoc |
booking_id | int | no | Link to a booking |
Response
201 Created with the resulting SMS message resource.
Errors
402 Payment Required— wallet has insufficient balance422 Unprocessable Entity— customer opted out, phone unparseable, or validation failed
Wallet Balance
GET /api/v1/sms-wallet/balance
{
"data": {
"balance_cents": 2340,
"low_balance": false,
"critically_low": false,
"low_balance_threshold_cents": 500
}
}
Initiate Wallet Top-Up
POST /api/v1/sms-wallet/topups
Creates a Stripe Checkout session for an arbitrary top-up amount. Returns the URL to redirect the supplier to. The wallet is credited via webhook only after the supplier completes payment — the top-up is recorded as pending until then.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
amount_cents | int | yes | $5 minimum (500), $500 maximum (50000) per top-up |
Response
{
"data": { "checkout_url": "https://checkout.stripe.com/c/pay/..." },
"meta": { ... }
}
Status code is 202 Accepted — the wallet hasn't actually been credited yet, the supplier still needs to complete checkout.
Wallet Transaction Ledger
GET /api/v1/sms-wallet/transactions
Append-only ledger of every wallet movement. Use this for accounting reconciliation.
{
"data": [
{
"id": 99,
"type": "debit",
"amount_cents": -3,
"balance_after_cents": 2337,
"reason": "sms_send",
"sms_message_id": 142,
"sms_topup_id": null,
"created_at": "2026-05-05T14:00:00+00:00"
}
],
"meta": { ... }
}
amount_cents is signed — negative for debits, positive for credits. The denormalised suppliers.sms_wallet_balance_cents always equals the running sum of this ledger.
Per-Country Pricing
GET /api/v1/sms-pricing
Returns the per-country rate table EquipDash uses to calculate wallet debits. Rates include the platform margin (50%) baked in and rounded up to whole cents.
{
"data": {
"margin_multiplier": 1.5,
"rates": [
{
"country": "AU",
"twilio_cost_cents": 5,
"our_cost_cents": 8,
"currency": "USD"
},
{
"country": "US",
"twilio_cost_cents": 2,
"our_cost_cents": 3,
"currency": "USD"
}
]
}
}
Rates are refreshed daily from Twilio's Pricing API. Use this endpoint to display per-country costs in your own admin UI.