Send SMS Messages
Send SMS messages to recipients worldwide using YeboLink's messaging API.
Endpoint
POST /api/v1/messages/sendAuthentication
Requires API Key authentication via X-API-Key header.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
to | string | Yes | Recipient phone number in E.164 format (e.g., +1234567890) |
channel | string | Yes | Must be "sms" for SMS messages |
content | object | Yes | Message content object |
content.text | string | Yes | The message text (max 1600 characters) |
metadata | object | No | Custom metadata for tracking (key-value pairs) |
Try It Now
Send SMS Message
Your API key is stored locally and never sent to our servers. Get an API key →
Example Request
curl -X POST https://api.yebolink.com/api/v1/messages/send \
-H "Content-Type: application/json" \
-H "X-API-Key: ybk_live_your_api_key" \
-d '{
"to": "+1234567890",
"channel": "sms",
"content": {
"text": "Hello! Your verification code is 123456. Valid for 10 minutes."
},
"metadata": {
"user_id": "12345",
"purpose": "verification"
}
}'const sendSMS = async (to, message) => {
const response = await fetch('https://api.yebolink.com/api/v1/messages/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.YEBOLINK_API_KEY
},
body: JSON.stringify({
to,
channel: 'sms',
content: {
text: message
},
metadata: {
user_id: '12345',
purpose: 'verification'
}
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
return await response.json();
};
// Usage
try {
const result = await sendSMS(
'+1234567890',
'Hello! Your verification code is 123456. Valid for 10 minutes.'
);
console.log('Message sent:', result.data.message_id);
console.log('Credits used:', result.data.credits_used);
} catch (error) {
console.error('Failed to send SMS:', error.message);
}import os
import requests
def send_sms(to: str, message: str, metadata: dict = None):
"""Send SMS message via YeboLink"""
api_key = os.environ.get('YEBOLINK_API_KEY')
response = requests.post(
'https://api.yebolink.com/api/v1/messages/send',
headers={
'X-API-Key': api_key
},
json={
'to': to,
'channel': 'sms',
'content': {
'text': message
},
'metadata': metadata or {}
}
)
response.raise_for_status()
return response.json()
# Usage
try:
result = send_sms(
to='+1234567890',
message='Hello! Your verification code is 123456. Valid for 10 minutes.',
metadata={
'user_id': '12345',
'purpose': 'verification'
}
)
print(f"Message sent: {result['data']['message_id']}")
print(f"Credits used: {result['data']['credits_used']}")
except requests.exceptions.HTTPError as e:
print(f"Failed to send SMS: {e.response.json()['message']}")<?php
function sendSMS($to, $message, $metadata = []) {
$apiKey = getenv('YEBOLINK_API_KEY');
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://api.yebolink.com/api/v1/messages/send',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"X-API-Key: $apiKey"
],
CURLOPT_POSTFIELDS => json_encode([
'to' => $to,
'channel' => 'sms',
'content' => [
'text' => $message
],
'metadata' => $metadata
])
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($httpCode !== 201) {
$error = json_decode($response, true);
throw new Exception($error['message']);
}
return json_decode($response, true);
}
// Usage
try {
$result = sendSMS(
'+1234567890',
'Hello! Your verification code is 123456. Valid for 10 minutes.',
[
'user_id' => '12345',
'purpose' => 'verification'
]
);
echo "Message sent: " . $result['data']['message_id'] . "\n";
echo "Credits used: " . $result['data']['credits_used'] . "\n";
} catch (Exception $e) {
echo "Failed to send SMS: " . $e->getMessage() . "\n";
}
?>require 'net/http'
require 'json'
require 'uri'
def send_sms(to, message, metadata = {})
api_key = ENV['YEBOLINK_API_KEY']
uri = URI('https://api.yebolink.com/api/v1/messages/send')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.path)
request['Content-Type'] = 'application/json'
request['X-API-Key'] = api_key
request.body = {
to: to,
channel: 'sms',
content: {
text: message
},
metadata: metadata
}.to_json
response = http.request(request)
if response.code.to_i != 201
error = JSON.parse(response.body)
raise "Failed to send SMS: #{error['message']}"
end
JSON.parse(response.body)
end
# Usage
begin
result = send_sms(
'+1234567890',
'Hello! Your verification code is 123456. Valid for 10 minutes.',
{ user_id: '12345', purpose: 'verification' }
)
puts "Message sent: #{result['data']['message_id']}"
puts "Credits used: #{result['data']['credits_used']}"
rescue => e
puts "Error: #{e.message}"
endpackage main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type SMSRequest struct {
To string `json:"to"`
Channel string `json:"channel"`
Content map[string]string `json:"content"`
Metadata map[string]string `json:"metadata,omitempty"`
}
type SMSResponse struct {
Success bool `json:"success"`
Data struct {
MessageID string `json:"message_id"`
Status string `json:"status"`
CreditsUsed int `json:"credits_used"`
CreatedAt string `json:"created_at"`
} `json:"data"`
}
func sendSMS(to, message string, metadata map[string]string) (*SMSResponse, error) {
apiKey := os.Getenv("YEBOLINK_API_KEY")
url := "https://api.yebolink.com/api/v1/messages/send"
payload := SMSRequest{
To: to,
Channel: "sms",
Content: map[string]string{
"text": message,
},
Metadata: metadata,
}
jsonPayload, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonPayload))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-API-Key", apiKey)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != 201 {
return nil, fmt.Errorf("failed to send SMS: %s", string(body))
}
var result SMSResponse
err = json.Unmarshal(body, &result)
if err != nil {
return nil, err
}
return &result, nil
}
func main() {
result, err := sendSMS(
"+1234567890",
"Hello! Your verification code is 123456. Valid for 10 minutes.",
map[string]string{
"user_id": "12345",
"purpose": "verification",
},
)
if err != nil {
fmt.Printf("Failed to send SMS: %v\n", err)
return
}
fmt.Printf("Message sent: %s\n", result.Data.MessageID)
fmt.Printf("Credits used: %d\n", result.Data.CreditsUsed)
}Response
Success Response (201 Created)
{
"success": true,
"data": {
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "queued",
"credits_used": 1,
"created_at": "2025-11-02T12:00:00Z"
}
}Response Fields:
| Field | Type | Description |
|---|---|---|
message_id | string | Unique identifier for the message |
status | string | Current message status (queued, sent, delivered, failed) |
credits_used | number | Number of credits deducted for this message |
created_at | string | ISO 8601 timestamp when message was created |
Message Statuses
Your SMS goes through several statuses:
| Status | Description |
|---|---|
queued | Message is queued for sending |
sent | Message has been sent to the carrier |
delivered | Message was successfully delivered to recipient |
failed | Message delivery failed |
Track status changes using webhooks for real-time updates.
Error Responses
Invalid Phone Number (400)
{
"success": false,
"error": "validation_error",
"message": "Phone number must be in E.164 format (e.g., +1234567890)"
}Insufficient Credits (402)
{
"success": false,
"error": "insufficient_credits",
"message": "Not enough credits. You need 1 credits but have 0."
}Invalid API Key (401)
{
"success": false,
"error": "unauthorized",
"message": "Invalid or missing API key"
}Rate Limit Exceeded (429)
{
"success": false,
"error": "rate_limit_exceeded",
"message": "Too many requests. Please try again later."
}Phone Number Format
E.164 Format Required
Always use E.164 format for phone numbers: +[country code][number]
Valid Examples:
- US:
+1234567890 - UK:
+441234567890 - South Africa:
+27123456789 - Lesotho:
+26878422613
Invalid Examples:
1234567890(missing +)001234567890(incorrect prefix)+1 (234) 567-890(contains spaces/punctuation)
Message Length & Encoding
GSM-7 Encoding (Standard)
Most English text uses GSM-7 encoding:
- Single SMS: 160 characters
- Concatenated SMS: 153 characters per part (7 characters used for concatenation headers)
Example:
- 160 characters = 1 SMS = 1 credit
- 161-306 characters = 2 SMS = 2 credits
- 307-459 characters = 3 SMS = 3 credits
Unicode (UCS-2) Encoding
Messages with emojis or special characters use Unicode:
- Single SMS: 70 characters
- Concatenated SMS: 67 characters per part
Triggers Unicode:
- Emojis (😀, 🎉, ❤️)
- Special characters (Arabic, Chinese, etc.)
- Some punctuation (", ", —)
Best Practices
1. Validate Phone Numbers
Always validate phone numbers before sending:
function isValidE164(phoneNumber) {
const e164Regex = /^\+[1-9]\d{1,14}$/;
return e164Regex.test(phoneNumber);
}
if (!isValidE164(phoneNumber)) {
throw new Error('Invalid phone number format');
}2. Handle Errors Gracefully
Implement retry logic with exponential backoff:
async function sendSMSWithRetry(to, message, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await sendSMS(to, message);
} catch (error) {
if (error.response?.status === 429) {
// Rate limited - wait and retry
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
continue;
}
throw error; // Don't retry other errors
}
}
throw new Error('Max retries exceeded');
}3. Use Metadata for Tracking
Add metadata to track messages in your system:
await sendSMS('+1234567890', 'Your code is 123456', {
user_id: '12345',
purpose: 'verification',
campaign_id: 'onboarding_2025',
environment: 'production'
});4. Monitor Credits
Check your balance regularly:
const checkCredits = async () => {
const response = await fetch('https://api.yebolink.com/api/v1/account', {
headers: {
'Authorization': `Bearer ${jwt_token}`
}
});
const data = await response.json();
console.log('Credits remaining:', data.data.credits_balance);
};5. Use Webhooks
Set up webhooks to receive delivery confirmations:
// Your webhook endpoint
app.post('/webhooks/yebolink', (req, res) => {
const { event, data } = req.body;
if (event === 'message.delivered') {
console.log(`Message ${data.message_id} delivered to ${data.recipient}`);
// Update your database
} else if (event === 'message.failed') {
console.log(`Message ${data.message_id} failed: ${data.error_message}`);
// Handle failure
}
res.status(200).send('OK');
});Use Cases
OTP/Verification Codes
const sendOTP = async (phoneNumber, code) => {
return await sendSMS(
phoneNumber,
`Your verification code is ${code}. Valid for 10 minutes. Do not share this code.`,
{ purpose: 'otp', code }
);
};Order Notifications
const sendOrderConfirmation = async (phoneNumber, orderNumber) => {
return await sendSMS(
phoneNumber,
`Your order #${orderNumber} has been confirmed! Track your delivery at example.com/track/${orderNumber}`,
{ purpose: 'order_confirmation', order_id: orderNumber }
);
};Appointment Reminders
const sendAppointmentReminder = async (phoneNumber, appointmentTime) => {
return await sendSMS(
phoneNumber,
`Reminder: Your appointment is scheduled for ${appointmentTime}. Reply CANCEL to reschedule.`,
{ purpose: 'appointment_reminder' }
);
};Marketing Messages
const sendPromotionalSMS = async (phoneNumber, promoCode) => {
return await sendSMS(
phoneNumber,
`Special offer! Get 20% off with code ${promoCode}. Valid until midnight. Shop now: example.com`,
{ purpose: 'marketing', campaign: 'flash_sale_nov' }
);
};Credits & Pricing
- Cost: 1 credit per SMS (160 characters)
- Concatenated Messages: Each segment costs 1 credit
- Example: A 320-character message = 3 SMS segments = 3 credits
See Billing for credit packages and pricing.
Next Steps
- Send Bulk Messages - Send to multiple recipients
- Retrieve Messages - Check message status
- Setup Webhooks - Get real-time delivery updates
- Try Other Channels - WhatsApp, Email, Voice