# 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**: ```ruby 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 ```bash 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 ```json { "to": "+959123456789", "message": "Your message here" } ``` ### Response (Success - 202 Accepted) ```json { "success": true, "message_id": "msg_abc123def456...", "status": "queued" } ``` ### Response (Error - 422 Unprocessable Entity) ```json { "error": "Invalid phone number format" } ``` ### Example: cURL ```bash 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 ```javascript 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 ```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 $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 ```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 ```json { "phone_number": "+959123456789" } ``` ### Response (Success - 200 OK) ```json { "success": true, "message": "OTP sent successfully", "expires_in": 300 } ``` ### Response (Error - Rate Limited) ```json { "error": "Rate limit exceeded. Maximum 3 OTP per hour for this number" } ``` ### Example: JavaScript ```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 ```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 ```json { "phone_number": "+959123456789", "code": "123456" } ``` ### Response (Success - 200 OK) ```json { "success": true, "message": "OTP verified successfully" } ``` ### Response (Error - Invalid) ```json { "error": "Invalid or expired OTP" } ``` ### Example: JavaScript ```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 ```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 ```json { "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 ```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) ```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 ```javascript // ❌ 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 ```javascript 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 ```javascript function validatePhoneNumber(phone) { // Myanmar phone numbers const myanmarRegex = /^\+959\d{8,9}$/; return myanmarRegex.test(phone); } ``` ### 4. Monitor Message Status ```javascript 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 ```bash # 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 SMS** → `POST /api/v1/sms/send` ✅ **Send OTP** → `POST /api/v1/otp/send` ✅ **Verify OTP** → `POST /api/v1/otp/verify` ✅ **Check Status** → `GET /api/v1/sms/status/:id` Your SaaS app is now ready to send SMS and OTP! 🚀