Files
MySMSAPio/SAAS_INTEGRATION_GUIDE.md
Min Zeya Phyo 1fdff55544
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
SMS gateway api service for android application
2026-01-27 11:24:16 +06:30

16 KiB

SaaS Application Integration Guide

Overview

This guide shows how to integrate MySMSAPio into your SaaS application to send SMS and OTP messages.

Prerequisites

  1. MySMSAPio Server: Running and accessible (e.g., http://192.168.18.66:3005)
  2. API Key: Client API key (starts with api_live_...)
  3. Gateway Device: At least one Android gateway online

Quick Start

1. Create an API Key

Via Admin Interface:

  1. Go to http://your-server:3005/admin/api_keys
  2. Click "New API Key"
  3. Enter name (e.g., "My SaaS App")
  4. Enable permissions: send_sms, receive_sms
  5. Copy the API key (shown only once!)

Via Rails Console:

result = ApiKey.generate!(
  name: "My SaaS App",
  permissions: {
    send_sms: true,
    receive_sms: true,
    send_otp: true,
    verify_otp: true
  }
)
puts result[:raw_key]
# Save this key: api_live_abc123...

2. Test Connection

curl -X GET http://your-server:3005/up \
  -H "Authorization: Bearer api_live_your_key_here"

Expected: 200 OK

API Endpoints

Base URL

http://your-server:3005/api/v1

Authentication

All requests require the Authorization header:

Authorization: Bearer api_live_your_key_here

Sending SMS

Endpoint

POST /api/v1/sms/send

Request

{
  "to": "+959123456789",
  "message": "Your message here"
}

Response (Success - 202 Accepted)

{
  "success": true,
  "message_id": "msg_abc123def456...",
  "status": "queued"
}

Response (Error - 422 Unprocessable Entity)

{
  "error": "Invalid phone number format"
}

Example: cURL

curl -X POST http://your-server:3005/api/v1/sms/send \
  -H "Authorization: Bearer api_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+959123456789",
    "message": "Hello from My SaaS App!"
  }'

Example: JavaScript/Node.js

const axios = require('axios');

async function sendSMS(phoneNumber, message) {
  try {
    const response = await axios.post(
      'http://your-server:3005/api/v1/sms/send',
      {
        to: phoneNumber,
        message: message
      },
      {
        headers: {
          'Authorization': 'Bearer api_live_your_key_here',
          'Content-Type': 'application/json'
        }
      }
    );

    console.log('SMS sent!');
    console.log('Message ID:', response.data.message_id);
    console.log('Status:', response.data.status);

    return response.data.message_id;
  } catch (error) {
    console.error('Failed to send SMS:', error.response?.data || error.message);
    throw error;
  }
}

// Usage
sendSMS('+959123456789', 'Welcome to our service!')
  .then(messageId => console.log('Sent:', messageId))
  .catch(err => console.error('Error:', err));

Example: Python

import requests

def send_sms(phone_number, message):
    url = "http://your-server:3005/api/v1/sms/send"
    headers = {
        "Authorization": "Bearer api_live_your_key_here",
        "Content-Type": "application/json"
    }
    data = {
        "to": phone_number,
        "message": message
    }

    response = requests.post(url, json=data, headers=headers)

    if response.status_code == 202:
        result = response.json()
        print(f"SMS sent! Message ID: {result['message_id']}")
        return result['message_id']
    else:
        print(f"Failed: {response.json()}")
        raise Exception(response.json().get('error', 'Unknown error'))

# Usage
message_id = send_sms("+959123456789", "Welcome to our service!")

Example: PHP

<?php
function sendSMS($phoneNumber, $message) {
    $url = 'http://your-server:3005/api/v1/sms/send';

    $data = [
        'to' => $phoneNumber,
        'message' => $message
    ];

    $options = [
        'http' => [
            'header'  => [
                "Authorization: Bearer api_live_your_key_here",
                "Content-Type: application/json"
            ],
            'method'  => 'POST',
            'content' => json_encode($data)
        ]
    ];

    $context  = stream_context_create($options);
    $result = file_get_contents($url, false, $context);

    if ($result === FALSE) {
        throw new Exception('Failed to send SMS');
    }

    $response = json_decode($result, true);
    echo "SMS sent! Message ID: " . $response['message_id'] . "\n";

    return $response['message_id'];
}

// Usage
$messageId = sendSMS("+959123456789", "Welcome to our service!");
?>

Example: Ruby

require 'httparty'

def send_sms(phone_number, message)
  response = HTTParty.post(
    'http://your-server:3005/api/v1/sms/send',
    headers: {
      'Authorization' => 'Bearer api_live_your_key_here',
      'Content-Type' => 'application/json'
    },
    body: {
      to: phone_number,
      message: message
    }.to_json
  )

  if response.code == 202
    puts "SMS sent! Message ID: #{response['message_id']}"
    response['message_id']
  else
    puts "Failed: #{response['error']}"
    raise StandardError, response['error']
  end
end

# Usage
message_id = send_sms("+959123456789", "Welcome to our service!")

Sending OTP

Endpoint

POST /api/v1/otp/send

Request

{
  "phone_number": "+959123456789"
}

Response (Success - 200 OK)

{
  "success": true,
  "message": "OTP sent successfully",
  "expires_in": 300
}

Response (Error - Rate Limited)

{
  "error": "Rate limit exceeded. Maximum 3 OTP per hour for this number"
}

Example: JavaScript

async function sendOTP(phoneNumber) {
  try {
    const response = await axios.post(
      'http://your-server:3005/api/v1/otp/send',
      {
        phone_number: phoneNumber
      },
      {
        headers: {
          'Authorization': 'Bearer api_live_your_key_here',
          'Content-Type': 'application/json'
        }
      }
    );

    console.log('OTP sent!');
    console.log('Expires in:', response.data.expires_in, 'seconds');

    return true;
  } catch (error) {
    console.error('Failed to send OTP:', error.response?.data || error.message);
    throw error;
  }
}

// Usage
sendOTP('+959123456789')
  .then(() => console.log('OTP sent successfully'))
  .catch(err => console.error('Error:', err));

Example: Python

def send_otp(phone_number):
    url = "http://your-server:3005/api/v1/otp/send"
    headers = {
        "Authorization": "Bearer api_live_your_key_here",
        "Content-Type": "application/json"
    }
    data = {
        "phone_number": phone_number
    }

    response = requests.post(url, json=data, headers=headers)

    if response.status_code == 200:
        result = response.json()
        print(f"OTP sent! Expires in {result['expires_in']} seconds")
        return True
    else:
        print(f"Failed: {response.json()}")
        raise Exception(response.json().get('error', 'Unknown error'))

# Usage
send_otp("+959123456789")

Verifying OTP

Endpoint

POST /api/v1/otp/verify

Request

{
  "phone_number": "+959123456789",
  "code": "123456"
}

Response (Success - 200 OK)

{
  "success": true,
  "message": "OTP verified successfully"
}

Response (Error - Invalid)

{
  "error": "Invalid or expired OTP"
}

Example: JavaScript

async function verifyOTP(phoneNumber, code) {
  try {
    const response = await axios.post(
      'http://your-server:3005/api/v1/otp/verify',
      {
        phone_number: phoneNumber,
        code: code
      },
      {
        headers: {
          'Authorization': 'Bearer api_live_your_key_here',
          'Content-Type': 'application/json'
        }
      }
    );

    console.log('OTP verified!');
    return true;
  } catch (error) {
    console.error('OTP verification failed:', error.response?.data || error.message);
    return false;
  }
}

// Usage
const isValid = await verifyOTP('+959123456789', '123456');
if (isValid) {
  console.log('User verified!');
} else {
  console.log('Invalid OTP');
}

Example: Python

def verify_otp(phone_number, code):
    url = "http://your-server:3005/api/v1/otp/verify"
    headers = {
        "Authorization": "Bearer api_live_your_key_here",
        "Content-Type": "application/json"
    }
    data = {
        "phone_number": phone_number,
        "code": code
    }

    response = requests.post(url, json=data, headers=headers)

    if response.status_code == 200:
        print("OTP verified successfully!")
        return True
    else:
        print(f"Verification failed: {response.json()}")
        return False

# Usage
if verify_otp("+959123456789", "123456"):
    print("User authenticated!")
else:
    print("Authentication failed")

Checking Message Status

Endpoint

GET /api/v1/sms/status/:message_id

Response

{
  "message_id": "msg_abc123def456...",
  "status": "delivered",
  "sent_at": "2025-10-22T10:30:00Z",
  "delivered_at": "2025-10-22T10:30:05Z",
  "failed_at": null,
  "error_message": null
}

Status Values

  • queued - Waiting for gateway
  • pending - No gateway available, will retry
  • sent - Sent to gateway device
  • delivered - Successfully delivered to recipient
  • failed - Failed to send

Example: JavaScript

async function checkMessageStatus(messageId) {
  try {
    const response = await axios.get(
      `http://your-server:3005/api/v1/sms/status/${messageId}`,
      {
        headers: {
          'Authorization': 'Bearer api_live_your_key_here'
        }
      }
    );

    console.log('Status:', response.data.status);
    console.log('Sent at:', response.data.sent_at);
    console.log('Delivered at:', response.data.delivered_at);

    return response.data;
  } catch (error) {
    console.error('Failed to check status:', error.response?.data || error.message);
    throw error;
  }
}

// Usage
const status = await checkMessageStatus('msg_abc123def456');

Complete Integration Example

User Registration with OTP (JavaScript)

const MySMSAPI = {
  baseURL: 'http://your-server:3005/api/v1',
  apiKey: 'api_live_your_key_here',

  headers() {
    return {
      'Authorization': `Bearer ${this.apiKey}`,
      'Content-Type': 'application/json'
    };
  },

  async sendSMS(phoneNumber, message) {
    const response = await axios.post(
      `${this.baseURL}/sms/send`,
      { to: phoneNumber, message },
      { headers: this.headers() }
    );
    return response.data;
  },

  async sendOTP(phoneNumber) {
    const response = await axios.post(
      `${this.baseURL}/otp/send`,
      { phone_number: phoneNumber },
      { headers: this.headers() }
    );
    return response.data;
  },

  async verifyOTP(phoneNumber, code) {
    try {
      const response = await axios.post(
        `${this.baseURL}/otp/verify`,
        { phone_number: phoneNumber, code },
        { headers: this.headers() }
      );
      return response.data.success;
    } catch (error) {
      return false;
    }
  },

  async checkStatus(messageId) {
    const response = await axios.get(
      `${this.baseURL}/sms/status/${messageId}`,
      { headers: this.headers() }
    );
    return response.data;
  }
};

// Usage Example: User Registration Flow
async function registerUser(phoneNumber, userData) {
  try {
    // Step 1: Send OTP
    console.log('Sending OTP...');
    await MySMSAPI.sendOTP(phoneNumber);
    console.log('OTP sent! Check your phone.');

    // Step 2: Wait for user to enter OTP (in real app, this would be a form)
    const userEnteredCode = '123456'; // Get from user input

    // Step 3: Verify OTP
    console.log('Verifying OTP...');
    const isValid = await MySMSAPI.verifyOTP(phoneNumber, userEnteredCode);

    if (isValid) {
      console.log('✅ Phone verified!');

      // Step 4: Create user account
      const user = await createUserInDatabase(phoneNumber, userData);

      // Step 5: Send welcome SMS
      const result = await MySMSAPI.sendSMS(
        phoneNumber,
        `Welcome to our service, ${userData.name}! Your account is now active.`
      );

      console.log('Welcome SMS sent:', result.message_id);

      return { success: true, user };
    } else {
      console.log('❌ Invalid OTP');
      return { success: false, error: 'Invalid OTP' };
    }
  } catch (error) {
    console.error('Registration failed:', error);
    return { success: false, error: error.message };
  }
}

// Example usage
registerUser('+959123456789', { name: 'John Doe', email: 'john@example.com' });

Error Handling

Common Errors

Status Error Cause Solution
401 Unauthorized Invalid API key Check API key is correct
422 Unprocessable Entity Invalid phone number Use international format (+959...)
429 Too Many Requests Rate limit exceeded Wait before retrying
404 Not Found Message ID not found Check message ID is correct
503 Service Unavailable No gateway online Ensure gateway is connected

Rate Limits

  • SMS Send: 100 per minute per API key
  • OTP Send: 3 per hour per phone number
  • OTP Verify: 3 attempts per OTP code

Best Practices

1. Store API Key Securely

// ❌ Don't hardcode in frontend
const apiKey = 'api_live_abc123...';

// ✅ Use environment variables
const apiKey = process.env.SMS_API_KEY;

// ✅ Store in backend config
const apiKey = config.get('sms.apiKey');

2. Handle Errors Gracefully

async function sendSMSWithRetry(phoneNumber, message, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await MySMSAPI.sendSMS(phoneNumber, message);
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await sleep(1000 * (i + 1)); // Exponential backoff
    }
  }
}

3. Validate Phone Numbers

function validatePhoneNumber(phone) {
  // Myanmar phone numbers
  const myanmarRegex = /^\+959\d{8,9}$/;
  return myanmarRegex.test(phone);
}

4. Monitor Message Status

async function sendAndTrack(phoneNumber, message) {
  const result = await MySMSAPI.sendSMS(phoneNumber, message);

  // Poll status until delivered
  const maxAttempts = 30; // 5 minutes (30 * 10 seconds)
  for (let i = 0; i < maxAttempts; i++) {
    await sleep(10000); // Wait 10 seconds

    const status = await MySMSAPI.checkStatus(result.message_id);

    if (status.status === 'delivered') {
      console.log('✅ Message delivered!');
      return status;
    } else if (status.status === 'failed') {
      console.log('❌ Message failed:', status.error_message);
      throw new Error(status.error_message);
    }
  }

  console.log('⏱️ Delivery timeout');
  return null;
}

Testing

Test API Key

Use the API Tester in the admin interface:

http://your-server:3005/admin/api_tester

Test from Command Line

# Send SMS
curl -X POST http://your-server:3005/api/v1/sms/send \
  -H "Authorization: Bearer api_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{"to":"+959123456789","message":"Test"}'

# Send OTP
curl -X POST http://your-server:3005/api/v1/otp/send \
  -H "Authorization: Bearer api_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{"phone_number":"+959123456789"}'

# Verify OTP
curl -X POST http://your-server:3005/api/v1/otp/verify \
  -H "Authorization: Bearer api_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{"phone_number":"+959123456789","code":"123456"}'

Troubleshooting

Messages Stuck in "pending"

Cause: No gateway online Solution: Ensure at least one Android gateway is connected

"Invalid phone number" error

Cause: Wrong format Solution: Use international format: +[country_code][number]

"Rate limit exceeded"

Cause: Too many requests Solution: Implement exponential backoff and caching

"Unauthorized" error

Cause: Invalid or expired API key Solution: Generate new API key in admin interface

Support

  • API Documentation: See API_DOCUMENTATION.md
  • Admin Interface: http://your-server:3005/admin
  • API Tester: http://your-server:3005/admin/api_tester
  • Logs: http://your-server:3005/admin/logs

Summary

Create API Key → Get api_live_... key Send SMSPOST /api/v1/sms/send Send OTPPOST /api/v1/otp/send Verify OTPPOST /api/v1/otp/verify Check StatusGET /api/v1/sms/status/:id

Your SaaS app is now ready to send SMS and OTP! 🚀