RSMS Gateway API Documentation
Version: 1.0
Last Updated: January 3, 2026
Base URL: https://api.rsms.no
Table of Contents
- Overview
- Authentication
- Rate Limits
- API Endpoints
- Request Format
- Response Format
- Error Codes
- Examples
- Best Practices
- Support
Overview
The SMS Gateway API allows you to send SMS messages to multiple recipients through a simple REST API. The gateway handles message delivery and provides comprehensive logging and statistics for billing purposes.
Key Features
- ✅ Bulk SMS Sending - Send to up to 1,000 recipients per request
- ✅ Multi-part Messages - Automatic handling of long messages (up to ~1,000 characters)
- ✅ Tag-based Sender Names - Use predefined tags for sender identification
- ✅ Rate Limiting - Built-in protection against abuse
- ✅ Detailed Logging - Full audit trail of all SMS transactions
- ✅ Real-time Status - Immediate response on message queuing
API Characteristics
- Protocol: HTTPS (TLS 1.2+)
- Format: JSON
- Authentication: Bearer Token (API Key)
- Character Encoding: UTF-8
- Max Request Size: 1 MB
Authentication
All API requests must include an authentication header with your API key.
Header Format
Authorization: Bearer YOUR_API_KEY
Example
curl -X POST https://api.rsms.no/api/v1/sms/send \
-H "Authorization: Bearer sk_live_abc123xyz456..." \
-H "Content-Type: application/json"
API Key Format
- Live Keys:
sk_live_followed by random string (32+ characters) - Test Keys:
sk_test_followed by random string (for future use)
Security Notes
⚠️ Important:
- ⚠️ Never commit API keys to version control
- ⚠️ Store keys securely in environment variables or secret managers
- ⚠️ Each project has one unique API key
- ⚠️ Contact your administrator if your key is compromised
Rate Limits
To ensure fair usage and system stability, the following rate limits apply:
Limits Per API Key
- 100 requests per minute per API key
- Maximum 10 concurrent requests per API key
Limits Per Tag
- 50 requests per minute per tag (sender code)
Rate Limit Headers
Every response includes rate limit information:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1704276000
Handling Rate Limits
When you exceed rate limits, you'll receive a 429 Too Many Requests response:
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again in 60 seconds",
"retryAfter": 60
}
}
Best Practice: Implement exponential backoff when receiving 429 responses.
API Endpoints
Send SMS
Endpoint: POST /api/v1/sms/send
Send SMS messages to one or more recipients.
Request Headers
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Request Body
{
"tag": "R-COV1RSMS",
"recipients": ["+4712345678", "+4787654321"],
"message": "Your message content here"
}
Request Parameters
| Parameter | Type | Required | Description |
| ------------ | ------ | -------- | ---------------------------------------------------------------- |
| tag | string | Yes | Tag code for sender identification (e.g., "R-COV1RSMS" → "RSMS") |
| recipients | array | Yes | Array of phone numbers in E.164 format (1-1000 recipients) |
| message | string | Yes | Message content (1-1000 characters) |
Request Format
Tag Parameter
The tag parameter identifies which sender name to use. Each tag maps to a specific sender name configured in your account.
Rules:
- Must be a valid tag assigned to your project
- Must be active
- Maximum 50 characters
- Examples:
"R-COV1RSMS","proj-alert","billing"
Example Tag Mappings:
"R-COV1RSMS" → "RSMS"
"MH-USV1RSMS" → "Microhard"
"GP-IRV2RSMS" → "Gapple Inc"
Recipients Parameter
Phone numbers must be in E.164 international format.
Format: +[Country Code][Number]
Valid Examples:
[
"+4712345678", // Norway
"+46701234567", // Sweden
"+4407123456789", // UK
"+12025551234" // USA
]
Invalid Examples:
[
"12345678", // ❌ Missing country code
"+47 123 45 678", // ❌ Contains spaces
"0047123456789" // ❌ Wrong format
]
Rules:
- Minimum: 1 recipient
- Maximum: 1,000 recipients per request
- Format:
^\+[1-9]\d{1,14}$ - Duplicates are automatically removed
- Invalid numbers will cause validation errors
Message Parameter
Character Limits:
- Minimum: 1 character
- Maximum: 1,000 characters (allows ~6 SMS segments)
SMS Segmentation:
The gateway automatically handles message splitting:
| Message Type | Single SMS | Multi-part SMS |
| -------------------- | ---------- | ----------------- |
| Standard (GSM-7) | 160 chars | 153 chars/segment |
| Unicode | 70 chars | 67 chars/segment |
Examples:
- 150 characters = 1 SMS segment
- 200 characters = 2 SMS segments (GSM-7)
- 100 characters with emoji = 2 SMS segments (Unicode)
Supported Characters:
- All UTF-8 characters
- Emojis (count as Unicode, use more segments)
- Line breaks (
\n) - Special characters
Response Format
Success Response
HTTP Status: 200 OK
{
"success": true,
"data": {
"messageId": "550e8400-e29b-41d4-a716-446655440000",
"recipientCount": 2,
"smsSegmentsPerRecipient": 1,
"totalSmsSegments": 2,
"estimatedCost": "1.34 NOK",
"status": "queued",
"timestamp": "2026-01-03T10:30:00.000Z"
}
}
Response Fields
| Field | Type | Description |
| ------------------------- | ------- | --------------------------------------------------- |
| success | boolean | Always true for successful requests |
| messageId | string | Unique UUID for this SMS transaction |
| recipientCount | integer | Number of recipients (after deduplication) |
| smsSegmentsPerRecipient | integer | SMS segments required per recipient |
| totalSmsSegments | integer | Total segments sent (recipientCount × segments) |
| estimatedCost | string | Estimated cost (for reference only) |
| status | string | Always "queued" (messages are queued immediately) |
| timestamp | string | ISO 8601 timestamp of when message was queued |
Error Response
HTTP Status: 4xx or 5xx
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": ["Additional context (optional)"]
}
}
Error Codes
Client Errors (4xx)
| Code | HTTP Status | Description | Action Required |
| --------------------- | ----------- | --------------------------------- | ----------------------------------------- |
| UNAUTHORIZED | 401 | Invalid or missing API key | Check your API key |
| FORBIDDEN | 403 | Valid key but lacks permission | Contact administrator |
| IP_NOT_WHITELISTED | 403 | Request from unauthorized IP | Add your IP to whitelist or contact admin |
| INVALID_TAG | 400 | Tag not found or inactive | Use a valid tag from your account |
| VALIDATION_ERROR | 400 | Invalid request payload | Fix validation errors in request |
| RECIPIENT_BLOCKED | 400 | Recipient blocked by SMS provider | Remove blocked recipient |
| RATE_LIMIT_EXCEEDED | 429 | Too many requests | Wait and retry with backoff |
Server Errors (5xx)
| Code | HTTP Status | Description | Action Required |
| --------------------- | ----------- | ------------------------------- | ------------------------------------------ |
| PROVIDER_ERROR | 500 | SMS provider error | Retry request; contact support if persists |
| PROVIDER_AUTH_ERROR | 500 | Provider authentication failed | Contact administrator |
| PROVIDER_NO_CREDITS | 500 | Provider account out of credits | Contact administrator |
| INTERNAL_ERROR | 500 | Unexpected server error | Retry request; contact support if persists |
Error Response Examples
401 - Invalid API Key
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
}
400 - Validation Error
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request payload",
"details": [
"recipients[0]: Invalid phone number format '+47abc'",
"message: Message exceeds maximum length of 1000 characters"
]
}
}
403 - IP Not Whitelisted
{
"success": false,
"error": {
"code": "IP_NOT_WHITELISTED",
"message": "Request from unauthorized IP address"
}
}
429 - Rate Limit Exceeded
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again in 60 seconds",
"retryAfter": 60
}
}
Examples
Example 1: Send to Single Recipient
Request:
curl -X POST https://api.rsms.no/api/v1/sms/send \
-H "Authorization: Bearer sk_live_abc123xyz456" \
-H "Content-Type: application/json" \
-d '{
"tag": "RS-COV1RSMS",
"recipients": ["+4712345678"],
"message": "Hello! This is a test message from our system."
}'
Response:
{
"success": true,
"data": {
"messageId": "550e8400-e29b-41d4-a716-446655440000",
"recipientCount": 1,
"smsSegmentsPerRecipient": 1,
"totalSmsSegments": 1,
"estimatedCost": "0.67 NOK",
"status": "queued",
"timestamp": "2026-01-03T10:30:00.000Z"
}
}
Example 2: Send to Multiple Recipients
Request:
curl -X POST https://api.rsms.no/api/v1/sms/send \
-H "Authorization: Bearer sk_live_abc123xyz456" \
-H "Content-Type: application/json" \
-d '{
"tag": "RS-COV1RSMS",
"recipients": [
"+4712345678",
"+4787654321",
"+46701234567"
],
"message": "Important notification: Your account has been updated."
}'
Response:
{
"success": true,
"data": {
"messageId": "660e8400-e29b-41d4-a716-446655440001",
"recipientCount": 3,
"smsSegmentsPerRecipient": 1,
"totalSmsSegments": 3,
"estimatedCost": "2.01 NOK",
"status": "queued",
"timestamp": "2026-01-03T10:31:00.000Z"
}
}
Example 3: Long Message (Multiple Segments)
Request:
curl -X POST https://api.rsms.no/api/v1/sms/send \
-H "Authorization: Bearer sk_live_abc123xyz456" \
-H "Content-Type: application/json" \
-d '{
"tag": "billing",
"recipients": ["+4712345678"],
"message": "Dear customer, this is a reminder that your invoice #12345 for the amount of 500 NOK is due on January 15, 2026. Please make payment before the due date to avoid late fees. You can pay via our website, mobile app, or bank transfer. For questions, contact support@example.com. Thank you!"
}'
Response:
{
"success": true,
"data": {
"messageId": "770e8400-e29b-41d4-a716-446655440002",
"recipientCount": 1,
"smsSegmentsPerRecipient": 2,
"totalSmsSegments": 2,
"estimatedCost": "1.34 NOK",
"status": "queued",
"timestamp": "2026-01-03T10:32:00.000Z"
}
}
Example 4: JavaScript/Node.js
const axios = require("axios");
async function sendSMS(tag, recipients, message) {
try {
const response = await axios.post(
"https://api.rsms.no/api/v1/sms/send",
{
tag: tag,
recipients: recipients,
message: message,
},
{
headers: {
Authorization: `Bearer ${process.env.SMS_API_KEY}`,
"Content-Type": "application/json",
},
},
);
console.log("SMS sent successfully:", response.data);
return response.data;
} catch (error) {
if (error.response) {
// Server responded with error
console.error("Error:", error.response.data.error);
// Handle rate limiting
if (error.response.status === 429) {
const retryAfter = error.response.data.error.retryAfter;
console.log(`Rate limited. Retry after ${retryAfter} seconds`);
}
} else {
console.error("Network error:", error.message);
}
throw error;
}
}
// Usage
sendSMS(
"RS-COV1RSMS",
["+4712345678", "+4787654321"],
"Hello from our application!",
);
Example 5: Python
import requests
import os
import time
API_KEY = os.environ.get('SMS_API_KEY')
BASE_URL = 'https://api.rsms.no/api/v1'
def send_sms(tag, recipients, message, max_retries=3):
"""
Send SMS with retry logic for rate limiting
"""
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}
payload = {
'tag': tag,
'recipients': recipients,
'message': message
}
for attempt in range(max_retries):
try:
response = requests.post(
f'{BASE_URL}/sms/send',
json=payload,
headers=headers,
timeout=30
)
# Success
if response.status_code == 200:
data = response.json()
print(f"SMS sent successfully: {data['data']['messageId']}")
return data
# Rate limited - retry with backoff
elif response.status_code == 429:
error_data = response.json()
retry_after = error_data['error'].get('retryAfter', 60)
print(f"Rate limited. Waiting {retry_after} seconds...")
time.sleep(retry_after)
continue
# Other errors
else:
error_data = response.json()
print(f"Error: {error_data['error']['message']}")
raise Exception(error_data['error']['code'])
except requests.exceptions.RequestException as e:
print(f"Network error: {e}")
if attempt < max_retries - 1:
wait_time = 2 ** attempt # Exponential backoff
print(f"Retrying in {wait_time} seconds...")
time.sleep(wait_time)
else:
raise
raise Exception("Max retries exceeded")
# Usage
send_sms(
tag='RS-COV1RSMS',
recipients=['+4712345678', '+4787654321'],
message='Hello from our Python application!'
)
Example 6: PHP
<?php
function sendSMS($tag, $recipients, $message) {
$apiKey = getenv('SMS_API_KEY');
$url = 'https://api.rsms.no/api/v1/sms/send';
$data = [
'tag' => $tag,
'recipients' => $recipients,
'message' => $message
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$responseData = json_decode($response, true);
if ($httpCode === 200) {
echo "SMS sent successfully: " . $responseData['data']['messageId'] . "\n";
return $responseData;
} else {
echo "Error: " . $responseData['error']['message'] . "\n";
// Handle rate limiting
if ($httpCode === 429) {
$retryAfter = $responseData['error']['retryAfter'];
echo "Rate limited. Retry after $retryAfter seconds\n";
}
throw new Exception($responseData['error']['code']);
}
}
// Usage
sendSMS(
'RS-COV1RSMS',
['+4712345678', '+4787654321'],
'Hello from our PHP application!'
);
?>
Best Practices
1. Error Handling
Always implement proper error handling:
// ✅ GOOD - Handles all error cases
try {
const response = await sendSMS(tag, recipients, message);
console.log("Success:", response.data.messageId);
} catch (error) {
if (error.response?.status === 429) {
// Rate limited - implement backoff
await sleep(error.response.data.error.retryAfter * 1000);
return retry();
} else if (error.response?.status >= 500) {
// Server error - retry with exponential backoff
return retryWithBackoff();
} else {
// Client error - log and alert
logger.error("SMS failed:", error.response.data);
alertAdmin(error);
}
}
// ❌ BAD - No error handling
const response = await sendSMS(tag, recipients, message);
console.log("Sent!");
2. Rate Limiting
Implement client-side rate limiting to avoid hitting API limits:
// Token bucket algorithm example
class RateLimiter {
constructor(tokensPerMinute) {
this.tokens = tokensPerMinute;
this.maxTokens = tokensPerMinute;
this.lastRefill = Date.now();
}
async acquire() {
this.refill();
if (this.tokens > 0) {
this.tokens--;
return true;
}
// Wait for token refill
const waitTime = 60000 / this.maxTokens;
await sleep(waitTime);
return this.acquire();
}
refill() {
const now = Date.now();
const timePassed = now - this.lastRefill;
const tokensToAdd = (timePassed / 60000) * this.maxTokens;
this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
this.lastRefill = now;
}
}
// Usage
const limiter = new RateLimiter(90); // Stay under 100/min limit
await limiter.acquire();
await sendSMS(tag, recipients, message);
3. Batching Large Recipient Lists
For large recipient lists, split into batches:
async function sendToMany(tag, allRecipients, message) {
const BATCH_SIZE = 1000; // API maximum
const batches = [];
for (let i = 0; i < allRecipients.length; i += BATCH_SIZE) {
batches.push(allRecipients.slice(i, i + BATCH_SIZE));
}
const results = [];
for (const batch of batches) {
const result = await sendSMS(tag, batch, message);
results.push(result);
// Small delay between batches to respect rate limits
if (batches.length > 1) {
await sleep(1000);
}
}
return results;
}
4. Phone Number Validation
Validate phone numbers before sending:
function validatePhoneNumber(phone) {
// E.164 format: +[country code][number]
const e164Regex = /^\+[1-9]\d{1,14}$/;
if (!e164Regex.test(phone)) {
throw new Error(`Invalid phone number format: ${phone}`);
}
return true;
}
// Validate all recipients before API call
const validRecipients = recipients.filter((phone) => {
try {
return validatePhoneNumber(phone);
} catch (e) {
console.warn(`Skipping invalid number: ${phone}`);
return false;
}
});
if (validRecipients.length === 0) {
throw new Error("No valid recipients");
}
5. Message Length Optimization
Calculate segments before sending to estimate costs:
function calculateSmsSegments(message) {
// Check if message contains Unicode characters
const isUnicode = /[^\x00-\x7F]/.test(message);
const length = message.length;
if (isUnicode) {
if (length <= 70) return 1;
return Math.ceil(length / 67);
} else {
if (length <= 160) return 1;
return Math.ceil(length / 153);
}
}
const segments = calculateSmsSegments(message);
const totalSegments = segments * recipients.length;
console.log(`This will use ${totalSegments} SMS segments`);
6. Secure API Key Storage
Never hardcode API keys:
// ❌ BAD - Hardcoded key
const API_KEY = "sk_live_abc123xyz456";
// ✅ GOOD - Environment variable
const API_KEY = process.env.SMS_API_KEY;
// ✅ GOOD - Secret manager (AWS, Azure, GCP)
const API_KEY = await secretManager.getSecret("sms-api-key");
7. Logging and Monitoring
Log all SMS operations for debugging and auditing:
async function sendSMS(tag, recipients, message) {
const requestId = generateUUID();
logger.info("SMS request started", {
requestId,
tag,
recipientCount: recipients.length,
messageLength: message.length,
});
try {
const response = await apiCall(tag, recipients, message);
logger.info("SMS request successful", {
requestId,
messageId: response.data.messageId,
segments: response.data.totalSmsSegments,
});
return response;
} catch (error) {
logger.error("SMS request failed", {
requestId,
errorCode: error.response?.data?.error?.code,
errorMessage: error.response?.data?.error?.message,
});
throw error;
}
}
8. Retry Strategy
Implement exponential backoff for server errors:
async function sendWithRetry(tag, recipients, message, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await sendSMS(tag, recipients, message);
} catch (error) {
const shouldRetry =
error.response?.status >= 500 ||
error.code === "ECONNRESET" ||
error.code === "ETIMEDOUT";
if (!shouldRetry || attempt === maxRetries - 1) {
throw error;
}
const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
console.log(`Retry attempt ${attempt + 1} after ${delay}ms`);
await sleep(delay);
}
}
}
9. Testing
Test with different scenarios before production:
// Test cases to implement
async function runTests() {
// 1. Valid request
await sendSMS("RS-COV1RSMS", ["+4712345678"], "Test message");
// 2. Multiple recipients
await sendSMS("RS-COV1RSMS", ["+4712345678", "+4787654321"], "Test");
// 3. Long message
await sendSMS("RS-COV1RSMS", ["+4712345678"], "A".repeat(200));
// 4. Invalid phone number (should fail)
try {
await sendSMS("RS-COV1RSMS", ["12345678"], "Test");
} catch (e) {
console.log("Expected error:", e.message);
}
// 5. Invalid tag (should fail)
try {
await sendSMS("invalid-tag", ["+4712345678"], "Test");
} catch (e) {
console.log("Expected error:", e.message);
}
}
Support
Getting Help
If you encounter issues or have questions:
- Check this documentation - Most common questions are answered here
- Review error codes - Error messages provide specific guidance
- Check rate limits - Ensure you're not exceeding rate limits
- Contact support - Email: sms@rosario.no
Support Hours
- Email Support: Monday-Friday, 09:00-17:00 CET
- Response Time: Within 24 hours for general inquiries
- Emergency Support: Contact your account manager
Feedback
We value your feedback! Please let us know:
- API improvements you'd like to see
- Documentation clarifications needed
- Issues or bugs encountered
- Feature requests
Email: sms@rosario.no
Changelog
Version 1.0 (January 3, 2026)
- Initial API documentation
- Single endpoint: POST /api/v1/sms/send
- Support for up to 1,000 recipients per request
- Rate limiting: 100 req/min per API key, 50 req/min per tag
- IP whitelisting support
Legal
Terms of Service
By using this API, you agree to:
- Not use the service for spam or illegal purposes
- Obtain proper consent from SMS recipients
- Comply with local telecommunications regulations
- Accept billing based on SMS segments sent
Data Privacy
- We log all SMS transactions for billing and debugging
- Phone numbers and message content are stored securely
- Data retention: 12 months for SMS logs
- Compliance: GDPR compliant
SLA (Service Level Agreement)
- Uptime Target: 99.9%
- Response Time: P95 < 2 seconds
- Support Response: < 24 hours
Last Updated: January 3, 2026
API Version: 1.0
Documentation Version: 1.0