Skip to main content

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
info

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:

FieldTypeRequiredDescription
mobile_phonestringYesMobile phone number in E164 format (e.g., +11234567890)
ip_addressstringYesUser's IP address for compliance tracking
user_agentstringYesUser's browser user agent string
opt_in_number_idstringYesThe 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

CountryFormatExample
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

Integrate the opt-in API at strategic points in your user journey:

  1. Booking Process

    • After reservation confirmation
    • During guest information collection
  2. Checkout Flow

    • On the payment confirmation page
    • After successful transaction
  3. Contact Forms

    • When users provide their mobile number
    • During account registration
  4. 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

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

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
});
Security Note

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:

NumberScenarioResponse
+15555550100Successful opt-in200 OK
+15555550101Already opted in409 Conflict
+15555550102Invalid format400 Bad Request
+15555550103Blocked number422 Unprocessable

Using Postman

  1. Create a new PUT request
  2. Set URL to https://app-api.sendsquared.com/v1/public/opt-in/
  3. Add headers:
    • Content-Type: application/json
    • x-api: YOUR_API_KEY_UUID
  4. 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 in
  • sms.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:

  1. Clear and Conspicuous Disclosure: Clearly state that users are opting in to receive SMS messages
  2. Express Written Consent: Obtain affirmative consent (checkbox, button click)
  3. Message Frequency: Disclose how often messages will be sent
  4. Charges: Include "Message and data rates may apply"
  5. 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

IssuePossible CauseSolution
401 UnauthorizedInvalid API keyVerify API key in dashboard
400 Bad RequestIncorrect phone formatEnsure E164 format with + prefix
Network timeoutSlow connectionImplement retry logic
No SMS receivedNumber not verifiedCheck number status in dashboard
Duplicate opt-insMissing deduplicationCheck 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:

  1. Check the API Status Page
  2. Review your API logs in the dashboard
  3. Contact support at support@sendsquared.com
  4. Include your request ID and timestamp for faster resolution