Authentication model
QMGateway intentionally separates customer application credentials from Android gateway credentials.
X-API-Key
Use a qm_live_... API key from a trusted backend server to submit SMS, send or verify OTPs, and read application statistics.
X-API-Key: qm_live_your_keyAuthorization: Bearer
After one-time activation, the Android app receives a qmd_... device token. The app uses that token to fetch queued messages, submit delivery results, send incoming SMS to QMGateway, and send heartbeats.
Authorization: Bearer qmd_your_device_tokenqm_live_... API key inside an Android APK or browser JavaScript. Customer API keys belong on trusted server-side applications. The QMSMSGateway Android app uses its own device token.API key permissions and IP restrictions
Each customer API key can be restricted to only the operations it needs. Available permissions are messages:send, messages:read, otp:send, otp:verify, and statistics:read. You can also restrict a key to specific server IP addresses or CIDR ranges from the API Keys page.
Send SMS from your application
/api/v1/messages.phpSubmit a single, bulk, immediate, or scheduled SMS request using your customer API key.
curl -X POST https://qmgateway.com/api/v1/messages.php \
-H "X-API-Key: qm_live_your_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-100245" \
-d '{
"phone":"+265991234567",
"message":"Your order is ready.",
"type":"transactional"
}'For bulk sending, replace phone with a recipients array. For scheduling, include an ISO-8601 scheduled_at value.
Check message delivery status
/api/v1/message_status.php?id=1052Use a key with messages:read to query a message created by your organisation.
curl "https://qmgateway.com/api/v1/message_status.php?id=1052" \
-H "X-API-Key: qm_live_your_key"{
"success": true,
"data": {
"id": 1052,
"phone_number": "+265991234567",
"status": "Delivered",
"sms_units": 1,
"delivered_at": "2026-08-07 10:30:00"
}
}OTP API
/api/v1/otp/send.phpcurl -X POST https://qmgateway.com/api/v1/otp/send.php \
-H "X-API-Key: qm_live_your_key" \
-H "Content-Type: application/json" \
-d '{"phone":"+265991234567","purpose":"login","expiry_seconds":300}'/api/v1/otp/verify.phpcurl -X POST https://qmgateway.com/api/v1/otp/verify.php \
-H "X-API-Key: qm_live_your_key" \
-H "Content-Type: application/json" \
-d '{"request_reference":"otp_reference","code":"123456"}'Application statistics
/app_statistics.php?period=monthThis endpoint uses the customer application API key.
curl "https://qmgateway.com/app_statistics.php?period=month" \
-H "X-API-Key: qm_live_your_key"Supported periods include today, week, month, year, and custom date ranges.
Activate the QMSMSGateway Android app
A signed-in customer generates a temporary activation code from Connect App. The Android app exchanges that one-time code for a device token.
/api/device/activate.phpcurl -X POST https://qmgateway.com/api/device/activate.php \
-H "Content-Type: application/json" \
-d '{
"activation_code":"ABC12345",
"device_identifier":"android-device-unique-id",
"name":"Office Gateway Phone",
"app_version":"1.0.0"
}'{
"success": true,
"device_token": "qmd_..."
}Fetch the next queued message
/get_sms.phpThis is a gateway-device endpoint. It returns the oldest eligible pending message and atomically assigns it to the authenticated device.
curl https://qmgateway.com/get_sms.php \
-H "Authorization: Bearer qmd_your_device_token"[
{"id":1052,"phone":"+265991234567","message":"Your update is ready."}
]When no message is available, the endpoint returns [].
Update delivery status
/update_status.phpOnly the device that claimed a message can update it. QMGateway marks the message Sent to Gateway when it is claimed. The device reports only the final status: Delivered or Failed.
curl -X POST https://qmgateway.com/update_status.php \
-H "Authorization: Bearer qmd_your_device_token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data "id=1052&status=Delivered"Submit an incoming SMS
/sms_receiver.phpThe Android gateway submits SMS messages received by its SIM using its device token.
curl -X POST https://qmgateway.com/sms_receiver.php \
-H "Authorization: Bearer qmd_your_device_token" \
-H "Content-Type: application/json" \
-d '{"from":"+265991234567","message":"YES"}'Device heartbeat
/api/device/heartbeat.phpUse the device bearer token to tell QMGateway that the Android gateway is online and report its app version.
curl -X POST https://qmgateway.com/api/device/heartbeat.php \
-H "Authorization: Bearer qmd_your_device_token" \
-H "Content-Type: application/json" \
-d '{"app_version":"1.0.0"}'Rate limits
Customer API responses include standard QMGateway rate-limit headers so applications can retry responsibly.
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1786099800When a limit is exceeded, QMGateway returns HTTP 429 and also includes Retry-After.
Error handling
Errors use a consistent structure with a stable machine-readable code, a developer-friendly message, and the same request identifier returned in the X-Request-ID header.
{
"success": false,
"error": {
"code": "INSUFFICIENT_SCOPE",
"message": "This API key does not have the required scope: messages:send.",
"request_id": "b8f49d2f0ea1a233"
}
}Common HTTP statuses are 400 invalid JSON, 401 missing or invalid credentials, 403 insufficient scope or blocked IP, 404 resource not found, 422 validation failure, 429 rate limit exceeded, and 500 server error.
Security guidance
Use HTTPS only. Keep qm_live_... keys on trusted backend servers, store qmd_... device tokens in Android secure storage, rotate or revoke credentials when exposure is suspected, restrict keys by permission and server IP where practical, and use idempotency keys for application requests that may be retried.
QMGateway stores customer API keys and device tokens as hashes and validates organisation, subscription, device, and message ownership before sensitive operations.