Blog/WhatsApp API

The Complete WhatsApp API Developer Checklist: Build, Test, and Scale

The definitive WhatsApp API developer checklist for 2026. Cover setup, authentication, message sending, webhooks, error handling, rate limits, and production scaling with Rapiwa API.

by Rabbi
The Complete WhatsApp API Developer Checklist: Build, Test, and Scale

This is the complete WhatsApp API developer checklist for 2026 — covering everything from initial account setup to production scaling. Use this as your reference guide when integrating Rapiwa's WhatsApp API ($5/month, no per-message fees) into any application. Check off each item before going live to avoid common issues.

What Is a WhatsApp API Developer Checklist?

A WhatsApp API developer checklist is a structured list of technical requirements, best practices, and tests to complete before deploying WhatsApp API integrations to production. It covers authentication, message sending, webhook handling, error management, rate limiting, and scaling. This checklist applies to Rapiwa and most unofficial WhatsApp REST APIs.


Phase 1: Account Setup and Configuration

Account Basics

  • Created Rapiwa account at rapiwa.com (3-day free trial available)
  • Verified email and completed account setup
  • Selected the appropriate plan (Starter/Professional/Enterprise)
  • Enabled 2FA on the Rapiwa account for security

WhatsApp Number Connection

  • Connected WhatsApp number via QR code scan in Dashboard → Devices
  • Verified the number shows "Connected" status in dashboard
  • The connected number is active and has WhatsApp installed on a phone
  • Tested receiving a message on the connected number before proceeding
  • Documented which phone/SIM is connected (for reconnection if needed)

API Credentials

  • Generated API key in Dashboard → API Keys
  • Stored API key in environment variables (never hardcoded)
  • Verified API key works with a test cURL:
curl -X POST https://app.rapiwa.com/send-message \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"number": "YOUR_TEST_PHONE", "message": "API key test!"}'

Expected: {"status": "success", "messageId": "..."}

  • Configured API key in .env or secrets manager:
RAPIWA_API_KEY=your_key_here
RAPIWA_BASE_URL=https://app.rapiwa.com

Phase 2: Sending Messages

Basic Text Messages

  • Confirmed API endpoint: POST https://app.rapiwa.com/send-message
  • Verified phone number format: international without + (8801234567890, not +880-1234-567890)
  • Tested with minimum required fields:
{
  "number": "8801234567890",
  "message": "Hello from [Your App]!"
}
  • Confirmed success response format:
{
  "status": "success",
  "messageId": "msg_abc123",
  "timestamp": "2026-07-18T10:30:00Z"
}

Phone Number Handling

  • Built phone number sanitization function:
def clean_phone(phone: str) -> str:
    """Remove all non-digit characters from phone number."""
    return ''.join(c for c in phone if c.isdigit())

# Test cases
assert clean_phone('+880-1234-567890') == '8801234567890'
assert clean_phone('+44 7700 900123') == '447700900123'
assert clean_phone('+1 (212) 555-1234') == '12125551234'
  • Tested with phone numbers from your primary target countries
  • Added validation to reject obviously invalid numbers (less than 7 digits, more than 15 digits)

Message Formatting

  • Tested WhatsApp text formatting:
    • *bold* renders correctly
    • _italic_ renders correctly
    • ~strikethrough~ renders correctly
    • \n creates line breaks
  • Ensured message length is under 4,096 characters
  • Tested multi-line messages:
message = (
    "Line 1\n"
    "Line 2\n\n"
    "*Bold section*\n"
    "_Italic note_"
)

Media Messages

  • Tested image sending via /send-image endpoint:
    • Image URL is publicly accessible HTTPS
    • Image is under 5MB
    • Supported format: JPEG, PNG, WebP
  • Tested document sending via /send-document:
    • Document URL is public HTTPS
    • Document is under 100MB
    • Filename is provided in the payload

Phase 3: Webhook Configuration

Webhook Setup

  • Server has a publicly accessible HTTPS URL for webhook
  • Webhook endpoint returns 200 OK within 10 seconds
  • Registered webhook URL in Rapiwa Dashboard → Webhooks
  • Selected relevant events: message.received
  • Tested webhook receipt with a manual trigger

Webhook Handler

  • Implemented webhook handler that:
    • Parses the JSON payload
    • Responds with 200 OK immediately
    • Processes business logic asynchronously (background task/queue)
@app.route('/webhook/whatsapp', methods=['POST'])
def handle_webhook():
    payload = request.get_json()
    
    # Return 200 immediately
    response = jsonify({'status': 'ok'})
    
    # Process in background
    if payload.get('event') == 'message.received':
        process_message_async(payload['data'])
    
    return response, 200
  • Implemented idempotency check (prevent double-processing the same messageId):
def process_message_async(data: dict) -> None:
    message_id = data.get('messageId')
    
    if redis.exists(f'processed:{message_id}'):
        return  # Already processed
    
    redis.setex(f'processed:{message_id}', 3600, '1')  # Mark as processed for 1 hour
    # ... process the message
  • Handles all incoming message types:
    • text — parse and respond to text messages
    • image — handle image attachments
    • document — handle document attachments
    • Other types — graceful fallback

Phase 4: Error Handling

HTTP Error Codes

  • Handles 401 Unauthorized (invalid API key):
    • Log the error
    • Alert the engineering team (not silently fail)
  • Handles 400 Bad Request (invalid payload):
    • Log the full request and response
    • Validate input before sending to avoid 400s
  • Handles 429 Too Many Requests (rate limit):
    • Implement exponential backoff: wait 1s, 2s, 4s, 8s...
    • Queue messages during rate limit windows
  • Handles 5xx Server Errors (Rapiwa API issues):
    • Retry up to 3 times with backoff
    • Alert on repeated 5xx errors

Error Handling Implementation

import time
import requests

def send_with_retry(phone: str, message: str, api_key: str, max_retries: int = 3) -> dict:
    """Send WhatsApp message with automatic retry on failure."""
    last_error = None
    
    for attempt in range(max_retries):
        try:
            response = requests.post(
                'https://app.rapiwa.com/send-message',
                headers={'Authorization': f'Bearer {api_key}'},
                json={'number': phone, 'message': message},
                timeout=15
            )
            
            if response.status_code == 401:
                raise PermissionError("Invalid API key — check RAPIWA_API_KEY env var")
            
            if response.status_code == 429:
                wait_time = 2 ** attempt  # Exponential backoff
                time.sleep(wait_time)
                continue
            
            if response.status_code >= 500:
                wait_time = 2 ** attempt
                time.sleep(wait_time)
                continue
            
            return response.json()
            
        except requests.Timeout:
            last_error = "Request timed out"
            time.sleep(2 ** attempt)
            continue
        
        except requests.ConnectionError:
            last_error = "Connection error — check network"
            time.sleep(5)
            continue
    
    raise RuntimeError(f"Failed after {max_retries} attempts: {last_error}")

Phase 5: Rate Limiting and Bulk Sending

Rate Limit Compliance

  • Added 1-second delay between messages for bulk sends
  • Implemented a message queue for high-volume sends (Celery, Bull, Sidekiq):
# Celery task for rate-limited bulk send
@celery.task(rate_limit='60/m')  # 60 per minute max
def send_whatsapp_task(phone: str, message: str) -> dict:
    return send_with_retry(phone, message, os.environ['RAPIWA_API_KEY'])
  • Implemented a daily volume monitor (alert if unexpected spike):
def check_daily_volume():
    """Alert if daily message count exceeds expected threshold."""
    count = redis.incr('daily_message_count')
    if count > EXPECTED_DAILY_MAX * 1.5:
        send_alert(f"⚠️ Unusual message volume: {count} messages sent today")

Phase 6: Security

Security Checklist

  • API key stored in environment variable (not in source code)
  • API key NOT committed to git:
# .gitignore
.env
config/secrets.yml
*.key
  • API key NOT logged in application logs
  • Phone numbers NOT logged in plaintext (mask in logs: 880***1234)
  • Webhook endpoint validates that requests come from expected source
  • Input validation on all phone numbers before calling API
  • Implemented rate limiting on your own webhook endpoint to prevent abuse

Phase 7: Testing

Pre-Production Test Suite

  • Unit tests for phone number sanitization
  • Unit tests for message template building
  • Integration test: send a real message to your own test number
  • Integration test: receive a webhook from a test message
  • Load test: send 100 messages in sequence (verify no errors)
  • Error test: send with invalid API key → verify 401 is handled
  • Error test: send to invalid phone number → verify 400 is handled
# test_rapiwa.py
import pytest
from unittest.mock import patch, Mock

def test_clean_phone():
    assert clean_phone('+880-1234-567890') == '8801234567890'
    assert clean_phone('+44 7700 900123') == '447700900123'

def test_send_message_success():
    with patch('requests.post') as mock_post:
        mock_post.return_value = Mock(
            status_code=200,
            json=lambda: {'status': 'success', 'messageId': 'msg_test_123'}
        )
        result = send_with_retry('8801234567890', 'Test', 'fake_key')
        assert result['status'] == 'success'

def test_send_message_401():
    with patch('requests.post') as mock_post:
        mock_post.return_value = Mock(status_code=401, json=lambda: {})
        with pytest.raises(PermissionError):
            send_with_retry('8801234567890', 'Test', 'invalid_key', max_retries=1)

Phase 8: Monitoring and Production

Production Monitoring

  • Set up monitoring for API response times (alert if >5 seconds)
  • Set up monitoring for error rates (alert if >5% failure rate)
  • Set up alerting for API key expiry
  • Set up dashboard for messages sent/received per day
  • Created runbook for common issues:
ISSUE: Messages not sending (401)
ACTION: Log in to Rapiwa dashboard → API Keys → regenerate key → update env var

ISSUE: WhatsApp number disconnected
ACTION: Log in to Rapiwa dashboard → Devices → scan QR code again on the phone

ISSUE: Webhook not receiving
ACTION: Check n8n/server is running → verify URL in Rapiwa webhook settings → send test message

Go-Live Checklist

  • All tests passing
  • Error handling tested for all HTTP status codes
  • Rate limiting implemented
  • Phone number validation in place
  • Monitoring and alerts configured
  • Runbook written and team trained
  • API key secured in production secrets manager
  • Webhook endpoint behind HTTPS
  • Sent 10 test messages across different message types
  • Received and processed at least 5 test webhook events

Quick Reference: Rapiwa API at a Glance

ItemValue
Base URLhttps://app.rapiwa.com
Send textPOST /send-message
Send imagePOST /send-image
Send documentPOST /send-document
Auth headerAuthorization: Bearer YOUR_API_KEY
Phone formatInternational without + (e.g., 8801234567890)
Max message length4,096 characters
Success status{"status": "success", "messageId": "..."}
Pricing$5/month flat, no per-message fees
Free trial3 days, no credit card
Sourceforge rating5.0/5

FAQ

What is the most common mistake developers make with the Rapiwa API? Phone number format. Always use international format without + or spaces: 8801234567890, not +880 1234 567890. Strip all non-digit characters before sending.

How do I handle the WhatsApp number disconnecting in production? Build an uptime monitor that sends a test message every 6 hours. If it fails with anything other than a business error, alert the team to re-scan the QR code in the Rapiwa dashboard.

Does Rapiwa have an API version I need to specify? Rapiwa's API doesn't require explicit versioning in the URL. The current API version is stable — changes are backwards-compatible.

What is the best way to store the Rapiwa API key in production? Use your cloud provider's secrets manager: AWS Secrets Manager, Google Cloud Secret Manager, HashiCorp Vault, or at minimum environment variables in a .env file not committed to git.

Can I use the same Rapiwa API key across multiple servers? Yes. The API key is not tied to a specific IP address. You can use it from multiple servers simultaneously — useful for high-availability setups with multiple application instances.