Reference

API Documentation

Every endpoint, parameter, and response for the MySMSAPio SMS Gateway. All routes are prefixed with /api/v1.

Getting Started

The MySMSAPio API is a JSON REST API. There are two classes of consumer:

  • Client applications — send SMS, generate/verify OTPs, read inbound messages, and query admin stats. Authenticated with a api_live_… key.
  • Gateway devices — Android phones that register, send heartbeats, report inbound SMS and delivery receipts. Authenticated with a gw_live_… key.

Base URL

https://your-host/api/v1

Content-Type

application/json

Authentication

All authenticated endpoints expect an API key in the Authorization header as a Bearer token. Keys are SHA-256 hashed at rest — the raw key is only shown once at creation.

Client key
api_live_<64 hex chars>

Permissions-based (send_sms, receive_sms…). Can expire.

Gateway key
gw_live_<64 hex chars>

Bound to a single gateway device. Must be active.

# Bearer token in the Authorization header
curl https://your-host/api/v1/sms/received \
  -H "Authorization: Bearer api_live_..."

Rate Limits

Limits are enforced via Redis and return 429 Too Many Requests with a retry_after hint.

Resource Limit Window
Send SMS100per minute / API key
Send OTP3per hour / phone number
OTP verify attempts3per code

Error Codes

Errors are returned as JSON with an error field. Validation errors also include details.

Status Meaning
400 Bad Request — missing required parameter
401 Unauthorized — missing or invalid API key
403 Forbidden — insufficient permissions
404 Not Found — resource does not exist
409 Conflict — e.g. gateway already registered
422 Unprocessable Entity — validation failure
429 Too Many Requests — rate limit exceeded
500 Internal Server Error
Client Application API
POST /sms/send

Send an SMS

Queues an outbound message. The API returns immediately with a message_id; delivery happens asynchronously via an available gateway. Rate limited to 100/min per API key.

Request body

Parameter Type Required Description
tostringyesE.164 phone number (validated via Phonelib)
messagestringyesMessage body (max 1600 chars)

Example request

curl -X POST https://your-host/api/v1/sms/send \
  -H "Authorization: Bearer api_live_..." \
  -H "Content-Type: application/json" \
  -d '{"to":"+14155551234","message":"Your code is 482915"}'

Response · 202 Accepted

{
  "success": true,
  "message_id": "msg_3f8a9c1b2e7d4f60",
  "status": "queued"
}
GET /sms/status/:message_id

Check SMS Status

Returns the current lifecycle state of a message, including timestamps.

Path parameters

Parameter Type Description
message_idstringThe ID returned when the message was created

Response · 200 OK

{
  "message_id": "msg_3f8a9c1b2e7d4f60",
  "status": "delivered",
  "sent_at": "2026-08-01T12:00:00Z",
  "delivered_at": "2026-08-01T12:00:03Z",
  "failed_at": null,
  "error_message": null
}

Statuses: queuedpendingsentdelivered | failed

GET /sms/received

List Received SMS

Paginated list of inbound messages, newest first. Optional filters by phone number or date.

Query parameters

Parameter Type Default Description
phone_numberstringFilter by sender phone
sincedatetimeOnly messages after this time
limitinteger50Items per page

Response · 200 OK

{
  "messages": [
    {
      "message_id": "msg_7a2f...",
      "from": "+14155559999",
      "message": "STOP",
      "received_at": "2026-08-01T12:05:00Z"
    }
  ],
  "total": 1,
  "page": 1,
  "pages": 1
}
POST /otp/send

Send an OTP

Generates a 6-digit code, sends it via SMS, and returns the expiry time. Max 3 codes per phone per hour.

Request body

Parameter Type Required Default
phone_numberstringyes
purposestringnoauthentication
expiry_minutesintegerno5

Response · 200 OK

{
  "success": true,
  "expires_at": "2026-08-01T12:10:00Z",
  "message_id": "msg_9b1c..."
}
POST /otp/verify

Verify an OTP

Validates the 6-digit code. After 3 failed attempts the code is expired. On success the code is marked verified and cannot be reused.

Request body

Parameter Type Required
phone_numberstringyes
codestringyes

Success · 200

{
  "success": true,
  "verified": true
}

Failure · 200

{
  "success": false,
  "verified": false,
  "error": "Invalid or expired OTP",
  "attempts_remaining": 2
}
GET /admin/stats

Admin Statistics

Aggregate system metrics: gateway counts, message throughput, pending/failed counts, and OTP performance for today.

Response · 200 OK

{
  "gateways": { "total": 4, "active": 3, "online": 2, "offline": 2 },
  "messages": {
    "total_sent": 10234, "total_received": 8821,
    "sent_today": 142, "received_today": 98,
    "total_today": 240, "pending": 3, "failed_today": 1
  },
  "otp": { "sent_today": 56, "verified_today": 49, "verification_rate": 87.5 },
  "timestamp": "2026-08-01T12:00:00Z"
}
GET /admin/gateways

List Gateways

Returns every registered gateway device with live status and counters.

POST /admin/gateways/:id/toggle

Activate or deactivate a gateway. Toggling off stops it from receiving outbound commands.

Toggle response · 200 OK

{
  "success": true,
  "gateway": { "id": 1, "device_id": "pixel-001", "active": false }
}
Gateway Device API
POST /gateway/register

Register a Gateway

No auth   Registers an Android device and returns a fresh gateway API key. Returns 409 if the device_id already exists.

Request body

Parameter Type Required Description
device_idstringyesUnique device identifier
namestringnoFriendly name (defaults to device id prefix)

Response · 201 Created

{
  "success": true,
  "api_key": "gw_live_a1b2c3...",
  "device_id": "pixel-001",
  "websocket_url": "wss://your-host/cable"
}

Save api_key immediately — it is never shown again.

POST /gateway/heartbeat

Gateway Heartbeat

Refreshes the gateway's online status. A gateway is marked offline if no heartbeat arrives within 2 minutes (handled by CheckGatewayHealthJob).

Optional body

Parameter Type Description
battery_levelinteger0–100
signal_strengthintegerdBm
messages_in_queueintegerPending outbound count on device

Response · 200 OK

{ "success": true, "pending_messages": 3 }
POST /gateway/sms/received

Report Inbound SMS

Called by the gateway when a new SMS arrives on the device. Creates an inbound record and triggers ProcessInboundSmsJob (which fires webhooks).

Request body

Parameter Type Required
senderstringyes
messagestringyes
timestampdatetimeno

Response · 200 OK

{ "success": true, "message_id": "msg_7a2f..." }
POST /gateway/sms/status

Report Delivery Status

Updates an outbound message's status. On failed, RetryFailedSmsJob is enqueued if retries remain (< 3).

Request body

Parameter Type Required Values
message_idstringyes
statusstringyessent · delivered · failed
error_messagestringnoReason for failure

Response · 200 OK

{ "success": true }
Realtime
WS /cable

WebSocket Channel

Gateways maintain a persistent Action Cable connection for real-time command dispatch and status reporting. Connections authenticate with the gateway API key as a query param.

Connect

wscat -c "wss://your-host/cable?api_key=gw_live_..."

Server → Gateway commands

{
  "action": "send_sms",
  "message_id": "msg_3f8a...",
  "recipient": "+14155551234",
  "message": "Hello!"
}

Gateway → Server events

action Payload
heartbeatbattery_level, signal_strength, messages_in_queue
delivery_reportmessage_id, status, error_message
message_receivedsender, message, timestamp
WEBHOOK Outbound HTTP POST

Webhooks

When configured, the API delivers signed event payloads to your URL via TriggerWebhookJob. If a secret key is set, each request is signed with HMAC-SHA256.

sms_received

Inbound SMS arrived

sms_sent

Message dispatched to gateway

sms_failed

Delivery failed

Example payload (sms_received)

{
  "event": "sms_received",
  "message_id": "msg_7a2f...",
  "from": "+14155559999",
  "message": "Confirm",
  "received_at": "2026-08-01T12:05:00Z"
}

Verifying the signature

# Ruby
signature = OpenSSL::HMAC.hexdigest("SHA256", secret_key, payload.to_json)
# Compare with the X-Webhook-Signature header