API v1 · REST · OTP over WhatsApp

API Documentation

Complete reference for integrating WhatsApp OTP authentication, messaging, devices, and webhooks into your application.

Base URL: https://api.loginwa.com/api/v1 Auth: Bearer <API_KEY>

Quickstart

  1. Log in → create an app → copy the Secret API Key.
  2. Call /auth/start with the user's phone number.
  3. User receives OTP via WhatsApp.
  4. Call /auth/verify with the session_id and OTP code.
  5. Handle the response (verified or error).
# Send OTP
curl -X POST https://api.loginwa.com/api/v1/auth/start \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone":"6281234567890"}'

# Verify OTP
curl -X POST https://api.loginwa.com/api/v1/auth/verify \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"session_id":"SESSION_ID","otp_code":"123456"}'

Authentication

  • Use Authorization: Bearer <API_KEY> header.
  • Alternative: X-Api-Key: <API_KEY> header.
  • Content type: application/json.
  • Rate limit: 120 requests per minute per API key.
  • Never expose your API key in frontend/mobile apps.

For mobile/SPA apps, call LoginWA from your backend server to keep the API key secure.

// Example header
Authorization: Bearer sk_live_abc123xyz789

OTP Endpoints

POST /auth/start

Send OTP to a phone number via WhatsApp.

ParameterTypeRequiredDescription
phonestringYesPhone number with country code (e.g., 6281234567890)
country_codestringNoDefault country code if not in phone
otp_lengthintNoOTP length (default: 6)
message_templatestringNoCustom message with {code} placeholder
metaobjectNoCustom metadata to track

Response

{
  "session_id": "3bbaaf0b-3c11-44a2-8a7e-4edc426c5fcd",
  "expires_in": 300,
  "sent_via_engine": true
}
POST /auth/verify

Verify OTP code entered by user.

ParameterTypeRequiredDescription
session_idstringYesSession ID from /auth/start
otp_codestringYesOTP code entered by user

Response

{
  "status": "verified",
  "phone": "6281234567890",
  "verified_at": "2026-02-05T12:00:00Z",
  "meta": {"user_id": "123"}
}

Messages API

Send custom WhatsApp messages beyond OTP.

POST /messages/send

Send a WhatsApp message to a phone number.

ParameterTypeRequiredDescription
phonestringYesRecipient phone number with country code
messagestringYesMessage content to send
device_idstringNoSpecific device ID to use (optional)
curl -X POST https://api.loginwa.com/api/v1/messages/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone":"6281234567890","message":"Hello from LoginWA!"}'

Devices API

Manage WhatsApp devices connected to your app.

GET /devices

List all devices for your app.

// Response
{
  "devices": [
    {
      "id": "dev_abc123",
      "name": "My WhatsApp",
      "phone": "6281234567890",
      "status": "connected",
      "created_at": "2026-01-15T10:00:00Z"
    }
  ]
}
POST /devices

Create a new device and get QR code.

ParameterTypeRequired
namestringYes
GET /devices/{id}

Get device details including QR code if pending.

POST /devices/{id}/refresh-qr

Refresh QR code for device pairing.

DELETE /devices/{id}

Delete a device and disconnect WhatsApp.

Broadcast API

Send bulk WhatsApp messages to multiple recipients with smart delay, scheduling, and progress tracking.

Key Features

  • Send to up to 10,000 contacts per campaign
  • Variable substitution: Hello {name}!
  • Media support (image, video, document)
  • Smart delay between messages (anti-ban)
  • Schedule campaigns for later
  • Pause/Resume capability
  • Real-time progress tracking

Campaign Status

StatusDescription
draftCampaign created, not started
queuedWaiting to be processed
sendingCurrently sending messages
pausedTemporarily stopped
completedAll messages sent
failedCampaign failed

Broadcast Endpoints

GET /broadcast/campaigns

List all your broadcast campaigns.

POST /broadcast/campaigns

Create a new broadcast campaign.

ParameterTypeRequired
namestringYes
messagestringYes
contactsarrayYes
contacts.*.phonestringYes
contacts.*.namestringNo
contacts.*.variablesobjectNo
media_urlstringNo
media_typestringNo (image/video/document/audio)
delay_secondsintegerNo (default: 5)
schedule_atdatetimeNo
GET /broadcast/campaigns/{id}

Get campaign details and progress.

POST /broadcast/campaigns/{id}/send

Start sending the campaign.

POST /broadcast/campaigns/{id}/pause

Pause an active campaign.

POST /broadcast/campaigns/{id}/resume

Resume a paused campaign.

GET /broadcast/campaigns/{id}/contacts

List contacts with delivery status. Filter with ?status=sent|failed|pending

DELETE /broadcast/campaigns/{id}

Delete a campaign (must be paused first).

Example: Create & Send Campaign

// 1. Create campaign
POST /api/v1/broadcast/campaigns
{
  "name": "Summer Sale 2026",
  "message": "Hello {name}!\n\nSpecial offer for you:\n- 50% discount\n- Free shipping\n\nClick: https://shop.com/promo",
  "contacts": [
    {"phone": "081234567890", "name": "Budi"},
    {"phone": "081234567891", "name": "Ani", "variables": {"city": "Jakarta"}},
    {"phone": "081234567892", "name": "Citra"}
  ],
  "delay_seconds": 5,
  "media_url": "https://example.com/promo.jpg",
  "media_type": "image"
}

// Response
{
  "success": true,
  "data": {
    "id": "camp_abc123xyz",
    "name": "Summer Sale 2026",
    "status": "draft",
    "total_contacts": 3,
    "estimated_duration": "15 seconds"
  }
}

// 2. Start sending
POST /api/v1/broadcast/campaigns/camp_abc123xyz/send

// 3. Check progress
GET /api/v1/broadcast/campaigns/camp_abc123xyz
{
  "success": true,
  "data": {
    "id": "camp_abc123xyz",
    "status": "sending",
    "progress": {
      "total": 3,
      "sent": 2,
      "delivered": 1,
      "failed": 0,
      "pending": 1,
      "percentage": 66.7
    }
  }
}

Webhooks API

Configure webhooks to receive real-time events (incoming messages, status updates, etc.).

Webhook Events

EventDescription
message.receivedNew incoming WhatsApp message
message.sentMessage successfully sent
message.failedMessage delivery failed
device.connectedDevice paired successfully
device.disconnectedDevice disconnected
otp.verifiedOTP verification successful

Webhook Payload Example

{
  "event": "message.received",
  "timestamp": "2026-02-05T12:00:00Z",
  "data": {
    "message_id": "msg_abc123",
    "from": "6281234567890",
    "body": "Hello!",
    "device_id": "dev_xyz"
  }
}

Webhook Endpoints

GET /webhooks

List all webhooks.

POST /webhooks

Create a new webhook.

ParameterTypeRequired
urlstringYes
eventsarrayYes
PUT /webhooks/{id}

Update webhook URL or events.

DELETE /webhooks/{id}

Delete a webhook.

POST /webhooks/{id}/test

Send a test event to the webhook.

POST /webhooks/{id}/regenerate-secret

Generate new signing secret.

Verifying Webhook Signatures

Each webhook includes a signature in the X-Webhook-Signature header. Verify it to ensure authenticity:

// PHP/Laravel example
$signature = $request->header('X-Webhook-Signature');
$payload = $request->getContent();
$secret = 'your_webhook_secret';

$expected = hash_hmac('sha256', $payload, $secret);

if (! hash_equals($expected, $signature)) {
    abort(403, 'Invalid signature');
}

SDKs & Downloads

GitHub Repository

Official SDKs, examples, and Postman collection.

View on GitHub →

SDK Bundle ZIP

Download all SDKs in one package.

Download SDK.zip →

WordPress Plugin

Add WhatsApp OTP to WP login/register.

Download Plugin.zip →

Android SDK

Kotlin SDK with sample app.

View Documentation →

Code Examples

PHP (Guzzle)

$client = new \GuzzleHttp\Client();

// Send OTP
$res = $client->post('https://api.loginwa.com/api/v1/auth/start', [
    'headers' => [
        'Authorization' => 'Bearer ' . env('LOGINWA_API_KEY'),
        'Content-Type' => 'application/json',
    ],
    'json' => [
        'phone' => '6281234567890',
        'otp_length' => 6,
    ],
]);

$data = json_decode($res->getBody(), true);
$sessionId = $data['session_id'];

Node.js (fetch)

const BASE_URL = 'https://api.loginwa.com/api/v1';
const API_KEY = process.env.LOGINWA_API_KEY;

async function sendOtp(phone) {
    const res = await fetch(`${BASE_URL}/auth/start`, {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${API_KEY}`,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({ phone }),
    });
    
    if (!res.ok) throw new Error('Failed to send OTP');
    return res.json();
}

Python (requests)

import requests
import os

BASE_URL = 'https://api.loginwa.com/api/v1'
API_KEY = os.environ.get('LOGINWA_API_KEY')

def send_otp(phone):
    res = requests.post(
        f'{BASE_URL}/auth/start',
        headers={
            'Authorization': f'Bearer {API_KEY}',
            'Content-Type': 'application/json',
        },
        json={'phone': phone}
    )
    res.raise_for_status()
    return res.json()

cURL

# Send OTP
curl -X POST https://api.loginwa.com/api/v1/auth/start \
  -H "Authorization: Bearer $LOGINWA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone":"6281234567890"}'

# Verify OTP
curl -X POST https://api.loginwa.com/api/v1/auth/verify \
  -H "Authorization: Bearer $LOGINWA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"session_id":"xxx","otp_code":"123456"}'

Pricing Plans

Start free, then scale as you grow. All plans include full API access, Widget, SDK, Dashboard, Webhooks, and unlimited projects.

Free
$0/mo
100 messages
Popular
Starter
$19/mo
5,000 messages
Business
$49/mo
15,000 messages
Enterprise
$149/mo
50,000 messages
Start Free → Manage Billing

Rate Limits & Quotas

Limit TypeValue
API Rate Limit120 requests/minute per API key
OTP Length6 digits
OTP TTL5 minutes (300 seconds)
Max Verify Attempts5 attempts
Monthly QuotaBased on your plan (see pricing)

Exceeding rate limits returns 429 Too Many Requests. Exceeding quota returns quota_exceeded error.

Error Codes

CodeHTTPDescription
unauthorized401Invalid or missing API key
invalid_phone422Phone number format invalid
invalid_code422OTP code is incorrect
expired422OTP session has expired
max_attempts422Too many verification attempts
quota_exceeded429Monthly message quota exceeded
rate_limited429Too many requests per minute
no_device503No connected WhatsApp device

Need Help?

Having trouble with integration? We're here to help.

Email: [email protected] Dashboard & Logs View Pricing API Status