Documentation Index
Fetch the complete documentation index at: https://mintlify.com/DaggieBlanqx/Google-Africastalking-AI-Course/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The SMS utilities module provides functions to send SMS messages using Africa’s Talking API. It supports both two-way SMS (with shortcode) and bulk SMS sending.
Environment Variables
Your Africa’s Talking username (e.g., sandbox or your production username)
Your Africa’s Talking API key
Your shortcode for two-way SMS (optional, only needed for 2-way messaging)
send_twoway_sms
Send a two-way SMS using a shortcode. Two-way SMS allows recipients to reply, and those replies are sent to your callback URL.
def send_twoway_sms(
message: str,
recipient: str,
shortcode: Optional[str] = AT_SHORTCODE
) -> Dict[str, Any]
Parameters
The SMS message to send. Cannot be empty.
The recipient’s phone number in international format (e.g., +254712345678)
The shortcode to use for sending. Defaults to AT_SHORTCODE from environment variables.
Returns
Africa’s Talking API response containing message status and details{
"SMSMessageData": {
"Message": "Sent to 1/1 Total Cost: KES 0.8000",
"Recipients": [{
"statusCode": 101,
"number": "+254712345678",
"status": "Success",
"cost": "KES 0.8000",
"messageId": "ATXid_..."
}]
}
}
Usage Example
from utils.sms_utils import send_twoway_sms
# Send a two-way SMS
response = send_twoway_sms(
message="Hello! Reply to this message.",
recipient="+254712345678"
)
print(f"Message sent: {response}")
# ✅ SMS sent to +254712345678: Hello! Reply to this message.
# Use custom shortcode
response = send_twoway_sms(
message="Custom shortcode message",
recipient="+254712345678",
shortcode="12345"
)
Error Handling
try:
response = send_twoway_sms("Test message", "+254712345678")
except ValueError as e:
print(f"Validation error: {e}")
# Possible errors:
# - Invalid recipient format: must start with '+'
# - Message cannot be empty
except Exception as e:
print(f"SMS sending failed: {e}")
# Network errors, API errors, etc.
Route Handler Integration
Example from routes/sms.py:34-52:
@sms_bp.route("/invoke-twoway-sms", methods=["GET"])
def invoke_twoway_sms():
phone = "+" + request.args.get("phone", "").strip()
message = request.args.get("message", "Hello from Africa's Talking!").strip()
if not phone:
return {"error": "Missing 'phone' query parameter"}, 400
try:
response = send_twoway_sms(message, phone)
return {"message": f"SMS sent to {phone}", "response": response}
except Exception as e:
return {"error": str(e)}, 500
Two-Way SMS Callback
Handle replies from recipients (from routes/sms.py:55-77):
@sms_bp.route("/twoway", methods=["POST"])
def twoway_callback():
linkId = request.values.get("linkId")
text = request.values.get("text")
sender = request.values.get("from")
print(f"Received 2-way SMS from {sender}: {text}")
# Respond to the sender
send_twoway_sms(
message=f'This is a response to: "{text}"',
recipient=sender
)
return "GOOD", 200
send_bulk_sms
Send an SMS to multiple recipients simultaneously. Useful for notifications, marketing, or alerts.
def send_bulk_sms(message: str, recipients: List[str]) -> Dict[str, Any]
Parameters
The SMS message to send to all recipients
List of phone numbers in international format (e.g., ["+254712345678", "+254723456789"])
Returns
Africa’s Talking API response containing delivery status for each recipient{
"SMSMessageData": {
"Message": "Sent to 2/2 Total Cost: KES 1.6000",
"Recipients": [
{
"statusCode": 101,
"number": "+254712345678",
"status": "Success",
"cost": "KES 0.8000",
"messageId": "ATXid_..."
},
{
"statusCode": 101,
"number": "+254723456789",
"status": "Success",
"cost": "KES 0.8000",
"messageId": "ATXid_..."
}
]
}
}
Usage Example
from utils.sms_utils import send_bulk_sms
# Send to multiple recipients
recipients = [
"+254712345678",
"+254723456789",
"+254734567890"
]
response = send_bulk_sms(
message="Important announcement: System maintenance at 2 AM.",
recipients=recipients
)
print(response)
# 📩 Bulk SMS sent to 3 recipients
# Check individual delivery status
for recipient in response["SMSMessageData"]["Recipients"]:
print(f"{recipient['number']}: {recipient['status']}")
Route Handler Integration
Example from routes/sms.py:13-31:
@sms_bp.route("/invoke-bulk-sms", methods=["GET"])
def invoke_bulk_sms():
phone = "+" + request.args.get("phone", "").strip()
message = request.args.get("message", "Hello from Africa's Talking!").strip()
if not phone:
return {"error": "Missing 'phone' query parameter"}, 400
try:
# Send to single recipient using bulk SMS
response = send_bulk_sms(message, [phone])
return {"message": f"SMS sent to {phone}", "response": response}
except Exception as e:
return {"error": str(e)}, 500
Error Handling
try:
response = send_bulk_sms("Notification", recipients)
except ValueError as e:
print(f"Validation error: {e}")
# Error: Recipients list cannot be empty
except Exception as e:
print(f"Bulk SMS failed: {e}")
# Network errors, API errors, invalid API keys, etc.
Both functions validate phone numbers to ensure they’re in international format:
# ✅ Valid formats
"+254712345678" # Kenya
"+234803456789" # Nigeria
"+233240123456" # Ghana
"+256701234567" # Uganda
# ❌ Invalid formats (will raise ValueError)
"0712345678" # Missing country code
"254712345678" # Missing '+' prefix
"712345678" # Missing country code and '+'
""
Validation Example
def validate_and_send(message: str, phone: str):
# Ensure phone starts with '+'
if not phone.startswith("+"):
phone = "+" + phone
try:
return send_twoway_sms(message, phone)
except ValueError as e:
print(f"Invalid phone format: {e}")
return None
Delivery Reports
Monitor SMS delivery status through webhooks (from routes/sms.py:80-100):
@sms_bp.route("/delivery-reports", methods=["POST"])
def sms_delivery_report():
"""
Handle SMS delivery reports from Africa's Talking.
Payload example:
{
"id": "ATXid_...",
"status": "Success",
"phoneNumber": "+254712345678",
"networkCode": "63902",
"failureReason": "",
"retryCount": "0"
}
"""
payload = {key: request.values.get(key) for key in request.values.keys()}
print("📩 SMS Delivery Report Received:")
for key, value in payload.items():
print(f" {key}: {value}")
# Process delivery status
if payload.get("status") != "Success":
print(f"⚠️ Delivery failed: {payload.get('failureReason')}")
return "OK", 200
Status Codes
Common Africa’s Talking SMS status codes:
| Code | Status | Description |
|---|
| 101 | Success | Message sent successfully |
| 102 | Queued | Message queued for delivery |
| 401 | RiskHold | Message flagged for review |
| 402 | InvalidSenderId | Invalid sender ID |
| 403 | InvalidPhoneNumber | Invalid recipient number |
| 404 | UnsupportedNumberType | Number type not supported |
| 405 | InsufficientBalance | Insufficient account balance |
| 406 | UserInBlackList | Recipient opted out |
| 407 | CouldNotRoute | Unable to route message |
| 500 | InternalServerError | Africa’s Talking server error |
Handling Status Codes
response = send_bulk_sms("Test", ["+254712345678"])
for recipient in response["SMSMessageData"]["Recipients"]:
status_code = recipient["statusCode"]
if status_code == 101:
print(f"✅ Success: {recipient['number']}")
elif status_code == 102:
print(f"⏳ Queued: {recipient['number']}")
elif status_code == 405:
print(f"❌ Insufficient balance")
else:
print(f"⚠️ Error {status_code}: {recipient['status']}")
Best Practices
1. Use Bulk SMS for Multiple Recipients
# ❌ Don't do this
for phone in phones:
send_twoway_sms(message, phone) # Multiple API calls
# ✅ Do this instead
send_bulk_sms(message, phones) # Single API call
2. Implement Error Retry Logic
import time
def send_sms_with_retry(message: str, recipients: list, retries: int = 3):
for attempt in range(retries):
try:
return send_bulk_sms(message, recipients)
except Exception as e:
if attempt < retries - 1:
time.sleep(2 ** attempt) # Exponential backoff
continue
raise
def safe_send_sms(message: str, recipients: list):
# Validate message
if not message or not message.strip():
raise ValueError("Message cannot be empty")
# Validate and format phone numbers
valid_recipients = []
for phone in recipients:
if not phone.startswith("+"):
phone = "+" + phone
if len(phone) >= 10: # Minimum valid length
valid_recipients.append(phone)
if not valid_recipients:
raise ValueError("No valid recipients")
return send_bulk_sms(message, valid_recipients)
4. Monitor Delivery Reports
Always implement delivery report webhooks to track message status:
# Set webhook URL in Africa's Talking dashboard:
# https://your-domain.com/sms/delivery-reports
5. Handle Opt-Outs
Implement opt-out handling (from routes/sms.py:103-119):
@sms_bp.route("/opt-out", methods=["POST"])
def sms_opt_out():
sender_id = request.values.get("senderId")
phone_number = request.values.get("phoneNumber")
# Add to opt-out list in your database
print(f"🚫 User {phone_number} opted out from {sender_id}")
return "OK", 200