Apps · OTP Hub

OTP Hub

Send one-time verification codes to your customers on WhatsApp from Inboxi's verified number, with one API call to send and one to check.

OTP Hub verifies your users' phone numbers on WhatsApp for sign-up, login, password reset, checkout or any sensitive action. You don't need a WhatsApp number, a Meta Business verification or an approved template. Inboxi sends the code from its own dedicated and verified number. It generates the code, stores only a hash of it, handles expiry and limits retries. Your backend makes two calls.

Your backend
POST /api/v1/otp/send with the phone
OTP Hub
Generates a 6-digit code and stores only its hash
WhatsApp
Authentication message with a Copy code button
Your user
Types the code into your app or site
Your backend
POST /api/v1/otp/verify → valid: true

Get started

  1. 1
    Install OTP Hub
    Go to Settings › Apps and click Install on OTP Hub.
  2. 2
    Create an API key
    On the OTP Hub page, open API keys and click Create. Copy the key right away, because it is shown only once. These keys can only use OTP Hub.
  3. 3
    Try it
    In Try it, send a code to your own WhatsApp and verify it, or turn on test mode to get the code back without sending anything.
  4. 4
    Call the API from your server
    Send with POST /api/v1/otp/send and check with POST /api/v1/otp/verify. See the API reference.
Keep the key on your server
Send the key in the X-API-Key header from your backend only. If you put it in a mobile app or website code, anyone can use it to send codes on your account.

A complete sign-in flow

Send the code and keep the returned id with the user's session:

curl -X POST "https://inboxi.app/api/v1/otp/send" \
  -H "X-API-Key: inx_live_your_otp_hub_key" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "201012345678",
    "language": "ar"
  }'

When the user submits the code, check it with the same id:

curl -X POST "https://inboxi.app/api/v1/otp/verify" \
  -H "X-API-Key: inx_live_your_otp_hub_key" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "otp_5f0c2a9e41b7d3c8a6e2f019",
    "code": "482913"
  }'
json
{
  "success": true,
  "data": {
    "valid": true,
    "reason": null,
    "id": "otp_5f0c2a9e41b7d3c8a6e2f019",
    "status": "verified",
    "attempts": 1,
    "attemptsLeft": 4,
    ...
  }
}

Allow the action only when valid is true. When valid is false, the reason field tells you what to show the user. The full list is in the API reference.

Which number sends the codes

When you install OTP Hub you choose the number your users receive codes from. You can change it any time in OTP Hub › Settings › Sending number.

OTP Hub's verified numberYour WhatsApp number
SetupNoneYour number must be connected to the workspace. We create the verification template inboxi_otp_code on it, and Meta must approve it before codes go out
Who paysYour Inboxi wallet, one price per codeMeta bills your WhatsApp account directly. OTP Hub charges nothing
Sender nameOTP HubYour business
Your number's limitsNot usedEach code counts toward your number's daily messaging limit
Other codes from your workspaceSalla login codes and code events (events marked sensitive) are sent from the OTP Hub number too, and billed the same wayKeep going from your number, as before
With the OTP Hub number, if a Salla login code or a code event cannot be sent through OTP Hub (for example the wallet is empty or the daily limit is reached), it is sent from your own number instead, so your customer still gets it. Codes sent through the API are never moved to another number: the request fails with the reason.

Every response and log row says where a code went: from is otp_hub or own_number, and billing is own_number for codes sent from your number.

The message your users receive

Codes go out as a WhatsApp authentication message from the number you chose. Meta fixes this message format: the code, a short security note and a Copy code button. The text cannot be changed and cannot include your company name (from your own number, WhatsApp shows your business as the sender). Tell your users on your verification screen where to look, for example: "We sent a 6-digit code on WhatsApp from OTP Hub."

SettingValue
Code6 digits, random
Expires10 minutes after it is sent
Verify attempts5 per code. After that, request a new code
New codeCancels the previous code for the same number, so only the latest code works
LanguageArabic or English template. Pass language: ar or en

Protection and limits

  • At most 5 codes per phone number per hour and one code every 30 seconds to the same number. Above that you get 429 with a Retry-After header.
  • A daily limit for your whole workspace (1,000 codes by default; change it in Settings). This stops someone from using your sign-up page to send thousands of codes and drain your wallet. Above it you get 429 DAILY_LIMIT_REACHED.
  • Allowed countries (optional): if you only serve some countries, list their calling codes in Settings. Numbers anywhere else are refused with 403 COUNTRY_NOT_ALLOWED and are not charged.
  • OTP Hub API keys allow 120 requests per minute each.
  • Only a hash of each code is stored. Nobody can read the codes, not even Inboxi.
  • Paid codes need enough wallet balance, so nobody can run up charges beyond what you have added.

Test mode

Pass "test": true to /send. No message is sent and nothing is charged. The code comes back in the response (data.code) so automated tests can verify it. Test requests appear in the log marked Test and do not count toward the per-phone limits.

The OTP Hub page

TabWhat it shows
OverviewYour wallet balance, the price per message and roughly how many messages the balance covers, with a Top up button. Below that: this month's codes sent, delivered, verified, verification rate and spend, and today's count against your daily limit.
API keysCreate and revoke OTP Hub keys.
Try itSend a real or test code and verify it, with the raw API response.
LogsEvery code: masked number, status, delivery, billing and price.
SettingsThe sending number (OTP Hub's or yours), daily limit, allowed countries, the low-balance alert, and a link to set up webhooks.

Webhooks

To have Inboxi tell your server what happens to each code, subscribe a URL to these events inSettings › Developer Hub › Webhooks. Deliveries are signed like every Inboxi webhook (see Webhooks). The payload is the request object plus the full phone. It never includes the code.

EventWhen
otp.sentWhatsApp accepted the message
otp.deliveredThe message reached the phone
otp.failedWhatsApp could not deliver it. A paid message is refunded. Includes reason
otp.verifiedThe user entered the right code

Safe retries

If your server might retry a send (for example after a timeout), add an Idempotency-Key header with a value that is unique for that attempt, such as your sign-up id. A repeat with the same key within 24 hours does not send another code. Instead, it returns the first request with "replayed": true, status 200 and the header Idempotent-Replayed: true.

Terms of use

When you install OTP Hub, you agree to these terms. Inboxi can suspend the app for any workspace that breaks them.

  1. OTP Hub is for one-time verification codes only. No marketing, promotions or other content.
  2. Send a code only to a person who asked for it on your site or app, for their own number.
  3. Protect the page that requests codes against automated abuse (for example with a CAPTCHA), and keep a daily limit that fits your traffic.
  4. Codes come from the shared OTP Hub number in Meta's fixed authentication wording, without your company name. Tell your users where to look.
  5. Misuse that harms the number's quality or other customers (spam, bulk sends to numbers that did not ask) leads to suspension without a refund of the affected usage.

Best practices

  • Verify with the id from /send, not the phone. That way a user can never confirm a different request.
  • Show a 30-second countdown before you enable "Resend code".
  • If you get NOT_ON_WHATSAPP, offer the user email or SMS instead.
  • Never log or store the code in your own system. You don't need it, because OTP Hub checks it for you.
  • Use test mode in development and CI. Use a real send only for a final check.