CloudWave Bulk SMS — Docs

REST API · v1

Bulk SMS REST API

Send SMS from your own software using the credits in your CloudWave Bulk SMS wallet. Every request is JSON, authenticated with your API key and secret, and billed per SMS part — exactly the same rules and prices as the portal.

Base URL
https://sms.cloudwavebd.com
Format
JSON (UTF-8)
Auth
API key + secret
Currency
BDT
Max recipients / request
500
Max message length
4000 chars

Authentication

Every request must carry your API key and API secret. Create them in the portal under API & Plugins → Generate API credentials; you can view them again at any time with View Key & Secret.

Option A — request headers (recommended)

X-Api-Key:    cw_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-Api-Secret: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

Option B — HTTP Basic auth

Authorization: Basic base64(API_KEY:API_SECRET)

Option C — query / form fields

For quick tests you may pass api_key and api_secret as query-string or form parameters. Prefer headers in production so the secret never appears in URLs or logs.

Keep the secret server-side. Never embed it in client-side JavaScript, a mobile app, or a public repository. If a key leaks, use Regenerate secret (rolls the secret, keeps the key) or Revoke API access in the portal.
Create a separate key per integration (one for WHMCS, one for your website, one for a script). Then you can revoke one without disturbing the others, and the portal's request log stays attributable.

Quickstart

  1. Generate credentialsOpen API & Plugins in the portal and click Generate API credentials. Copy the key and the secret.
  2. Check your balanceCall GET /api/balance to confirm the credentials work and you have funds.
  3. Send a testPOST to /api/send with a single number you control.
  4. Check deliveryCall GET /api/history?campaign_id=… using the campaign_id returned by the send.
curl -X POST 'https://sms.cloudwavebd.com/api/send' \
  -H 'Content-Type: application/json' \
  -H 'X-Api-Key: cw_YOUR_API_KEY' \
  -H 'X-Api-Secret: YOUR_API_SECRET' \
  -d '{ "message": "Hello from the API!", "to": "01712345678" }'

Endpoints

MethodPathPurpose
GET /api/balance Current balance and currency.
GET /api/senders List the Sender IDs you may send from, with per-part rates.
POST /api/send Send one message to 1–500 Bangladeshi numbers.
GET /api/history Your campaigns and per-message delivery status.

All paths are relative to the base URL https://sms.cloudwavebd.com. Every response includes "ok": true|false.

GET /api/balance

GET/api/balance

Returns your current wallet balance. Use it to show credit in your own dashboard or to pre-check before a large send.

Response

{
  "ok": true,
  "balance": 123.45,
  "currency": "BDT",
  "client": { "id": 3, "email": "client@example.com" }
}
FieldTypeDescription
balancenumberWallet balance in BDT (4-decimal precision).
currencystringAlways BDT.
client.idintegerYour SMS-portal client id.
client.emailstringThe account email.

GET /api/senders

GET/api/senders

Lists every Sender ID the account may send from, with its provider and per-part price. Use these sender_id values when sending, or omit it to use the default.

Response

{
  "ok": true,
  "default_sender": "8809617611036",
  "senders": [
    { "sender_id": "8809617611036", "provider": "bulksmsbd", "rate_bdt": 0.35, "unicode": true }
  ]
}
FieldTypeDescription
senders[].sender_idstringThe Sender ID (mask) the message appears from.
senders[].providerstringUpstream gateway key from config/providers.php.
senders[].rate_bdtnumberPrice per SMS part in BDT, resolved for your account.
senders[].unicodebooleantrue when Bangla (Unicode) messages are accepted on this Sender ID.

POST /api/send

POST/api/send

Sends one message to one or many Bangladeshi numbers. The wallet is debited per SMS part before the message is handed to the gateway.

Request body

FieldTypeRequiredDescription
messagestringyesThe SMS text (UTF-8; Bangla supported). Max 4000 characters.
tostring | string[]yesA single number or an array. Accepted forms: 01712345678, 8801712345678, +8801712345678. Invalid numbers are skipped.
sender_idstringnoAny Sender ID from GET /api/senders. Omit to use the account default.
campaignstringnoLabel shown in History and in the portal.
allow_duplicatesbooleannoDefault false. When false a repeated number is sent once; when true every entry is sent and billed.

Example request

{
  "message": "Your SMS text — Bangla is supported",
  "to": ["01712345678", "8801812345678"],
  "sender_id": "8809617611036",   // optional
  "campaign": "Campaign name",     // optional
  "allow_duplicates": false        // optional — allow the same number more than once
}

Send it

curl -X POST 'https://sms.cloudwavebd.com/api/send' \
  -H 'Content-Type: application/json' \
  -H 'X-Api-Key: cw_YOUR_API_KEY' \
  -H 'X-Api-Secret: YOUR_API_SECRET' \
  -d '{
    "message": "Hello from your API!",
    "to": ["01712345678"]
  }'

Response — 200 OK

{
  "ok": true,
  "campaign_id": 42,
  "provider": "bulksmsbd",
  "sender_id": "8809617611036",
  "recipients": 2,
  "parts": 1,
  "encoding": "text",
  "total_cost": 0.7,
  "balance_after": 122.75,
  "sent": 2,
  "failed": 0,
  "skipped_invalid": []
}
FieldTypeDescription
campaign_idintegerUse with GET /api/history?campaign_id=….
providerstringGateway that carried the message.
sender_idstringSender ID actually used.
recipientsintegerRecipients accepted after de-duping / validation.
partsintegerSMS parts per recipient (billing unit).
encodingstringtext (GSM-7) or unicode (Bangla).
total_costnumberBDT debited (parts × rate × recipients).
balance_afternumberWallet balance after the debit.
sent / failedintegerDispatch outcome for this call; leftovers are retried by the worker.
skipped_invalidstring[]Numbers that were not valid BD mobiles (first 20).

Response — 202 Accepted (held for approval)

When a request targets more recipients than the account's approval threshold (default 100, set by the operator) it is held for admin approval. The cost is reserved from the wallet and nothing is sent until an operator approves it; a rejection refunds the reservation.

{
  "ok": true,
  "status": "pending_approval",
  "campaign_id": 43,
  "recipients": 250,
  "parts": 1,
  "total_cost": 87.5,
  "balance_after": 35.25,
  "message": "This campaign exceeds the approval threshold and is held for admin approval. …"
}

Errors

Errors use standard HTTP status codes and a consistent JSON shape:

{ "ok": false, "error": "unauthorized", "message": "Invalid API key or secret." }
HTTPerrorMeaningHow to fix
401unauthorizedMissing or invalid credentials.Check the key/secret — they are case-sensitive.
402send_failedInsufficient wallet balance.Top up in the portal (Add Funds).
403suspendedAPI account suspended.Contact the operator.
404not_foundCampaign not found on your account.Check campaign_id.
422invalid_messagemessage missing/empty.Send a non-empty message.
422invalid_recipientsNo valid BD numbers in to.Use 017… / 8801… / +8801….
422invalid_sender_idUnknown Sender ID.Use a value from GET /api/senders.
422too_many_recipientsOver the 500-recipient cap.Split into batches.
500server_errorServer-side problem.Retry; if it persists, contact support.
503no_senderNo Sender ID configured.Contact the operator.

GET /api/history

GET/api/history

Without parameters: your recent campaigns, newest first. With campaign_id: that campaign plus every message and its delivery status.

Query parameters

ParamTypeDescription
campaign_idintegerReturn one campaign with its message list (paginated).
statusstringFilter by status: queued, sending, sent, delivered, partial, failed.
pageintegerPage number, default 1.
per_pageintegerItems per page, default 25, max 100.

Response — campaign list

{
  "ok": true,
  "campaigns": [
    { "id": 42, "name": "Eid offer", "sender_id": "8809617611036", "provider": "bulksmsbd",
      "recipients": 2, "parts": 1, "total_cost": 0.7, "status": "sent",
      "created_at": "2026-09-28 12:30:11" }
  ],
  "page": 1, "per_page": 25, "total": 1
}

Response — single campaign

{
  "ok": true,
  "campaign": { "id": 42, "name": "Eid offer", "status": "sent", "total_cost": 0.7, "recipients": 2, "parts": 1 },
  "messages": [
    { "id": 501, "recipient": "8801712345678", "status": "sent", "attempts": 1,
      "error": null, "parts": 1, "amount": 0.35, "sent_at": "2026-09-28 12:30:12" }
  ],
  "page": 1, "per_page": 25, "total": 2
}

Billing & message parts

An SMS is billed per part. Long messages and Bangla (Unicode) messages split into more parts, so the cost is parts × rate_per_part × recipients. The rate per part comes from GET /api/senders (your account may have custom rates).

EncodingWhen it is used1 partEach extra part
GSM-7 (text)Message is pure GSM-7 (English/Latin)160 chars153 chars
Unicode (unicode)Message contains Bangla or any non-GSM-7 character70 chars67 chars
The wallet is checked and debited atomically: a send is only accepted if the balance covers the whole campaign. If the balance is short you receive 402 send_failed and nothing is sent.

Delivery statuses

StatusMeaning
queuedAccepted and waiting for dispatch (the worker retries these).
sendingCurrently being handed to the gateway.
sentAccepted by the gateway.
deliveredConfirmed delivered (when the provider's delivery report is wired).
failedRejected / not accepted. The error field explains why.
pending_approvalHeld for operator approval (large campaign).

Admin approval for large sends

Sends above the account's threshold (default 100 recipients) are queued as pending_approval. The full cost is reserved from the wallet immediately, but no message is transmitted until an operator approves it in the portal.

  • You receive 202 Accepted with status: "pending_approval" and a campaign_id.
  • Poll GET /api/history?campaign_id=… to watch it change to queued → sent (approved) or rejected.
  • Approve dispatches the campaign; Reject refunds the entire reserved cost to the wallet.
  • Split very large audiences into batches under the threshold if you need instant (non-approved) delivery.

Best practices

  • Store credentials in environment variables or a secrets store — never in code or version control.
  • Reuse one connection and set a sensible timeout (5–10s) on your HTTP client.
  • Batch recipients up to 500 per request instead of one request per number.
  • Handle 202 pending_approval as success-with-review, not as an error.
  • De-duplicate your audience before sending unless duplicates are intentional.
  • Poll history sparingly; the portal is the source of truth and delivery reports arrive asynchronously.
  • Respect Bangladesh telemarketing / BTRC rules — send only to recipients who opted in.

Code samples

Send an SMS to two recipients. Replace the credentials and numbers.

curl -X POST 'https://sms.cloudwavebd.com/api/send' \
  -H 'Content-Type: application/json' \
  -H 'X-Api-Key: cw_YOUR_API_KEY' \
  -H 'X-Api-Secret: YOUR_API_SECRET' \
  -d '{
    "message": "Hello from the API!",
    "to": ["01712345678", "8801812345678"],
    "campaign": "API test"
  }'

Plugins & integrations

Prefer a ready-made integration over writing your own client? Both plugins authenticate with the same API key and secret from this page and bill the same wallet.

WHMCS Module

Send SMS for orders, invoices and tickets, run bulk campaigns, and let clients top up their wallet by paying a WHMCS invoice — all from your WHMCS admin area.

WordPress / WooCommerce Plugin

Order receipts, order-status updates, new-user and comment alerts, and bulk SMS — straight from your WordPress or WooCommerce site.

FAQ

My message went to sent but the recipient says they didn't get it.

sent means the gateway accepted it. Actual handset delivery depends on the recipient's network; a delivery report (when available) later flips the status to delivered.

Can I send the same message to the same number twice?

Yes — set allow_duplicates: true. Otherwise duplicates are removed and billed once.

Why is my cost higher than recipients × rate?

Because the message split into multiple parts (long text, or Bangla/Unicode). See Billing & parts.

Is there a webhook for delivery reports?

Not yet — poll GET /api/history?campaign_id=…. Delivery-report sync is on the roadmap.

What is the rate limit?

Requests are limited by your wallet balance and the 500-recipient cap. Avoid more than a few concurrent sends; split large audiences into sequential batches.

Prefer a ready-made integration? See the WHMCS module and the WordPress plugin.

CloudWave Bulk SMS · API v1 · Base URL https://sms.cloudwavebd.com · Last updated 23 Oct 2026.