SMS Opt-In API Integration
Overview
The SMS Opt-In API allows you to programmatically opt-in users to receive SMS notifications through SendSquared. This integration is essential for maintaining compliance while keeping your customers informed throughout their journey.
Why Use SMS Opt-In API?
Integrating the SMS opt-in feature into your workflow significantly improves user engagement and satisfaction by:
- Keeping customers informed at every step
- Enabling real-time notifications
- Providing automated communication touchpoints
- Ensuring regulatory compliance
API Endpoint
PUT https://app-api.sendsquared.com/v1/public/opt-in/
Authentication
All requests must include an API key in the headers:
x-api: YOUR_API_KEY_UUID
Your API key can be found in your SendSquared dashboard under Settings → API Keys. Keep this key secure and never expose it in client-side code.
Request Format
Required Headers
Content-Type: application/json
x-api: YOUR_API_KEY_UUID
Request Payload
The payload must include the following fields:
Field | Type | Required | Description |
---|---|---|---|
mobile_phone | string | Yes | Mobile phone number in E164 format (e.g., +11234567890 ) |
ip_address | string | Yes | User's IP address for compliance tracking |
user_agent | string | Yes | User's browser user agent string |
opt_in_number_id | string | Yes | The specific opt-in number ID from your campaign |
Example Payload
{
"mobile_phone": "+11234567890",
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"opt_in_number_id": "num_1234567890abcdef"
}
Code Examples
cURL
curl -X PUT https://app-api.sendsquared.com/v1/public/opt-in/ \
-H "Content-Type: application/json" \
-H "x-api: YOUR_API_KEY_UUID" \
-d '{
"mobile_phone": "+11234567890",
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"opt_in_number_id": "num_1234567890abcdef"
}'
JavaScript (Fetch API)
const optInUser = async (userData) => {
const response = await fetch('https://app-api.sendsquared.com/v1/public/opt-in/', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'x-api': 'YOUR_API_KEY_UUID'
},
body: JSON.stringify({
mobile_phone: userData.phone,
ip_address: userData.ipAddress,
user_agent: navigator.userAgent,
opt_in_number_id: 'num_1234567890abcdef'
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
};
Python
import requests
import json
def opt_in_user(phone, ip_address, user_agent, opt_in_number_id):
url = "https://app-api.sendsquared.com/v1/public/opt-in/"
headers = {
"Content-Type": "application/json",
"x-api": "YOUR_API_KEY_UUID"
}
payload = {
"mobile_phone": phone,
"ip_address": ip_address,
"user_agent": user_agent,
"opt_in_number_id": opt_in_number_id
}
response = requests.put(url, headers=headers, data=json.dumps(payload))
if response.status_code == 200:
return response.json()
else:
raise Exception(f"API request failed with status {response.status_code}")
PHP
<?php
function optInUser($phone, $ipAddress, $userAgent, $optInNumberId) {
$url = 'https://app-api.sendsquared.com/v1/public/opt-in/';
$data = array(
'mobile_phone' => $phone,
'ip_address' => $ipAddress,
'user_agent' => $userAgent,
'opt_in_number_id' => $optInNumberId
);
$options = array(
'http' => array(
'header' => "Content-Type: application/json\r\n" .
"x-api: YOUR_API_KEY_UUID\r\n",
'method' => 'PUT',
'content' => json_encode($data)
)
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === FALSE) {
throw new Exception('API request failed');
}
return json_decode($result, true);
}
?>
Node.js (Axios)
const axios = require('axios');
const optInUser = async (phoneNumber, ipAddress, userAgent, optInNumberId) => {
try {
const response = await axios.put(
'https://app-api.sendsquared.com/v1/public/opt-in/',
{
mobile_phone: phoneNumber,
ip_address: ipAddress,
user_agent: userAgent,
opt_in_number_id: optInNumberId
},
{
headers: {
'Content-Type': 'application/json',
'x-api': 'YOUR_API_KEY_UUID'
}
}
);
return response.data;
} catch (error) {
console.error('Opt-in failed:', error.response?.data || error.message);
throw error;
}
};
Response Format
Success Response (200 OK)
{
"success": true,
"message": "User successfully opted in",
"data": {
"contact_id": "con_1234567890abcdef",
"phone_number": "+11234567890",
"opt_in_status": "active",
"opted_in_at": "2024-03-28T12:00:00Z"
}
}
Error Responses
400 Bad Request
{
"success": false,
"error": "Invalid phone number format",
"error_code": "INVALID_PHONE_FORMAT"
}
401 Unauthorized
{
"success": false,
"error": "Invalid API key",
"error_code": "INVALID_API_KEY"
}
409 Conflict
{
"success": false,
"error": "Contact already opted in",
"error_code": "ALREADY_OPTED_IN"
}
422 Unprocessable Entity
{
"success": false,
"error": "Missing required field: opt_in_number_id",
"error_code": "MISSING_REQUIRED_FIELD"
}
Phone Number Formatting
Phone numbers must be in E164 format, which includes:
- The
+
prefix - Country code
- No spaces, dashes, or parentheses
Examples
Country | Format | Example |
---|---|---|
United States | +1XXXXXXXXXX | +11234567890 |
United Kingdom | +44XXXXXXXXXX | +447911123456 |
Canada | +1XXXXXXXXXX | +14165550123 |
Australia | +61XXXXXXXXX | +61412345678 |
JavaScript E164 Formatter
function formatToE164(phoneNumber, countryCode = '1') {
// Remove all non-numeric characters
const cleaned = phoneNumber.replace(/\D/g, '');
// Add country code if not present
const withCountryCode = cleaned.startsWith(countryCode)
? cleaned
: countryCode + cleaned;
// Add + prefix
return '+' + withCountryCode;
}
// Examples
formatToE164('(123) 456-7890'); // Returns: +11234567890
formatToE164('07911 123456', '44'); // Returns: +447911123456
Best Practices for Integration
Recommended Opt-In Locations
Integrate the opt-in API at strategic points in your user journey:
-
Booking Process
- After reservation confirmation
- During guest information collection
-
Checkout Flow
- On the payment confirmation page
- After successful transaction
-
Contact Forms
- When users provide their mobile number
- During account registration
-
Profile Management
- In user account settings
- During profile updates
Implementation Checklist
- Store API key securely (environment variables)
- Validate phone numbers before sending
- Capture accurate IP address and user agent
- Handle all possible error responses
- Implement retry logic for network failures
- Log opt-in attempts for compliance
- Display clear opt-in language to users
- Provide opt-out instructions
User Consent Language
Always display clear consent language before opt-in:
<div class="sms-consent">
<input type="checkbox" id="sms-opt-in" name="sms_opt_in">
<label for="sms-opt-in">
I agree to receive SMS notifications about my booking.
Message and data rates may apply. Text STOP to unsubscribe.
</label>
</div>
Getting IP Address and User Agent
Server-Side (Recommended)
Node.js/Express
app.post('/opt-in', (req, res) => {
const ipAddress = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress;
const userAgent = req.headers['user-agent'];
// Process opt-in with these values
});
PHP
$ipAddress = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'];
$userAgent = $_SERVER['HTTP_USER_AGENT'];
Python/Flask
from flask import request
@app.route('/opt-in', methods=['POST'])
def opt_in():
ip_address = request.headers.get('X-Forwarded-For', request.remote_addr)
user_agent = request.headers.get('User-Agent')
# Process opt-in
Client-Side (Less Secure)
// Get user agent
const userAgent = navigator.userAgent;
// Get IP address (requires external service)
fetch('https://api.ipify.org?format=json')
.then(response => response.json())
.then(data => {
const ipAddress = data.ip;
// Use for opt-in
});
Never expose your API key in client-side code. Always make opt-in requests from your backend server.
Testing Your Integration
Test Phone Numbers
SendSquared provides test phone numbers for different scenarios:
Number | Scenario | Response |
---|---|---|
+15555550100 | Successful opt-in | 200 OK |
+15555550101 | Already opted in | 409 Conflict |
+15555550102 | Invalid format | 400 Bad Request |
+15555550103 | Blocked number | 422 Unprocessable |
Using Postman
- Create a new PUT request
- Set URL to
https://app-api.sendsquared.com/v1/public/opt-in/
- Add headers:
Content-Type: application/json
x-api: YOUR_API_KEY_UUID
- Add body (raw JSON):
{
"mobile_phone": "+15555550100",
"ip_address": "192.168.1.1",
"user_agent": "PostmanRuntime/7.28.4",
"opt_in_number_id": "num_test"
}
Webhook Integration
SendSquared can notify your system when opt-in status changes:
Webhook Events
sms.opted_in
- User successfully opted insms.opted_out
- User opted out (via STOP text)sms.opt_in_failed
- Opt-in attempt failed
Webhook Payload Example
{
"event": "sms.opted_in",
"timestamp": "2024-03-28T12:00:00Z",
"data": {
"contact_id": "con_1234567890abcdef",
"phone_number": "+11234567890",
"opt_in_number_id": "num_1234567890abcdef",
"campaign_id": "cam_1234567890abcdef"
}
}
Error Handling Best Practices
Retry Logic
const optInWithRetry = async (userData, maxRetries = 3) => {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await optInUser(userData);
} catch (error) {
lastError = error;
// Don't retry on client errors (4xx)
if (error.response && error.response.status < 500) {
throw error;
}
// Wait before retrying (exponential backoff)
const waitTime = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
}
throw lastError;
};
User-Friendly Error Messages
const getErrorMessage = (errorCode) => {
const messages = {
'INVALID_PHONE_FORMAT': 'Please enter a valid phone number with country code',
'ALREADY_OPTED_IN': 'You are already subscribed to SMS notifications',
'INVALID_API_KEY': 'Configuration error. Please contact support.',
'MISSING_REQUIRED_FIELD': 'Please fill in all required fields',
'BLOCKED_NUMBER': 'This phone number cannot receive SMS messages'
};
return messages[errorCode] || 'An unexpected error occurred. Please try again.';
};
Compliance and Regulations
TCPA Compliance
Ensure your opt-in process complies with TCPA requirements:
- Clear and Conspicuous Disclosure: Clearly state that users are opting in to receive SMS messages
- Express Written Consent: Obtain affirmative consent (checkbox, button click)
- Message Frequency: Disclose how often messages will be sent
- Charges: Include "Message and data rates may apply"
- Opt-Out Instructions: Provide clear unsubscribe instructions
GDPR Considerations
For EU users:
- Obtain explicit consent
- Provide clear information about data processing
- Allow users to withdraw consent easily
- Document consent timestamp and method
Troubleshooting
Common Issues and Solutions
Issue | Possible Cause | Solution |
---|---|---|
401 Unauthorized | Invalid API key | Verify API key in dashboard |
400 Bad Request | Incorrect phone format | Ensure E164 format with + prefix |
Network timeout | Slow connection | Implement retry logic |
No SMS received | Number not verified | Check number status in dashboard |
Duplicate opt-ins | Missing deduplication | Check before sending request |
Debug Logging
const debugOptIn = async (userData) => {
console.log('Opt-in attempt:', {
phone: userData.phone,
timestamp: new Date().toISOString()
});
try {
const result = await optInUser(userData);
console.log('Opt-in successful:', result);
return result;
} catch (error) {
console.error('Opt-in failed:', {
status: error.response?.status,
error: error.response?.data,
message: error.message
});
throw error;
}
};
Support
If you encounter issues with the SMS Opt-In API:
- Check the API Status Page
- Review your API logs in the dashboard
- Contact support at support@sendsquared.com
- Include your request ID and timestamp for faster resolution