OTPChat

Preparing your OTP workspace…

Developer API

Send OTPs in one request

Authenticate with your private key, post to /secure-api/v1/send, and deliver WhatsApp or Telegram verification messages. Same endpoint — pick the channel.

Base URL https://otpchat.com
  1. 1
    Get your key

    Sign up, open Dashboard → API keys, copy oc_live_….

  2. 2
    Choose a channel

    whatsapp or telegram + phone (or Telegram chatId).

  3. 3
    Choose how you send

    Platform plan from Wallet, or your own WhatsApp line on My numbers (then pass customNumberId).

Server-side only

Never put API keys in browsers, mobile apps, or public repos.

Iraq-ready numbers

Prefer E.164 without +, e.g. 9647XXXXXXXXX.

Telegram verify once

First Telegram phone send returns a deep link; after the user shares their phone with the bot, later OTPs deliver instantly.

Authentication

Every request needs your private API key as a Bearer token.

HTTP header
Authorization: Bearer oc_live_xxxxxxxxxxxxxxxx

Rotate the key from the dashboard if it was ever exposed. Treat it like a password.

POST /secure-api/v1/send

Send message

Deliver an OTP or free-text message on WhatsApp or Telegram.

FieldTypeRequiredDescription
phoneNumber string yes* E.164 phone digits, e.g. 9647XXXXXXXXX. Aliases: phone, recipient
chatId string no* Telegram only. Private chat id or group/supergroup id (-100…). Prefer this over putting chat ids in phoneNumber
channel string no whatsapp (default), telegram, or sms
text string yes* Full message body. Alias: message
verificationCode string no If set and text omitted → “Your verification code is {code}”
provider string no Alias for channel (compat)
customNumberId string no WhatsApp only. Your custom line id (uwn_…) from My numbers / GET /secure-api/custom-wa. Alias: senderNumberId. Omit to use your default active/trial line (or the shared pool on a platform plan).

* Provide phoneNumber or chatId, and either text/message or verificationCode.

Success · 200

WhatsApp or linked Telegram
{
  "success": true,
  "message": "sent",
  "data": {
    "logId": "msg_…",
    "channel": "whatsapp",
    "costIqd": 0,
    "trial": false,
    "customNumberId": "uwn_…",
    "customRoute": true
  }
}

Channels

Pick a channel per request. Disabled channels return a clear error.

Live

Telegram

Real-person OTP via phone link to @otpchatai_bot, or direct send to a known chatId / group. Same API key and subscription.

Soon

SMS

Listed for pricing. Returns CHANNEL_DISABLED until enabled.

Telegram OTP

Telegram bots cannot cold-message a phone. OTPChat verifies the person once, then delivers codes to their linked chat.

Flow · phone verification

  1. SendPOST /secure-api/v1/send with channel: "telegram" and phoneNumber.
  2. If not linked — HTTP 202 + TELEGRAM_LINK_REQUIRED and a deepLink (https://t.me/…).
  3. Recipient — opens the link → Start → taps Share phone number (must match the OTP phone).
  4. OTP delivers — pending code is sent automatically; later sends go straight through (HTTP 200).

Link required · 202

JSON
{
  "success": false,
  "message": "Telegram identity not linked yet. Open the bot link, tap Start, then share your phone number to receive the OTP.",
  "code": "TELEGRAM_LINK_REQUIRED",
  "data": {
    "logId": "msg_…",
    "channel": "telegram",
    "phone": "9647XXXXXXXXX",
    "pendingId": "tgpend_…",
    "expiresAt": "2026-07-17T01:34:07.063Z",
    "botUsername": "otpchatai_bot",
    "deepLink": "https://t.me/otpchatai_bot?start=…",
    "deepLinkApp": "tg://resolve?domain=otpchatai_bot&start=…",
    "openUrl": "https://otpchat.com/telegram/open?bot=otpchatai_bot&start=…",
    "mobileOpen": {
      "preferred": "_system",
      "fallback": "_blank"
    },
    "instructions": [
      "Open deep link / openUrl",
      "Tap Start",
      "Share phone number",
      "OTP arrives automatically"
    ]
  }
}

Mobile / Cordova: open data.deepLink or data.openUrl with cordova.InAppBrowser.open(url, '_system') (fallback _blank) so the OS can launch the Telegram app. Do not keep t.me inside an in-app WebView.

Send to a group

  1. Add @otpchatai_bot to the group.
  2. In the group, send /chatid — the bot replies with an id like -1001234567890.
  3. Call send with chatId + channel: "telegram".
Group send
curl -X POST "https://otpchat.com/secure-api/v1/send" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "chatId": "-1001234567890",
  "channel": "telegram",
  "verificationCode": "552211"
}'
GET /secure-api/v1/telegram/status

Telegram link status

Check whether a phone is linked, or poll a pending OTP after TELEGRAM_LINK_REQUIRED.

QueryTypeDescription
phoneNumber string E.164 digits — returns linked, chatId, phone
pendingId string From a prior TELEGRAM_LINK_REQUIRED response — returns pending row status
By phone
curl "https://otpchat.com/secure-api/v1/telegram/status?phoneNumber=9647XXXXXXXXX" \
  -H "Authorization: Bearer YOUR_API_KEY"

# {
#   "success": true,
#   "data": {
#     "linked": true,
#     "chatId": "176500130",
#     "phone": "9647XXXXXXXXX"
#   }
# }
By pendingId
curl "https://otpchat.com/secure-api/v1/telegram/status?pendingId=tgpend_…" \
  -H "Authorization: Bearer YOUR_API_KEY"

# data.status: "waiting" | "delivered" | …

Custom WhatsApp numbers

Send OTPs from your own business WhatsApp line. No platform subscription required — each number has a 3‑day trial, then a yearly activation fee.

  • Two products — Platform monthly plans (shared pool + Telegram) or custom WhatsApp lines on My numbers. You can use both.
  • Flow — Add phone → OTPChat creates an upstream sender → scan QR on the connect page → trial (3 days) → pay yearly → admin activates.
  • Yearly price$149 USD / line (USD wallet or Stripe) or 230,000 IQD / line (IQD wallet or Wayl).
  • Phone uniqueness — A number already linked on another account is rejected.
  • StatuslastStatus is the live WhatsApp link: connected or disconnected (phone can be registered while still disconnected until you scan QR).

Auth

  • Management APIs below require a logged-in dashboard session cookie (same as the website).
  • Send API uses your private oc_live_ Bearer key — pass optional customNumberId.

Management endpoints

MethodPathPurpose
GET/secure-api/custom-waList your lines + pricing
POST/secure-api/custom-waCreate a line (provisions upstream key + binds phone)
POST/secure-api/custom-wa/:id/refreshRefresh live connect status
GET/secure-api/custom-wa/:id/connectConnect session (status + QR proxy URL)
GET/secure-api/custom-wa/:id/connect-qrPNG QR image (same live QR as upstream status)
POST/secure-api/custom-wa/:id/pay-yearlyPay yearly activation (wallet / Stripe / Wayl)
GET /secure-api/custom-wa

Session cookie required.

List lines
curl -X GET "https://otpchat.com/secure-api/custom-wa" \
  -H "Cookie: connect.sid=YOUR_SESSION"
Response · 200
{
  "success": true,
  "data": {
    "numbers": [
      {
        "id": "uwn_…",
        "label": "Baghdad store",
        "phone": "9647718593918",
        "whatsappName": "",
        "status": "connecting",
        "lastStatus": "disconnected",
        "trialEndsAt": "2026-07-23T…",
        "paidUntil": null,
        "canConnect": true,
        "canPay": true,
        "isActive": false
      }
    ],
    "pricing": {
      "yearlyUsd": 149,
      "yearlyIqd": 230000,
      "trialDays": 3
    }
  }
}
POST /secure-api/custom-wa
FieldTypeRequiredDescription
phonestringyesE.164 digits without +, e.g. 9647XXXXXXXXX
labelstringnoDisplay name (max 80 chars)
Create line
curl -X POST "https://otpchat.com/secure-api/custom-wa" \
  -H "Content-Type: application/json" \
  -H "Cookie: connect.sid=YOUR_SESSION" \
  -d '{
  "label": "Baghdad store",
  "phone": "9647XXXXXXXXX"
}'

On success, open the connect UI: /dashboard/custom-numbers/{id}/connect and scan the QR until lastStatus is connected.

GET /secure-api/custom-wa/:id/connect
Connect session
{
  "success": true,
  "data": {
    "connected": false,
    "dbStatus": "disconnected",
    "phone": "+9647XXXXXXXXX",
    "whatsappName": "",
    "instructions": "Phone is registered but WhatsApp is disconnected…",
    "qrProxyUrl": "/secure-api/custom-wa/uwn_…/connect-qr",
    "number": { "id": "uwn_…", "status": "connecting", "lastStatus": "disconnected" }
  }
}

GET …/connect-qr returns image/png (no-store). Refresh it every ~25s while waiting for scan.

POST /secure-api/custom-wa/:id/refresh

Re-checks upstream and updates status / lastStatus. Use until lastStatus === "connected".

POST /secure-api/custom-wa/:id/pay-yearly
FieldTypeRequiredDescription
currencystringnoUSD (default) or IQD
providerstringnowallet, stripe (USD), or wayl (IQD). Default: wallet for USD, Wayl for IQD.
Pay from IQD wallet
curl -X POST "https://otpchat.com/secure-api/custom-wa/uwn_…/pay-yearly" \
  -H "Content-Type: application/json" \
  -H "Cookie: connect.sid=YOUR_SESSION" \
  -d '{
  "currency": "IQD",
  "provider": "wallet"
}'

Wallet payment moves the line to pending_approval. Card/Wayl may return data.redirectUrl. After admin approve, status becomes active with paidUntil ≈ +1 year.

Line status values

statusMeaning
setup / connectingCreated — waiting for WhatsApp QR link
trialConnected during the 3‑day trial window
trial_expiredTrial ended — pay yearly to continue
pending_approvalPayment received — awaiting OTPChat review
activeApproved yearly line (check paidUntil)
disconnectedWas active/trial but WhatsApp link dropped — re-scan QR
rejectedPayment rejected by admin

Send via a custom line

POST /secure-api/v1/send · Bearer API key
curl -X POST "https://otpchat.com/secure-api/v1/send" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "phoneNumber": "9647XXXXXXXXX",
  "channel": "whatsapp",
  "verificationCode": "543646",
  "customNumberId": "uwn_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}'

Omit customNumberId to use your default active/trial custom line. Users on a platform plan without custom lines keep using the shared WhatsApp pool. Access is allowed with an active platform trial/plan or an active/trial custom WhatsApp line.

Errors

Handle these codes so your app can retry, open Telegram, or prompt a subscription renewal.

HTTPcodeMeaning
401Missing or invalid API key
202TELEGRAM_LINK_REQUIREDOpen data.deepLink; user must Start + share phone
400TELEGRAM_SEND_FAILEDBot could not deliver (bad chat id, blocked, etc.)
400CHANNEL_DISABLEDChannel off or SMS coming soon
400BAD_CHANNELUnknown channel value
400BAD_RECIPIENTMissing phone / chat id
402TRIAL_API_LIMITTrial API OTP limit reached
402SUBSCRIPTION_REQUIREDNo active platform trial/plan and no active/trial custom WhatsApp line
400Custom WA create/pay errors (e.g. phone already linked, insufficient wallet, QR not ready)

Billing rules

Trial usage plus monthly unlimited OTP subscriptions. WhatsApp and Telegram share the same plan.

  • Platform trial — 3 days, 5 dashboard tests, and 50 API OTP messages on the shared pool.
  • Platform plans — monthly/yearly unlimited on shared WhatsApp + Telegram (Wallet).
  • Custom WhatsApp lines — separate 3‑day trial per number, then $149 / 230,000 IQD per year (My numbers). Admin activates after payment.
  • Active access — successful sends are logged with zero message cost while a platform plan or custom line trial/active period covers the send.
  • Renewal — renew platform plans from Wallet; renew each custom line from My numbers → Pay yearly.

Examples

WhatsApp and Telegram side by side — swap the key and recipient, ship.

WhatsApp OTP
curl -X POST "https://otpchat.com/secure-api/v1/send" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "phoneNumber": "9647XXXXXXXXX",
  "channel": "whatsapp",
  "verificationCode": "543646"
}'
WhatsApp via custom line
curl -X POST "https://otpchat.com/secure-api/v1/send" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "phoneNumber": "9647XXXXXXXXX",
  "channel": "whatsapp",
  "verificationCode": "543646",
  "customNumberId": "uwn_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}'
Telegram OTP
curl -X POST "https://otpchat.com/secure-api/v1/send" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "phoneNumber": "9647XXXXXXXXX",
  "channel": "telegram",
  "verificationCode": "882910"
}'
GET /secure-api/health

Health check

Ping the API without an API key.

Response
GET https://otpchat.com/secure-api/health

{ "success": true, "service": "otpchat", "port": 3018 }
Ready to integrate?

Create an account, grab a key, and send your first WhatsApp or Telegram OTP in minutes.