Complete guide to integrating SMESS WhatsApp API into your application
SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Create and reveal keys from your client portal.
Messages are sent from your own WhatsApp number. Link it once by scanning a QR code on the Connections page (WhatsApp → Linked Devices). Until a number is connected, sends can't deliver.
Register or log in, then create a key at API Keys.
SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Send messages via POST to /api/send using apikey, recipient and text — or try it without writing code in the API Tester, the fastest way to a first successful send.
The API returns HTTP 202 with a queue_id; errors carry an error_code. Watch delivery in Messages / Queue, or get push notifications via Delivery Webhooks.
All API requests require your API key. You can authenticate in three ways (in priority order):
| Method | How | Notes |
|---|---|---|
| POST field | apikey=SM-... |
Recommended for server-to-server integrations. |
| Header | X-API-Key: SM-... |
Preferred for cleaner logs. |
| Bearer | Authorization: Bearer SM-... |
Standard OAuth-style header. |
curl -X POST https://smess.io/api/send \
-H "X-API-Key: SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-d "recipient=+233244123456" \
-d "text=Hello World!"
JSON request bodies are supported. Send Content-Type: application/json with the same parameter names — form-encoded and JSON behave identically:
curl -X POST https://smess.io/api/send \
-H "Content-Type: application/json" \
-H "X-API-Key: SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-d '{"recipient": "+233244123456", "text": "Hello World!"}'
Messages are delivered from your own WhatsApp number — recipients see your number, your business name, and your profile photo. You link the number once by scanning a QR code on the Connections page; it stays linked as a companion device.
| Tier | Channel | Sender the recipient sees |
|---|---|---|
| Primary | Your linked WhatsApp session | Your number, name & photo |
| Fallback | Email alert to you | If your session drops, nothing is sent from any other number — you're alerted immediately so you can re-link (takes seconds). One number, one channel, maximum account safety. |
WhatsApp aggressively flags newly registered numbers that immediately start sending automated traffic — that can get a number restricted or banned regardless of which API or tool you use. Before going live:
| 1–2 weeks of normal use | Use the number in the WhatsApp app like a human first: real two-way conversations, a group or two, complete business profile (name, photo, category, description). |
| Ramp volumes gradually | Start low, prioritise recipients who have saved your number or messaged you first, and grow over weeks — never launch a fresh number straight into a campaign. |
| Automatic protection | SMESS paces every newly connected number with a warm-up ramp during its first week (shorter sending intervals unlock as the number matures). Slower early delivery is deliberate protection, not a malfunction. |
| Meta Verified | Recommended: subscribe to Meta Verified in the WhatsApp Business app (Settings → Meta Verified, ∼US$11/month). You get the verified badge — stronger recipient trust — plus access to Meta support channels if your number is ever flagged. |
POST https://smess.io/api/send
Messages are submitted to a queue and delivered by the background worker. The API returns HTTP 202 Accepted on success, not 200.
| Name | Type | Required | Description |
|---|---|---|---|
| apikey | string | Yes | Your API authentication key. Also accepted as X-API-Key or Authorization: Bearer header. Not api_key. |
| recipient | string | Yes | Phone number with country code (e.g., +233244123456). Not phone. |
| text | string | Yes* | Message text or media caption. Not message. *At least one content field is required. |
| priority | string/int | No | otp / urgent / high / normal (default) / bulk / low |
| idempotency_key | string | No | Unique key per send attempt. Replaying the same key within 5 minutes returns the original response without sending a duplicate. Recommended for OTPs. |
The endpoint detects the message type from the parameters you provide. Include only the parameters for the type you want to send:
| Type | Required parameter(s) |
|---|---|
| Text | text |
| Image | file (image URL) |
| Document | document (file URL) + optional filename |
| Video | video |
| Audio | audio |
| Location | latitude + longitude + optional label |
| Contact card | contact_name + contact_phone |
| Buttons | button1 + button1id (up to 3) |
| Copy code (OTP) | copycode + optional copytext |
| List menu | list_title + list_button + list_sections (JSON) |
curl -X POST https://smess.io/api/send \
-H "X-API-Key: SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-d "recipient=+233244123456" \
-d "text=Hello from SMESS!"
{
"success": true,
"message": "Message queued successfully. It will be sent by the queue worker.",
"data": {
"queue_id": 42,
"recipient": "+233244123456",
"message_type": "text",
"priority": 5,
"priority_label": "normal",
"status": "queued",
"timestamp": "2026-06-16 10:30:00",
"remaining_quota": 999,
"note": "Message will be sent by queue worker with rate limiting and spam prevention"
}
}
All message types use the same POST /api/send endpoint. The type is auto-detected from the parameters you send.
curl -X POST https://smess.io/api/send \
-H "X-API-Key: SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-d "recipient=+233244123456" \
-d "document=https://example.com/invoice.pdf" \
-d "filename=Invoice-2026.pdf" \
-d "text=Here is your invoice."
curl -X POST https://smess.io/api/send \
-H "X-API-Key: SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-d "recipient=+233244123456" \
-d "file=https://example.com/photo.jpg" \
-d "text=Check this out!"
curl -X POST https://smess.io/api/send \
-H "X-API-Key: SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-d "recipient=+233244123456" \
-d "text=Confirm your delivery?" \
-d "button1=Yes" \
-d "button1id=confirm" \
-d "button2=No" \
-d "button2id=cancel"
curl -X POST https://smess.io/api/send \
-H "X-API-Key: SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-d "recipient=+233244123456" \
-d "text=Your verification code is:" \
-d "copycode=482916" \
-d "copytext=Tap to Copy" \
-d "priority=otp" \
-d "idempotency_key=otp-user-123-1785290338"
curl -X POST https://smess.io/api/send \
-H "X-API-Key: SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-d "recipient=+233244123456" \
-d "latitude=5.6037" \
-d "longitude=-0.1870" \
-d "label=Accra Mall"
curl -X POST https://smess.io/api/send \
-H "X-API-Key: SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-d "recipient=+233244123456" \
-d "text=Please select a department:" \
-d "list_title=Contact Us" \
-d "list_button=View Departments" \
-d 'list_sections=[{"title":"Sales","rows":[{"title":"New Orders","description":"Place a new order","rowId":"sales_new"}]}]'
For campaigns, submit many messages in one call with POST /api/bulk Growth & Enterprise plans. The queue worker paces delivery automatically (smart-sending gaps and caps still apply per recipient).
curl -X POST https://smess.io/api/bulk \
-d "apikey=SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-d 'messages=[
{"recipient": "+233244123456", "text": "Hello Ama!"},
{"recipient": "+233209876543", "text": "Hello Kojo!", "document": "https://example.com/invoice.pdf"}
]'
| Parameter | Type | Description |
|---|---|---|
| messages | JSON array | Array of {recipient, text, document} objects. Alternative: upload a CSV file as csv_file with header recipient, text, document. |
| campaign_id | optional | Your identifier for the batch — returned in the response and attached to every queued message. Auto-generated if omitted. |
| scheduled_at | optional | YYYY-MM-DD HH:MM:SS — queue now, deliver later. Great for campaigns timed to business hours. |
| priority | optional | Defaults to bulk priority (paced slower than transactional sends so they never block OTPs or receipts). |
Response:
{
"success": true,
"queued": 2,
"total_submitted": 2,
"campaign_id": "spring-promo-01",
"message": "2 messages queued for delivery"
}
errors array while the rest queue normally. Delivery outcomes arrive per message via Delivery Webhooks or the Queue page.
After queuing, track message status in the client portal or via the message detail API:
| Resource | URL | Purpose |
|---|---|---|
| Queue | /my-queue | Pending / processing / sent / failed messages |
| Message History | /my-messages | Delivered messages and delivery receipts |
queue_id returned in the 202 response and use it to correlate with status updates in your dashboard or webhooks.
Prefer push over polling? Register a delivery-status webhook on your account (contact support to enable it) and SMESS will POST a JSON event to your HTTPS endpoint whenever one of your messages reaches a final state:
{
"event": "message.status",
"queue_id": 12345,
"status": "sent",
"recipient": "233244123456",
"error": null,
"attempts": 1,
"timestamp": "2026-07-24T15:04:05+00:00"
}
| Detail | Behaviour |
|---|---|
| status | sent or failed — on failure, error carries the reason (delivery error, spam-rule block, recipient cap, …) |
| Signature | With a signing secret configured, every request carries X-SMESS-Signature: sha256=<hex> — the HMAC-SHA256 of the raw body. Verify it before trusting the payload. |
| Delivery | 5-second timeout, one automatic retry, best-effort — a webhook outage never delays your messages. Respond with any 2xx quickly; process async. |
| Held messages | Gap/cap delays don't fire events — only final sent / failed outcomes do. Correlate using the queue_id from your send response. |
The API returns JSON error responses with an HTTP status code and an error_code when available:
| 400 | Bad Request — missing or invalid parameters (e.g., wrong phone format) |
| 401 | Unauthorized — API key missing or invalid (INVALID_KEY) |
| 402 | Payment Required — quota exceeded (QUOTA_EXCEEDED) or subscription/trial expired |
| 403 | Forbidden — key inactive/revoked (KEY_INACTIVE) or account suspended |
| 413 | Payload Too Large — media file exceeds your plan limit |
| 429 | Too Many Requests — per-minute rate limit or per-recipient cooldown (OTP_COOLDOWN) |
| 503 | Service Unavailable — WhatsApp connection not ready (WHATSAPP_DISCONNECTED / NOT_ACTIVATED) |
{
"success": false,
"error": "Invalid API key",
"error_code": "INVALID_KEY",
"code": 401
}
Limits are per API key and depend on your plan:
| Limit | Starter | Growth | Enterprise |
|---|---|---|---|
| API submissions / minute | 60 | 300 | 1000 |
| Document/media max size | 25 MB | 50 MB | 100 MB |
| Per-recipient minimum gap | 60 seconds (configurable 0–600s per key) | ||
| Per-recipient daily cap | 50 / day (default — raisable per API key on request, e.g. for event-day alert streams) | ||
priority=bulk and submit via POST /api/bulk (Growth+). The queue worker spaces out delivery automatically.
Common mistakes that cause API errors:
The server did not receive a valid key. Common causes:
| ❌ Wrong (will fail) | ✅ Correct |
|---|---|
api_key (with underscore) | apikey |
key or token | apikey / X-API-Key / Authorization: Bearer |
JSON body (Content-Type: application/json) | application/x-www-form-urlencoded or multipart |
Query string ?apikey=... | POST field or header (query string is deprecated and logged) |
| ❌ Wrong (will fail) | ✅ Correct |
|---|---|
phone, number, to | recipient |
message, msg, body | text |
image, photo | file (images) or document (any file) |
curl -X POST https://smess.io/api/send \
-H "X-API-Key: SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-d "recipient=+233244123456" \
-d "text=Hello, this works!"
# WRONG - these parameter names are NOT accepted
curl -X POST https://smess.io/api/send \
-H "Content-Type: application/json" \
-d '{"api_key":"SM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","phone":"+233244123456","message":"This will fail"}'