Download Markdown Get an API key
Developer Reference

AkidOTP Integration Guide

Everything you need to integrate AkidOTP verification into your app — WhatsApp, Telegram, SMS, and Email. All endpoints, JSON shapes, and limit values reflect the current production API.

Base URL: https://akidotp.com — all paths below are relative to it. Content type: send Content-Type: application/json on every POST.

1. Introduction

AkidOTP is a multi-channel verification API. You verify your own end-users' phone numbers and email addresses through four channels:

2. Authentication

Getting an API key

Create a developer account and generate a key from the AkidOTP dashboard, or use the auth API directly:

Action Method + path Auth Body Success
Sign up POST /api/auth/signup { name, email, password, verificationToken } 201 { success, token, developer }
Log in (step 1) POST /api/auth/login { email, password } 200 { success: true, otpSent: true }
Log in (step 2) POST /api/auth/login { email, code } 200 { success, token, developer }
Create key POST /api/auth/keys Authorization: Bearer <JWT> optional { name } 201 { success, key, apiKey }
List keys GET /api/auth/keys Authorization: Bearer <JWT> 200 { success, keys }

The header to send on every verification request

X-API-Key: YOUR_API_KEY

No other identifier is required. Your account and plan are resolved server-side from the key — you do not send a developer id or app id.

3. Phone channels — WhatsApp / Telegram / SMS

With reverse verification, you do not send a code to the user. You display a code and the user sends it to AkidOTP from their own device; receiving it from their number proves ownership.

START

POST /api/mobileverify/start
X-API-Key: YOUR_API_KEY
Content-Type: application/json

{ "phone": "+218912345678" }

phone (E.164, leading +) is the only required field. There is no request field that limits the response to a single channel — the response returns deep links for every channel your plan allows, and you choose which one to present.

Success 200 — fields present depend on your plan:

{
  "success": true,
  "code": "483920",                 // 6-digit code to show your user
  "socketRoom": "9f3c…d21a",        // session handle; use it to poll
  "expiresIn": 120,                 // seconds

  // If your plan allows WhatsApp:
  "whatsappLink": "https://wa.me/<number>?text=483920",
  "whatsappPersonalLink": "intent://…",   // Android: WhatsApp personal
  "whatsappBusinessLink": "intent://…",   // Android: WhatsApp Business

  // If your plan allows Telegram:
  "telegramLink": "https://t.me/<bot>?start=483920",

  // If your plan allows SMS and SMS quota remains:
  "smsNumber": "<inbound number>",
  "smsBody": "483920",
  "smsLink": "sms:<number>?body=483920",     // Android
  "smsLinkIOS": "sms:<number>&body=483920",  // iOS
  "smsRemaining": 99                          // a number, or "unlimited"
}

The session is short-lived — see expiresIn (seconds) in the response. Missing phone400 { success: false, error: "Phone number required" }.

What the end-user does

Result — (a) Polling

GET /api/mobileverify/status?room=SOCKET_ROOM
X-API-Key: YOUR_API_KEY
State Response
Verified { "verified": true, "phone": "+218912345678" }
Pending { "verified": false, "expired": false, "mismatch": false }
Wrong number replied { "verified": false, "mismatch": true }
Expired / no session { "verified": false, "expired": true }

The verified response includes the confirmed phone. Poll roughly every 3 seconds until verified or expired is true.

Result — (b) Webhook

If you set a webhook URL on your API key (in the dashboard), AkidOTP POSTs a signed notification to it when a reverse verification succeeds.

Payloads — WhatsApp and Telegram share one shape; SMS uses a different shape, so handle both:

// WhatsApp / Telegram
{
  "phone": "+218912345678",
  "requestId": "9f3c…d21a",
  "channel": "whatsapp",
  "status": "verified",
  "verifiedAt": "2026-07-05T12:34:56.000Z"
}

// SMS (verified)
{
  "event": "verification.verified",
  "channel": "sms",
  "phone": "+218912345678",
  "timestamp": "2026-07-05T12:34:56.000Z"
}

Treat success as status === "verified" or event === "verification.verified".

4. Email channel

Forward OTP: AkidOTP emails a 6-digit code and the user types it back. Synchronous — no webhook.

START — send the code

POST /api/verify/email/send
X-API-Key: YOUR_API_KEY
Content-Type: application/json

{ "phone": "+218912345678", "email": "user@example.com" }

Both phone and email are required. Success: 200 { "sent": true, "message": "Verification code sent to user@example.com" }. Missing a field → 400 { "error": "Phone and email are required" }.

VERIFY — check the code

POST /api/verify/email/confirm
X-API-Key: YOUR_API_KEY
Content-Type: application/json

{ "email": "user@example.com", "code": "483920" }

Success returns a token (JWT). The response contains a signed JWT in token, plus the confirmed phone and email:

{ "verified": true, "phone": "+218912345678", "email": "user@example.com", "token": "JWT_TOKEN" }

Failure responses (all 401, distinguished by message):

Case Status Body
Wrong code 401 { "error": "Incorrect code. Please try again." }
Expired code 401 { "error": "Code has expired. Please request a new one." }
No code on file 401 { "error": "No OTP found for this email. Please request a new code." }
Too many attempts 401 { "error": "Too many incorrect attempts. Please request a new code." }
Missing field 400 { "error": "Email and code are required" }

Webhook? No. The email channel is check-only — it never fires a webhook. The result is returned synchronously in the confirm response.

5. Code & limits

Setting Value
OTP format Short numeric code
OTP expiry Short-lived — expires after a few minutes
Max verify attempts (email) Limited — the code is invalidated after a few incorrect tries
Reverse session lifetime Short-lived — see expiresIn in the start response
Resend Brief cooldown between resends
Rate limit (all /api/*) Per-IP — honor HTTP 429 and the retryAfter value

Rate-limit response: { "error": "Too many requests. Please try again later.", "code": "RATE_LIMITED", "retryAfter": <seconds> }.

6. Plans & channel access

Plan WhatsApp Telegram SMS Email Monthly verifications SMS / month
Free 1,000 0
Starter 10,000 50
Growth 100,000 200
Enterprise Unlimited Unlimited

The reverse channels (WhatsApp / Telegram / SMS) require Starter or higher. Email is available on every tier. Your overall monthly verification count is enforced on every call (exceeding it returns 429), and your SMS allowance is enforced when you call POST /api/mobileverify/start. Email usage counts against your overall monthly limit. Upgrades take effect immediately — plan and limits are read live on every request, so a change applies to your next call.

7. Errors

Condition Status Example body
Missing API key 401 { "success": false, "error": "API key required. Include X-API-Key header." }
Invalid / revoked key 401 { "success": false, "error": "Invalid or revoked API key." }
Channel not on plan 403 { "success": false, "error": "WhatsApp verification is not available on the free plan. Upgrade to Starter or higher.", "upgrade": true }
Monthly limit exceeded 429 { "success": false, "error": "Monthly verification limit reached (1,000). Upgrade your plan for more.", "currentUsage": 1000, "limit": 1000, "plan": "free", "upgrade": true }
Rate limit exceeded 429 { "error": "Too many requests. Please try again later.", "code": "RATE_LIMITED", "retryAfter": <seconds> }
Bad input (missing phone) 400 { "success": false, "error": "Phone number required" }
Invalid code (email) 401 { "error": "Incorrect code. Please try again." }
Expired code (email) 401 { "error": "Code has expired. Please request a new one." }

8. Quick-start curl examples

Replace KEY, phone, and email with your values. SOCKET_ROOM and the code come from the START response.

WhatsApp

# 1) Start
curl -X POST https://akidotp.com/api/mobileverify/start \
  -H "X-API-Key: KEY" -H "Content-Type: application/json" \
  -d '{"phone":"+218912345678"}'

# 2) Show the code and open whatsappLink; the user SENDS it to AkidOTP on WhatsApp.

# 3) Poll for the result
curl "https://akidotp.com/api/mobileverify/status?room=SOCKET_ROOM" \
  -H "X-API-Key: KEY"
# → { "verified": true, "phone": "+218912345678" }

Telegram

curl -X POST https://akidotp.com/api/mobileverify/start \
  -H "X-API-Key: KEY" -H "Content-Type: application/json" \
  -d '{"phone":"+218912345678"}'
# Response includes telegramLink. User opens it, taps Start, shares contact. Then poll:
curl "https://akidotp.com/api/mobileverify/status?room=SOCKET_ROOM" -H "X-API-Key: KEY"

SMS

curl -X POST https://akidotp.com/api/mobileverify/start \
  -H "X-API-Key: KEY" -H "Content-Type: application/json" \
  -d '{"phone":"+218912345678"}'
# Response includes smsNumber, smsBody, smsLink / smsLinkIOS. User sends the SMS. Then poll:
curl "https://akidotp.com/api/mobileverify/status?room=SOCKET_ROOM" -H "X-API-Key: KEY"

Email

# 1) Send the code
curl -X POST https://akidotp.com/api/verify/email/send \
  -H "X-API-Key: KEY" -H "Content-Type: application/json" \
  -d '{"phone":"+218912345678","email":"user@example.com"}'
# → { "sent": true, "message": "Verification code sent to user@example.com" }

# 2) Confirm the emailed 6-digit code
curl -X POST https://akidotp.com/api/verify/email/confirm \
  -H "X-API-Key: KEY" -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","code":"483920"}'
# → { "verified": true, "phone": "…", "email": "…", "token": "JWT_TOKEN" }
Ready to build? Create a free account and get your API key — 1,000 verifications/month, no credit card. Questions? See the FAQ or contact us.