API v1 · REST · OTP over WhatsApp

API Reference

Send and verify OTP codes over WhatsApp. Use this page for quick integration and error handling.

Base URL: https://api.loginwa.com/api/v1 Auth: Authorization: Bearer <SECRET_API_KEY> Try Demo

Quickstart

  1. Log in → create an app → copy the Secret API key.
  2. Call /auth/start with the user phone number.
  3. Call /auth/verify with the returned session_id and the OTP code entered by the user.
curl -X POST https://api.loginwa.com/api/v1/auth/start \
  -H "Authorization: Bearer <SECRET_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"phone":"6281234567890","meta":{"user_id":"123"}}'

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

Authentication

  • Use Authorization: Bearer <SECRET_API_KEY> (or X-Api-Key).
  • Content type: application/json.
  • Each key has its own quota and rate limit (default 120 req/min).
curl -H "Authorization: Bearer $API_KEY" \
     -H "Content-Type: application/json" \
     https://api.loginwa.com/api/v1/auth/start

Use different keys per project to isolate traffic, usage, and logs.

Endpoints

/auth/start

  • POST JSON: {"phone": "628...", "meta": {"user_id": "optional"}}
  • Returns session_id, expires_in, and sent_via_engine flag.
  • Errors: 401 unauthorized, 422 invalid_phone, 429 quota_exceeded.

Success response

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

/auth/verify

  • POST JSON: {"session_id": "...", "otp_code": "123456"}
  • Returns status, phone, and verified_at on success.
  • Errors: 401 unauthorized, 422 invalid_code | expired | blocked.

Success response

{
  "status": "verified",
  "phone": "6281234567890",
  "verified_at": "2025-11-28T12:00:00Z"
}

Rules & limits

  • Rate limit: per API key, default 120 requests / minute.
  • Quota: tied to your plan; exceeding quota returns quota_exceeded.
  • OTP length: 6 digits. Max attempts: 5.
  • TTL: OTP valid for 5 minutes (300 seconds).

Use dashboard logs to monitor success rate, failures, and per-app usage.

Errors & handling

  • 401 unauthorized — missing/invalid API key.
  • 422 invalid_phone — phone format cannot be parsed.
  • 422 invalid_code / expired / max_attempts — verification failed.
  • 429 quota_exceeded — monthly quota finished for this key.

Handle 4xx gracefully and let users retry or request a new OTP after cooldown.

Examples

PHP · Node · cURL

PHP (Guzzle)

$client = new \GuzzleHttp\Client();
$res = $client->post('https://api.loginwa.com/api/v1/auth/start', [
  'headers' => [ 'Authorization' => 'Bearer '.getenv('API_KEY') ],
  'json' => [ 'phone' => '6281234567890' ]
]);
$body = json_decode((string) $res->getBody(), true);

Node (fetch)

const res = await fetch('https://api.loginwa.com/api/v1/auth/start', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ' + process.env.API_KEY,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ phone: '6281234567890' })
});
const data = await res.json();

cURL

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

Support

Need help with integration, higher limits, or production cutover?

Email: [email protected] Dashboard: logs & usage per app View pricing