Developer centre

Build SMS and OTP workflows safely

Customer systems and the QMSMSGateway Android app use different credentials. Keep application API keys on your backend, and use device bearer tokens only on activated gateway phones.

Authentication model

QMGateway intentionally separates customer application credentials from Android gateway credentials.

Customer application API

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.

HTTP header
X-API-Key: qm_live_your_key
QMSMSGateway Android app

Authorization: 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.

HTTP header
Authorization: Bearer qmd_your_device_token
Do not put a qm_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.

Use separate keys for production, staging, and individual backend services. Give each key the minimum permissions required.

Send SMS from your application

POST /api/v1/messages.php

Submit a single, bulk, immediate, or scheduled SMS request using your customer API key.

cURL
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

GET /api/v1/message_status.php?id=1052

Use a key with messages:read to query a message created by your organisation.

cURL
curl "https://qmgateway.com/api/v1/message_status.php?id=1052" \
  -H "X-API-Key: qm_live_your_key"
Response
{
  "success": true,
  "data": {
    "id": 1052,
    "phone_number": "+265991234567",
    "status": "Delivered",
    "sms_units": 1,
    "delivered_at": "2026-08-07 10:30:00"
  }
}

OTP API

POST /api/v1/otp/send.php
cURL
curl -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}'
POST /api/v1/otp/verify.php
cURL
curl -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

GET /app_statistics.php?period=month

This endpoint uses the customer application API key.

cURL
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.

POST /api/device/activate.php
cURL
curl -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"
  }'
Response
{
  "success": true,
  "device_token": "qmd_..."
}
Store the returned device token in Android secure storage. The token is specific to that gateway phone and can be disabled independently.

Fetch the next queued message

GET /get_sms.php

This is a gateway-device endpoint. It returns the oldest eligible pending message and atomically assigns it to the authenticated device.

cURL
curl https://qmgateway.com/get_sms.php \
  -H "Authorization: Bearer qmd_your_device_token"
Response
[
  {"id":1052,"phone":"+265991234567","message":"Your update is ready."}
]

When no message is available, the endpoint returns [].

Update delivery status

POST /update_status.php

Only 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
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

POST /sms_receiver.php

The Android gateway submits SMS messages received by its SIM using its device token.

cURL
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

POST /api/device/heartbeat.php

Use the device bearer token to tell QMGateway that the Android gateway is online and report its app version.

cURL
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.

HTTP headers
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1786099800

When 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.

JSON response
{
  "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.