Guide Genetics SMS API Documentation

Complete integration guide and API reference

πŸ”Œ API Reference

SMS API Documentation

Authentication

All API requests require authentication via API keys. API keys can be passed in three ways:

1. Authorization Header (Recommended)

bash
curl -H "Authorization: Bearer your_api_key_here" https://api.example.com/api/v1/sms/send

2. X-API-Key Header

bash
curl -H "X-API-Key: your_api_key_here" https://api.example.com/api/v1/sms/send

3. Query Parameter

bash
curl "https://api.example.com/api/v1/sms/send?api_key=your_api_key_here"

SMS API Endpoints

Send SMS Message

Send an SMS message to a recipient.

Endpoint: POST /api/v1/sms/send

Headers:

  • X-API-Key: your_api_key
  • Content-Type: application/json

Request Body:

json
{
  "to": "+1234567890",
  "message": "Hello, this is your SMS message!"
}

Note: FROM_NUMBER is automatically detected from your app's configured phone numbers! No need to specify it.

Success Response (201 Created):

json
{
  "success": true,
  "message": "SMS sent successfully",
  "data": {
    "id": 123,
    "recipient": "+1234567890",
    "message": "Hello, this is your SMS message!",
    "status": "sent",
    "bird_message_id": "msg_abc123",
    "sent_at": "2025-08-07T09:15:30Z"
  }
}

Error Responses:

json
// 400 Bad Request - Missing parameters
{
  "error": "Recipient is required"
}

// 401 Unauthorized - Invalid API key
{
  "error": "Invalid API key"
}

// 422 Unprocessable Entity - SMS service failure
{
  "success": false,
  "message": "Failed to send SMS",
  "error": "Bird API error: Invalid recipient number"
}

Get Message Status

Retrieve the status of a specific SMS message.

Endpoint: GET /api/v1/sms/messages/:id

Headers:

  • X-API-Key: your_api_key

Success Response (200 OK):

json
{
  "id": 123,
  "recipient": "+1234567890",
  "message": "Hello, this is your SMS message!",
  "status": "delivered",
  "bird_message_id": "msg_abc123",
  "sent_at": "2025-08-07T09:15:30Z",
  "delivered_at": "2025-08-07T09:15:45Z",
  "error_message": null,
  "organization_id": 5
}

Error Responses:

json
// 404 Not Found - Message doesn't exist or belongs to different organization
{
  "error": "Message not found"
}

// 401 Unauthorized
{
  "error": "Invalid API key"
}

List Messages

Get a paginated list of SMS messages for your organization.

Endpoint: GET /api/v1/sms/messages

Headers:

  • X-API-Key: your_api_key

Query Parameters:

  • page (optional): Page number (default: 1)
  • per_page (optional): Messages per page (default: 20, max: 100)
  • status (optional): Filter by status (pending, sent, delivered, failed)

Example Request:

bash
curl -H "X-API-Key: your_api_key" \
  "https://api.example.com/api/v1/sms/messages?page=1&per_page=10&status=sent"

Success Response (200 OK):

json
{
  "messages": [
    {
      "id": 123,
      "recipient": "+1234567890",
      "message": "Hello, this is your SMS message!",
      "status": "sent",
      "bird_message_id": "msg_abc123",
      "sent_at": "2025-08-07T09:15:30Z"
    },
    {
      "id": 122,
      "recipient": "+1234567891",
      "message": "Another message",
      "status": "delivered",
      "bird_message_id": "msg_def456",
      "sent_at": "2025-08-07T08:30:15Z"
    }
  ],
  "pagination": {
    "current_page": 1,
    "per_page": 10,
    "total_pages": 5,
    "total_count": 47
  }
}

Message Status Types

  • pending: Message created but not yet sent to Bird API
  • sent: Message successfully sent to Bird API and accepted for delivery
  • delivered: Message confirmed delivered to recipient's device
  • failed: Message delivery failed (carrier rejection, invalid number, etc.)
  • expired: Message expired before delivery (exceeded TTL)

Get Available From Numbers

Get available phone numbers configured for your app. This is optional since FROM_NUMBER is auto-detected during SMS sending.

Endpoint: GET /api/v1/sms/from-numbers

Headers:

  • X-API-Key: your_api_key

Example Request:

bash
curl -H "X-API-Key: your_api_key" \
  "https://api.example.com/api/v1/sms/from-numbers"

Success Response (200 OK):

json
{
  "app_name": "Guide Genetics Family History",
  "phone_numbers": [
    {
      "number": "+18882127786",
      "label": "Primary SMS",
      "channel_id": "ce1de3ed-3fb0-439c-b5e8-94a95aa8e6ae"
    }
  ],
  "default_from_number": "+18882127786"
}

Use Cases:

  • Display which phone number will be used for SMS sending
  • Show users available phone numbers if an app has multiple configured
  • Validate phone number configuration before sending messages

Technical Architecture

Bird SMS Integration

The SMS API integrates with Bird's platform using their connector-based architecture. Understanding this architecture helps explain how phone numbers and channels work:

Architecture Overview:

Your App Configuration
β”œβ”€β”€ Phone Numbers (User-facing)
β”‚   β”œβ”€β”€ Number: "+18882127279"
β”‚   β”œβ”€β”€ Label: "SMS - Secondary πŸ‡ΊπŸ‡Έ"
β”‚   └── Channel ID: "ea07f819-e8cb-56e9-bf5f-c62983fcc624"
β”‚
Bird Workspace
β”œβ”€β”€ Connectors (8381371c-27ba-4e0d-bbea-0ccfb0bf84fa)
β”‚   β”œβ”€β”€ Channel Object
β”‚   β”‚   β”œβ”€β”€ channelId: "ea07f819-e8cb-56e9-bf5f-c62983fcc624"  ← Used for SMS sending
β”‚   β”‚   └── platform: "sms-messagebird"
β”‚   β”œβ”€β”€ Number Object
β”‚   β”‚   β”œβ”€β”€ phoneNumber: "+18882127279"
β”‚   β”‚   └── numberId: "01985b46-c1e4-7285-bef9-897a5c8efbf0"
β”‚   └── Configuration...

Automatic Channel Resolution: The SMS API automatically handles the mapping between your app's phone number configuration and Bird's channel system:

  1. Configuration Storage: Your app stores phone numbers with their associated channel IDs
  2. Smart Resolution: The API detects if stored IDs are connector IDs and automatically resolves to channel IDs
  3. Backward Compatibility: Existing configurations work seamlessly without manual updates
  4. Dynamic Updates: Changing phone numbers updates the channel mapping automatically

SMS Sending Flow:

1. API receives SMS request with your API key
2. System identifies your organization and app
3. App's phone numbers are checked for available channels
4. If connector ID is stored, it's resolved to channel ID
5. SMS sent via Bird API using correct channel ID
6. Response mapped back to your message tracking

This architecture ensures that:

  • Phone number changes don't require new API keys
  • Multiple apps can use different phone numbers seamlessly
  • Bird API changes are handled transparently
  • Debugging information is comprehensive

Error Handling

Common HTTP Status Codes

  • 200 OK: Request successful
  • 201 Created: Resource created successfully
  • 400 Bad Request: Invalid request parameters
  • 401 Unauthorized: Authentication required or invalid
  • 404 Not Found: Resource not found
  • 422 Unprocessable Entity: Request valid but cannot be processed
  • 500 Internal Server Error: Server error

Error Response Format

All errors return JSON in this format:

json
{
  "error": "Error description here"
}

For validation errors:

json
{
  "error": "Recipient (to) and message are required"
}

Rate Limiting

Note: Rate limiting is not currently implemented but is planned for future releases.

Code Examples

Ruby

ruby
require 'httparty'

class SmsClient
  def initialize(api_key, base_url)
    @api_key = api_key
    @base_url = base_url
  end

  def send_sms(recipient, message)
    HTTParty.post(
      "#{@base_url}/api/v1/sms/send",
      headers: { 'X-API-Key' => @api_key, 'Content-Type' => 'application/json' },
      body: { recipient: recipient, message: message }.to_json
    )
  end

  def get_message(id)
    HTTParty.get(
      "#{@base_url}/api/v1/sms/messages/#{id}",
      headers: { 'X-API-Key' => @api_key }
    )
  end

  def list_messages(page: 1, per_page: 20, status: nil)
    params = { page: page, per_page: per_page }
    params[:status] = status if status

    HTTParty.get(
      "#{@base_url}/api/v1/sms/messages",
      headers: { 'X-API-Key' => @api_key },
      query: params
    )
  end
end

# Usage
client = SmsClient.new('your_api_key', 'https://sms.guidegenetics.com')
response = client.send_sms('+1234567890', 'Hello from Ruby!')
puts response.parsed_response

Node.js

javascript
class SmsClient {
  constructor(apiKey, baseUrl) {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
  }

  async sendSms(recipient, message) {
    const response = await fetch(`${this.baseUrl}/api/v1/sms/send`, {
      method: 'POST',
      headers: {
        'X-API-Key': this.apiKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ recipient, message })
    });

    return await response.json();
  }

  async getMessage(id) {
    const response = await fetch(`${this.baseUrl}/api/v1/sms/messages/${id}`, {
      headers: { 'X-API-Key': this.apiKey }
    });

    return await response.json();
  }

  async listMessages({ page = 1, perPage = 20, status = null } = {}) {
    const params = new URLSearchParams({ page, per_page: perPage });
    if (status) params.append('status', status);

    const response = await fetch(`${this.baseUrl}/api/v1/sms/messages?${params}`, {
      headers: { 'X-API-Key': this.apiKey }
    });

    return await response.json();
  }
}

// Usage
const client = new SmsClient('your_api_key', 'https://sms.guidegenetics.com');
const result = await client.sendSms('+1234567890', 'Hello from Node.js!');
console.log(result);

Python

python
import requests

class SmsClient:
    def __init__(self, api_key, base_url):
        self.api_key = api_key
        self.base_url = base_url
        self.headers = {'X-API-Key': api_key}

    def send_sms(self, recipient, message):
        response = requests.post(
            f"{self.base_url}/api/v1/sms/send",
            headers={**self.headers, 'Content-Type': 'application/json'},
            json={'recipient': recipient, 'message': message}
        )
        return response.json()

    def get_message(self, message_id):
        response = requests.get(
            f"{self.base_url}/api/v1/sms/messages/{message_id}",
            headers=self.headers
        )
        return response.json()

    def list_messages(self, page=1, per_page=20, status=None):
        params = {'page': page, 'per_page': per_page}
        if status:
            params['status'] = status

        response = requests.get(
            f"{self.base_url}/api/v1/sms/messages",
            headers=self.headers,
            params=params
        )
        return response.json()

# Usage
client = SmsClient('your_api_key', 'https://sms.guidegenetics.com')
result = client.send_sms('+1234567890', 'Hello from Python!')
print(result)

cURL Examples

Send SMS:

bash
curl -X POST https://sms.guidegenetics.com/api/v1/sms/send \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+1234567890",
    "message": "Hello from cURL!"
  }'

Get message status:

bash
curl -H "X-API-Key: your_api_key_here" \
  https://sms.guidegenetics.com/api/v1/sms/messages/123

List messages:

bash
curl -H "X-API-Key: your_api_key_here" \
  "https://sms.guidegenetics.com/api/v1/sms/messages?page=1&per_page=10&status=sent"

Testing Your Integration

Test with Invalid API Key

bash
curl -X POST https://sms.guidegenetics.com/api/v1/sms/send \
  -H "X-API-Key: invalid_key" \
  -H "Content-Type: application/json" \
  -d '{"to": "+1234567890", "message": "Test"}'

Expected: 401 Unauthorized

Test with Missing Parameters

bash
curl -X POST https://sms.guidegenetics.com/api/v1/sms/send \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"to": "+1234567890"}'

Expected: 400 Bad Request

Test Message Retrieval

bash
curl -H "X-API-Key: your_api_key_here" \
  https://sms.guidegenetics.com/api/v1/sms/messages/999999

Expected: 404 Not Found

Getting Started for Third-Party Developers

1. Obtain API Access

To integrate with the Guide Genetics SMS API:

  1. Contact the administrator to get your organization set up
  2. Receive your API key - a 64-character hex string
  3. Note the base URL - typically https://sms.guidegenetics.com
  4. Review rate limits - Currently no limits, but may be added

2. Quick Integration Test

bash
# Test your API key
curl -H "X-API-Key: your_api_key_here" \
  https://sms.guidegenetics.com/api/v1/sms/messages

# Expected: JSON array of your messages (may be empty)

3. SDK/Libraries

While no official SDKs exist, the API works with standard HTTP libraries:

  • Ruby: HTTParty, Faraday, Net::HTTP
  • Python: requests, urllib3
  • Node.js: fetch, axios, request (deprecated)
  • PHP: Guzzle, curl
  • Java: OkHttp, HttpClient
  • C#: HttpClient
  • Go: net/http package

Integration Patterns

Pattern 1: Simple SMS Notifications

ruby
# Ruby example for basic notifications
class SmsNotifier
  def initialize(api_key, base_url)
    @api_key = api_key
    @base_url = base_url
  end

  def send_notification(recipient, message)
    HTTParty.post(
      "#{@base_url}/api/v1/sms/send",
      headers: { 'X-API-Key' => @api_key, 'Content-Type' => 'application/json' },
      body: { recipient: recipient, message: message }.to_json
    )
  end
end

# Usage
notifier = SmsNotifier.new(ENV['SMS_API_KEY'], ENV['SMS_API_URL'])
response = notifier.send_notification('+1234567890', 'Your order is ready!')

Pattern 2: Bulk SMS with Status Tracking

python
# Python example for bulk messaging with status tracking
import requests
import time

class BulkSmsManager:
    def __init__(self, api_key, base_url):
        self.api_key = api_key
        self.base_url = base_url
        self.headers = {'X-API-Key': api_key, 'Content-Type': 'application/json'}

    def send_bulk_sms(self, recipients, message):
        message_ids = []
        for recipient in recipients:
            response = requests.post(
                f"{self.base_url}/api/v1/sms/send",
                headers=self.headers,
                json={'recipient': recipient, 'message': message}
            )
            if response.status_code == 201:
                data = response.json()
                message_ids.append(data['data']['id'])
            time.sleep(0.1)  # Rate limiting courtesy
        return message_ids

    def check_delivery_status(self, message_ids):
        statuses = {}
        for msg_id in message_ids:
            response = requests.get(
                f"{self.base_url}/api/v1/sms/messages/{msg_id}",
                headers={'X-API-Key': self.api_key}
            )
            if response.status_code == 200:
                data = response.json()
                statuses[msg_id] = {
                    'recipient': data['recipient'],
                    'status': data['status'],
                    'sent_at': data['sent_at']
                }
        return statuses

# Usage
sms_manager = BulkSmsManager(os.environ['SMS_API_KEY'], os.environ['SMS_API_URL'])
recipients = ['+1234567890', '+1234567891', '+1234567892']
message_ids = sms_manager.send_bulk_sms(recipients, 'Bulk notification message')

# Check status later
time.sleep(30)
statuses = sms_manager.check_delivery_status(message_ids)
print(statuses)

Pattern 3: Checking Delivery Status

javascript
// Node.js example for checking delivery status
class SmsStatusChecker {
  constructor(apiKey, baseUrl) {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
  }

  async checkStatus(messageId) {
    const response = await fetch(`${this.baseUrl}/api/v1/sms/messages/${messageId}`, {
      headers: { 'X-API-Key': this.apiKey }
    });

    const data = await response.json();

    // Check detailed status
    if (data.status === 'delivered') {
      console.log(`Delivered at: ${data.delivered_at}`);
    } else if (data.status === 'failed') {
      console.log(`Failed at: ${data.failed_at}`);
      console.log(`Error: ${data.error_message}`);
    }

    return data;
  }
}

// Usage
const checker = new SmsStatusChecker('your_api_key', 'https://sms.guidegenetics.com');
const status = await checker.checkStatus(123);

Error Handling Best Practices

Retry Logic

python
def send_sms_with_retry(api_client, recipient, message, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = api_client.send_sms(recipient, message)
            if response.status_code == 201:
                return response.json()
            elif response.status_code == 429:  # Rate limited
                time.sleep(2 ** attempt)  # Exponential backoff
                continue
            else:
                print(f"API error {response.status_code}: {response.text}")
                break
        except requests.RequestException as e:
            print(f"Network error on attempt {attempt + 1}: {e}")
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)
    return None

Status Code Handling

ruby
class SmsApiError < StandardError; end
class SmsRateLimitError < SmsApiError; end
class SmsAuthError < SmsApiError; end
class SmsValidationError < SmsApiError; end

def handle_sms_response(response)
  case response.code
  when 201
    response.parsed_response
  when 400
    raise SmsValidationError, response.parsed_response['error']
  when 401
    raise SmsAuthError, 'Invalid API key'
  when 422
    raise SmsApiError, response.parsed_response['error']
  when 429
    raise SmsRateLimitError, 'Rate limit exceeded'
  else
    raise SmsApiError, "Unexpected error: #{response.code}"
  end
end

Production Deployment Checklist

Environment Setup

  • [ ] Store API keys in environment variables, not code
  • [ ] Use HTTPS for all API calls
  • [ ] Implement proper error logging
  • [ ] Set up monitoring for API failures
  • [ ] Configure retry logic with exponential backoff
  • [ ] Validate phone numbers before sending

Security Considerations

  • [ ] Never log API keys
  • [ ] Rotate API keys regularly
  • [ ] Use server-side API calls only (never expose keys in client-side code)
  • [ ] Implement IP whitelisting if needed
  • [ ] Monitor for unusual usage patterns

Performance Optimization

  • [ ] Implement connection pooling
  • [ ] Cache message status to reduce API calls
  • [ ] Use asynchronous requests for bulk operations
  • [ ] Implement circuit breaker pattern for reliability
  • [ ] Set appropriate timeouts (recommend 30 seconds)

Frequently Asked Questions

Q: What's the message character limit?

A: While the API doesn't enforce a strict limit, SMS messages over 160 characters may be split into multiple parts by carriers. For best results, keep messages under 160 characters.

Q: Can I schedule messages for future delivery?

A: Not currently supported. Messages are sent immediately when the API is called.

Q: How do I handle international phone numbers?

A: Use E.164 format (e.g., +1234567890). The API accepts various formats but E.164 is recommended.

Q: What happens if Bird API is down?

A: The SMS will be marked as 'failed' in the database. Implement retry logic in your application.

Q: Can I send MMS or rich media?

A: Currently only SMS text messages are supported.

Q: How do I test without sending real SMS?

A: Contact the administrator about setting up a test environment with sandbox credentials.

Troubleshooting Common Issues

Issue: Invalid API key error

Solution:

  • Verify the API key is correct (64-character hex string)
  • Check the X-API-Key header is properly set
  • Ensure no extra spaces or characters in the key

Issue: Message appears sent but isn't received

Solution:

  • Verify the phone number format (use E.164: +1234567890)
  • Check if the number is on a carrier blocklist
  • Review message content for spam-like characteristics

Issue: Message not found when checking status

Solution:

  • Verify you're using the correct message ID from the send response
  • Ensure you're using the same API key that sent the message
  • Messages are scoped to your organization only

Issue: Slow API responses

Solution:

  • Check your network latency to the API server
  • Implement connection reuse in your HTTP client
  • Consider using asynchronous requests

Rate Limiting (Future)

Currently no rate limits are enforced, but they may be added in future versions:

  • Planned limits: 100 requests/minute, 1000 requests/hour
  • Rate limit headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
  • Best practice: Implement exponential backoff when receiving 429 responses

Support

For technical support or questions about the API, please refer to the admin dashboard or contact the development team.

Webhook Configuration

Delivery Status Webhooks

The SMS API supports receiving real-time delivery status updates from Bird via webhooks. This enables automatic tracking of message delivery, failures, and detailed error information.

Webhook Endpoint: POST /api/v1/webhooks/bird-sms-status

Setup Instructions:

  1. Deploy the latest API code to your server
  2. Configure your Bird workspace to send webhooks to: https://your-domain.com/api/v1/webhooks/bird-sms-status
  3. Select all delivery status events in Bird dashboard

Status Updates Received:

  • Message accepted by carrier
  • Message delivered to device
  • Delivery failures with detailed reasons
  • Message expiration

Automatic Updates: When a webhook is received, the following fields are automatically updated:

  • status - Current delivery status
  • delivered_at - Timestamp of successful delivery
  • failed_at - Timestamp of failure
  • error_message - Detailed failure reason including:
    • Regulatory issues (unregistered sender/content)
    • Carrier rejections
    • Invalid numbers
    • Network issues

Common Failure Reasons:

  • Regulatory Compliance: Sending messages to this network requires registration
  • Invalid Number: The destination number is not valid
  • Carrier Rejection: Message blocked by carrier spam filter
  • Network Issues: Temporary network failure

Monitoring Delivery Status

ruby
# Ruby example for monitoring delivery status
class DeliveryMonitor
  def check_message_status(message_id)
    response = HTTParty.get(
      "#{@base_url}/api/v1/sms/messages/#{message_id}",
      headers: { 'X-API-Key' => @api_key }
    )

    data = response.parsed_response

    case data['status']
    when 'delivered'
      puts "βœ“ Delivered at #{data['delivered_at']}"
    when 'failed'
      puts "βœ— Failed: #{data['error_message']}"
      puts "  Failed at: #{data['failed_at']}"
    when 'sent'
      puts "⏳ Sent, awaiting delivery confirmation"
    end

    data
  end
end

Changelog

v1.2.0 (2025-09-26)

  • Major Fix: Resolved Bird SMS connector-to-channel ID mapping issue
  • Enhanced Bird API integration with automatic connector resolution
  • Improved phone number management with dynamic channel selection
  • Added comprehensive debugging and error logging
  • Backward compatibility maintained for existing configurations
  • Enhanced admin interface for Bird channel selection
  • Better error handling and fallback mechanisms

v1.1.0 (2025-08-27)

  • Added webhook support for real-time delivery status
  • Enhanced status tracking with delivery/failure timestamps
  • Detailed error messages for failed deliveries
  • Database indexes for improved performance

v1.0.0 (2025-08-07)

  • Initial API release
  • SMS sending via Bird platform
  • Message status tracking
  • Multi-tenant app β†’ organization structure
  • Token-based authentication
  • Admin dashboard interface

Need Help?

If you have questions or need assistance with the API integration, we're here to help.