API Documentation · v4.1

Your first SMS in five minutes.

Grab your API credentials, POST a JSON payload, receive delivery. The whole REST/JSON surface is here: every endpoint, every parameter, every error code.

REST / JSON POST only 7 endpoints 33 error codes 12 official SDKs AI agent integration

Two ways to integrate

Pick the path that fits your project. Both talk to the same REST/JSON API.

Path A · Recommended

Use an official SDK

Production-tested client libraries covering almost every language. Auth, phone normalization, message cleaning, and bulk chunking are handled for you. Install with your language's package manager and send in three lines.

PHP · Python · JavaScript · Ruby · Go · Java · C# · Swift · Kotlin · Rust · Dart · Zig
Browse the 12 SDKs ↗
Path B · From scratch

Call the REST API directly

One HTTPS POST with a JSON body. Works in any language with an HTTP client. Use this when your language isn't covered above, when you need custom wiring, or when you want full control. The rest of this page is the reference for Path B.

cURL · fetch · axios · requests · Guzzle · HttpClient · any HTTP library
Continue to quickstart

Requirements

What you need before your first call.

1

Active kwtSMS.com account with API enabled.

2

API username and password from the API information page in your account.

3

HTTPS-capable client (all major languages and frameworks).

4

Request headers: Content-Type: application/json, method POST.

Quickstart

Four steps. Copy, paste, send.

1Get your credentials

Sign in to your kwtSMS account and open the API information page. Copy your API username and API password.

Your API username is not your mobile number or account login. It's issued separately on the API information page.

2Set headers

POSTapplication/json
// required request headers Content-Type: application/json Accept: application/json

3Send your first SMS

// POST https://www.kwtsms.com/API/send/ { "username": "myuser", "password": "mypass", "sender": "KWT-SMS", "mobile": "96598765432", "message": "Hello from kwtSMS" }

4Read the response

// 200 OK { "result": "OK", "msg-id": "f4c8…", "numbers": 1, "points-charged": 1, "balance-after": 180 }

Save the msg-id. You'll need it to poll status or DLR later.

Must-know information

The operational facts that bite integrators who skip reading.

Promotional SenderID (KWT-SMS) is for testing only

Delivery is intentionally throttled (~100s+) and blacklisted or Virgin numbers won't receive. For production, register a private senderid. See Promotional vs Transactional SenderID and SenderID help.

Links in messages

International carriers filter links to protect against fraud. Kuwait is fine; messages with links to other countries may be blocked.

Sending speed and rate limits

Max 5 requests per second per IP. Exceed and your IP is blocked automatically. Recommended: 2 per second with a small delay.

International coverage

Disabled by default. Contact support to activate specific destination countries on your account.

Server timezone

Asia/Kuwait (GMT+3). All timestamps in responses use server time.

Security baseline

Always HTTPS. Never hardcode credentials. API lockdown (IP allowlist) is available in the dashboard as an optional second layer.

API reference

All seven endpoints. Every parameter, every response. All calls are POST with Content-Type: application/json.

Send

POSThttps://www.kwtsms.com/API/send/

Sends an SMS to one or many Kuwait-format numbers. Auto-detects language (English GSM-7 or Arabic UCS-2). Returns a unique msg-id for status and DLR lookups.

Copy-paste examples PHP · Python · JavaScript · Ruby · Go · Java · C# · Swift · Kotlin · Rust · Dart · Zig
ParameterTypeMaxDescription
usernamereqalphanumeric15API username.
passwordreqalphanumeric30API password.
senderreqalphanumeric11Private SenderID (case-sensitive).
mobilereqnumeric2500Comma-separated Kuwait numbers. No +, 00, (), . or spaces.
messagereqalphanumericno limitMessage body. Use \n for new lines. No HTML or emoji.
testnumeric10 or 1. If 1, message queued but not sent (saves test credits).
Sample input
{ "username": "myuser", "password": "mypass", "sender": "KWT-SMS", "mobile": "96598765432,96598765433", "message": "تجربة + عربي", "test": "1" }
Sample output
{ "result": "OK", "msg-id": "f4c8…", "numbers": 2, "points-charged": 2, "balance-after": 178, "unix-timestamp": 1684763355 }

Balance

POSThttps://www.kwtsms.com/API/balance/

Returns the account's current SMS credit balance and total credits purchased. Every send response also returns balance-after, so you rarely need this call on its own.

ParameterTypeMaxDescription
usernamereqalphanumeric15API username.
passwordreqalphanumeric30API password.
Sample input
{ "username": "myuser", "password": "mypass" }
Sample output
{ "result": "OK", "available": 150, "purchased": 1000 }

Sender IDs

POSThttps://www.kwtsms.com/API/senderid/

Returns the list of sender IDs your account can send from. Use this to build a dropdown or to validate the sender string before calling send.

ParameterTypeMaxDescription
usernamereqalphanumeric15API username.
passwordreqalphanumeric30API password.
Sample input
{ "username": "myuser", "password": "mypass" }
Sample output
{ "result": "OK", "senderid": ["KWT-SMS", "MYAPP"] }

Coverage

POSThttps://www.kwtsms.com/API/coverage/

Returns the country prefixes active on your account. Call this when a send fails with ERR026 to confirm the destination country is activated.

ParameterTypeMaxDescription
usernamereqalphanumeric15API username.
passwordreqalphanumeric30API password.
Sample input
{ "username": "myuser", "password": "mypass" }
Sample output
{ "result": "OK", "prefixes": ["965", "971"] }

Validate

POSThttps://www.kwtsms.com/API/validate/

Checks numbers for format validity and route availability before you attempt to send. Returns three buckets: OK (good), ER (format error, typically a + or 00 prefix), and NR (no route, country not activated).

ParameterTypeMaxDescription
usernamereqalphanumeric15API username.
passwordreqalphanumeric30API password.
mobilereqnumeric2500Comma-separated numbers to validate.
Sample input
{ "username": "myuser", "password": "mypass", "mobile": "0096598765432,+96598765432,96598765432" }
Sample output
{ "result": "OK", "mobile": { "ER": ["0096598765432", "+96598765432"], "NR": [], "OK": ["96598765432"] } }

Status

POSThttps://www.kwtsms.com/API/status/

Returns the queue status of a sent message: pending, error in queue, or sent to gateway. This is not a delivery report; it tells you whether the gateway has dispatched the message.

ParameterTypeMaxDescription
usernamereqalphanumeric15API username.
passwordreqalphanumeric30API password.
msgidreqalphanumeric32The msg-id returned by the earlier send call.
Sample input
{ "username": "myuser", "password": "mypass", "msgid": "f4c841adee210f31307633ceaebff2ec" }
Sample output
{ "result": "OK", "status": "sent", "description": "Message successfully sent to gateway" }

Delivery report (DLR)

POSThttps://www.kwtsms.com/API/dlr/

Delivery reports for international messages. Kuwait numbers don't return DLR. Wait at least 5 minutes after sending before calling, and add a delay between requests if you poll in bulk, or your IP may be blocked.

ParameterTypeMaxDescription
usernamereqalphanumeric15API username.
passwordreqalphanumeric30API password.
msgidreqalphanumeric32The msg-id returned by the earlier send call.
Sample input
{ "username": "myuser", "password": "mypass", "msgid": "f4c841adee210f31307633ceaebff2ec" }
Sample output
{ "result": "OK", "report": [ { "Number": "96598765432", "Status": "Received by recipient" } ] }

Error codes

All 33 error responses. Every code returned by every endpoint. Errors follow the shape {"result":"ERROR","code":"ERRxxx","description":"…"}.

CodeFunctionDescription
ERR001ALLAPI off on this account.
ERR002ALLUsername, password, or other required parameter is missing.
ERR003ALLWrong username or password.
ERR004ALLYour account does not have API access.
ERR005ALLYour account has been blocked.
ERR006sendNo valid numbers submitted.
ERR007sendCannot send more than 200 numbers at a time.
ERR008sendThe senderid chosen is banned.
ERR009sendMessage parameter is empty.
ERR010sendYour account balance is zero.
ERR011sendNot enough balance to send this message.
ERR012sendCannot send more than 6 page messages (message too long).
ERR013sendYour queued messages reached 1000, wait and try again.
ERR019dlrNo reports found.
ERR020dlrMessage does not exist.
ERR021dlrMessage does not have a delivery report.
ERR022dlrDelivery reports not ready. Check back after 24 hours.
ERR023dlrUnknown error. Could not get delivery reports.
ERR024ALLAPI lockdown is ON and the IP address is not in the allowed list.
ERR025sendNo valid numbers found. Must be digits only with country code. No +, 00, (), . or space.
ERR026sendNo route for this number. Country not activated. Contact support to add.
ERR027sendHTML tags not allowed.
ERR028sendMust wait 15 seconds before sending to the same number again.
ERR029statusMessage does not exist or the msgid is wrong.
ERR030statusMessage stuck in queue with error (delete to recover credits).
ERR031sendBad language detected, send rejected.
ERR032sendSpam message detected, send rejected.
ERR033coverageNo active coverage on your account. Contact support to add one.

Best practices

The checklist we hand production teams before go-live. Full write-up: SMS API implementation best practices.

01

Always use HTTPS

SSL encrypts your traffic and protects against eavesdropping.

02

Never hardcode credentials

Use environment variables or a config file so passwords can rotate without a deploy.

03

Test English and Arabic before handoff

If the client sends a language you didn't test, output can turn into gibberish.

04

Include the app name in OTP messages

Telecom rules require OTP and critical notifications identify the sender. Example: "Your OTP for APPNAME is 12345".

05

Allow 3 minutes before OTP resend

Gives users room to receive and read the SMS before they hit "Resend".

06

Sanitize user input, especially mobile numbers

Remove +, 00, spaces. Add country code if missing. Reject Arabic or Hindi digits.

07

Don't poll /balance/ after each send

The send response already returns points-charged and balance-after.

08

Stop bots and automation

Use captcha or similar on signup and login forms to prevent abuse and credit drainage.

09

Enforce your own rate limits

Per-IP and per-phone limits over time protect your balance and your users.

10

Delay between requests

Max 5 per second per IP. We recommend 2 per second in production.

Testing and verification

Before you flip to live mode, run every test below. Each row is "do this, expect this". Full write-up: SMS API integration test checklist.

Use "test":"1" on every send call until the last group. Test messages queue but don't deliver and don't burn credits. Delete them from the dashboard Queue to recover any held credit.

1Smoke test, one call per endpoint

DoExpect
POST /API/balance/ with your API username and password.{"result":"OK","available":N,"purchased":M}
POST /API/senderid/ with the same credentials.{"result":"OK","senderid":["KWT-SMS", ...]}
POST /API/coverage/.{"result":"OK","prefixes":["965", ...]}
POST /API/validate/ with "mobile":"96598765432".The number comes back inside the OK array.
POST /API/send/ with "test":"1" to one of your own numbers.{"result":"OK","msg-id":"...","numbers":1,...}. Save the msg-id.
POST /API/status/ with the msgid you just saved.{"result":"OK","status":"sent",...}

2Phone number edge cases

Mobile inputExpected behavior
+96598765432Accepted. Your client must strip the + before posting.
0096598765432Accepted. Your client must strip the leading 00.
965 9876 5432Accepted. Your client must strip spaces.
965-9876-5432Accepted. Your client must strip dashes.
٩٦٥٩٨٧٦٥٤٣٢ (Arabic digits)Convert to Latin digits before sending. API rejects Arabic digits.
96522334455 (Kuwait landline)API returns ERR025. No SMS sent, no credit charged.
123456 (too short)API returns ERR006 or ERR025. No SMS sent.
abc123 (not a number)API returns ERR025. No SMS sent.
971504496677 (country not activated)API returns ERR026. Contact support to enable coverage.

3Send flow, end to end

DoExpect
Send an English message with "test":"1".result:"OK". Message appears in the dashboard Queue. Handset does NOT receive.
Delete the test message from the Queue.Any held credit is refunded immediately.
Send an Arabic message with "test":"0" (real send).Handset receives within 60 seconds. Arabic displays correctly, not ???? or boxes.
Send an OTP message that includes your app name (e.g. "Your OTP for APPNAME is 123456").Handset receives. Message passes telecom compliance check.
Poll /API/status/ with the msgid."status":"sent" once dispatched to carrier.
Send the same OTP to the same number twice in under 15 seconds.Second call returns ERR028. First call is unaffected.

4Error triggers, confirm your app handles each

DoExpect
Send with a wrong password.ERR003. Your app shows a clear auth error, not a stack trace.
Send with an empty message field.ERR009.
Send to 201 numbers in one call.ERR007. Chunk into batches of 200.
Send a message with HTML tags.ERR027. Strip HTML before sending.
Send a message with emojis.Often stuck silently in queue with no error. Strip emojis in your client.
Trigger rate limit: fire 10 sends per second from one IP.IP auto-blocked. Keep sends at 2 per second or fewer.

5Go-live blockers, fix before launch

IssueSeverityCheck
SMS not arriving at all.CriticalEnsure "test":"0", your private senderid is approved, and the number is not blacklisted.
Arabic shows as ???? or boxes.CriticalPost the body as UTF-8 JSON. Confirm your HTTP client doesn't re-encode.
No rate limiting on OTP endpoint.CriticalBots can drain your balance in minutes. Rate limit per phone (3 to 5 per hour) and per IP.
No CAPTCHA on signup or login OTP forms.CriticalAdd captcha before the SMS is sent, not after.
App name missing from OTP body.HighTelecom compliance rule for OTP and critical alerts.
Phone format variants (+, 00, spaces) not handled.HighNormalize on input: digits only, strip leading zeros.
OTP codes never expire.HighSet a 3 to 5 minute TTL.
No resend countdown timer.MediumBlock resend for 3 to 4 minutes so users don't spam.
Using KWT-SMS in production.CriticalRegister a private senderid (and pick Transactional for OTP).

FAQs

The questions support handles most days.

I always get "Username or password is wrong" even though I'm sure they're correct.
First, confirm you're using the API username and password from the API information page in your kwtSMS account. Those are separate from your account login, and your mobile number won't work as a username. If that's correct and you still see the error, you're likely sending the request as GET. Don't. Use POST with Content-Type: application/json and put the credentials in the JSON body.
How do I add new lines in text messages? +
Use \n in the message field.
I sent a message, credits deducted, but the message was not received. +
Check the Sending Queue, confirm "test":"0", check Archive, verify number format, consider blacklist/DND, allow for campaign-season delays. Contact support with name, account, msg-id, timestamp, and message text.
Why is the senderid always KWT-SMS? +
That's the default promotional senderid. Buy and register a private senderid on your account to use your own. SenderID is case-sensitive (Kuwait ≠ kuwait ≠ KUWAIT). See SenderID help.
How do I register a sender ID, and what documents are required? +
Sign in to your kwtSMS account and open Menu → Buy Sender ID. Complete the purchase. The exact document checklist appears on the sender ID page right after purchase, tailored to the sender ID type you bought. Follow the step-by-step instructions there, then send the documents to us by email or WhatsApp. If anything is unclear, reach out via Contact support.
Why aren't delivery reports ready? +
DLR usually takes 24 hours. After Kuwait implemented Mobile Number Portability (MNP), DLR isn't fully reliable for measuring campaign effectiveness or link open rates. Kuwait numbers do not return DLR at all; only international numbers do.
What is a blacklist number and how do I fix it? +
A DND list filter set by the telecom to block marketing or spam. Only the number owner can ask their carrier to remove them. Whitelisted transactional SenderIDs can reach DND numbers for OTP and critical notifications.
How do I whitelist my SenderID for OTP? +
Send a request on your company letterhead. We'll handle the rest with the carriers. Details: SenderID help.
I can't open your website. Is it down? +
More likely your public IP was auto-blocked by our firewall. Contact support with your public IP to unblock.
AI Agent Integration

Talk to kwtSMS from any AI agent

kwtsms-cli exposes the full REST/JSON API as natural-language tool calls. Drop it into Claude Code, Cursor, or any MCP-compatible host, then say "send an OTP to …" and watch it happen.

Set up kwtsms-cli