Web Rabbit MediaDocs
Messaging

Send SMS

Create a bulk SMS campaign and pull per-recipient delivery reports.

Send a bulk SMS campaign. One call creates the campaign, expands your recipient list, debits the wallet and hands the batch to the network.

Endpoint

POST/v1/messaging/sms
iNo sender ID yet? Use WRB
sender is 3–11 letters or digits. If you have not registered your own sender ID, send with WRB — the shared Web Rabbit sender name, available to every workspace from day one. For branded messages register your own under Messaging → Bulk SMS → Sender IDs; once it is approved it becomes your default. Unregistered names are rejected by the networks with “Sender Id Not Allowed”.

Request

FieldTypeDescription
business_idstringOptional on API-key calls — taken from the key. Required for dashboard sessions. A value that disagrees with the key is rejected.
modeenumtest or live. Optional on API-key calls — taken from the key.
senderrequiredstringApproved sender ID, 3–11 alphanumeric characters. Defaults to the shared WRB name.
messagerequiredstringMessage body. Max 1600 characters.
recipientsstring[]Numbers in local (0240000000) or international (233240000000) format.
group_idsstring[]Contact group ids. Merged with recipients; opted-out contacts are skipped.
namestringCampaign name shown in the dashboard.
schedule_atstringISO timestamp. Omit to send immediately. The campaign is handed to the network scheduler when it accepts it, otherwise Web Rabbit holds it and releases it at the requested time. The response says which, in scheduled_by.
timezone_idstringNetwork time-zone id used with schedule_at. Ids come from GET /v1/messaging/reference?kind=timezone.
validity_secondsintegerHow long the network keeps retrying an unreachable handset before giving up. 60 to 259200 seconds.
dlt_entity_idstringRegulator entity id, for markets that require registration (for example India DLT).
dlt_template_idstringRegulator template id.
tlvobject[]Regulator TLV tags, each { tag, tagValue }, passed straight through to the network.

Recipients are de-duplicated and validated. A campaign accepts at most 10,000 numbers.

iSafe retries
Send an Idempotency-Key header and a retry of the same body returns the original campaign instead of sending twice. Reusing a key with a different body returns 409 idempotency_conflict. Same rules as Payments.
shell
curl -X POST https://api.webrabbitmedia.com/v1/messaging/sms \
  -H "Authorization: Bearer wr_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "business_id": "b0a1…",
    "mode": "live",
    "name": "October promo",
    "sender": "WRB",
    "message": "Our October sale is live — 20% off everything.",
    "recipients": ["0240000000", "233201112233"]
  }'

Response

Response · 200
{
  "ok": true,
  "campaign_id": "3f0c…",
  "provider_campaign_id": "664a…",
  "cost": 4.40,
  "recipients": 12,
  "breakdown": [
    { "country": "Ghana", "iso2": "GH", "recipients": 10, "rate_per_segment": 0.20, "cost": 2.00 },
    { "country": "Nigeria", "iso2": "NG", "recipients": 2, "rate_per_segment": 0.20, "cost": 0.40 }
  ],
  "credit_left": 4180
}
FieldTypeDescription
campaign_idstringWeb Rabbit campaign id. Use it to pull delivery reports.
provider_campaign_idstringUpstream batch id, null while queued.
costnumberCredits debited: the sum of segments × rate for each recipient, priced by destination country.
breakdownarrayCost grouped by destination country: recipients, rate per segment and subtotal.
recipientsnumberValid, de-duplicated numbers accepted.
rejectednumberRecipients the network refused. Their credits are refunded at exactly what they cost.
simulatedbooleanPresent and true for test-mode sends.

What a message costs

Messages are paid from your messaging wallet. SMS from a bundle package is used before wallet balance, and Web Rabbit can also add credit to your wallet directly.

Price is per segment and depends on the country the recipient's number belongs to, because that is how the networks charge us. Every destination is priced on that country's local network rate: we take the carrier price in euro, convert it to cedis and apply our margin:

Formula
price per segment = ceil(carrier EUR rate × EUR→GHS × 3) , minimum GHS 0.20
campaign cost     = Σ  segments × price per segment  (per recipient)
FieldTypeDescription
Ghana (+233)GHS 0.20Per 160-character segment. The floor price.
Nigeria (+234)GHS 0.20Local network rate.
Kenya (+254)GHS 1.35Local network rate.
South Africa (+27)GHS 0.36Local network rate.
Senegal (+221)GHS 0.76Local network rate.
Anywhere elseGHS 3.96Fallback for destinations outside the published list.

The full list of 52 African destinations, and the exact price we will charge you, is shown live under Messaging → Wallet → Price per country. Prices move with the carrier sheet and the exchange rate; the breakdown in every send response tells you precisely what you were charged and why. Wallets stay in GHS whatever the destination.

Numbers may be written as Ghana local (0240000000) or in full international form (233240000000, 2348030000000). A local number is always read as Ghana.

Multiple sender IDs

A workspace can hold as many approved sender IDs as it needs — one per product line, for example RIDES, DELIVERY and SAFETY. There is no limit and no extra cost; each name is registered and approved separately with the networks, which typically takes 1–3 business days. Register them under Messaging → Sender IDs.

Sender selection is per request: set sender to any approved name on your workspace for that mode. Sending an unapproved name is rejected with HTTP 400 before the message is charged, and WRB is always available as a fallback while a new name is in review.

When you request a name we ask for three things: the message type — transactional, promotional, or both — the country the name is registered for (Ghana by default), and a short use case with a sample message. The networks only accept one of those three message types, so it is a choice rather than free text; your use case and sample travel with the request as the supporting detail. If a request is saved but the network has not picked it up yet, use Resend to network on the row instead of creating the name again.

Delivery reports

iUse webhooks, not polling
Subscribe to message.delivered, message.failed and message.rejected and we push one signed receipt per recipient, with the same HMAC-SHA256 scheme as payment events. Every submitted message gets exactly one terminal event; if the network never reports, we close it after 24 hours as undelivered. The endpoint below is a fallback for forcing a refresh.
POST/messaging-status

Poll for per-recipient outcomes. Each call reconciles the campaign’s messages with the network’s report and returns how many rows changed.

shell
curl -X POST https://api.webrabbitmedia.com/v1/messaging/messaging-status \
  -H "Authorization: Bearer wr_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "campaign_id": "3f0c…" }'
Response · 200
{ "ok": true, "updated": 12 }

Message statuses move queued → submitted → delivered, or to undelivered, failed or rejected. Unknown network codes stay submitted until the next report, and at most 24 hours — after that we settle the message as undelivered with provider_status: "NO_DLR" and emit message.failed.