# LibOTP API Gateway — Developer Reference

> Temporary virtual SMS numbers for OTP. Prepaid wallet. India-first.
> This document is written so humans and AI coding assistants can implement a full client from it.

**Canonical docs:** https://libotp.online/docs/api  
**Machine-readable copy:** https://libotp.online/docs/api.md  
**Get an API key:** https://libotp.online/app/api (sign in required)

---

## Base URL

```
https://libotp.online/api/handler_api.php
```

Alias (same handler):

```
https://libotp.online/stubs/handler_api.php
```

All requests are **HTTP GET** with query parameters. Responses are **plain text** (not JSON), unless noted.

---

## Authentication

Every request must include your personal API key:

| Param | Where | Required |
|-------|--------|----------|
| `api_key` | query string | yes |

Also accepted: `apiKey`, `key`, or header `X-Api-Key`.

Create or rotate the key at https://libotp.online/app/api after signing in. Keep the key secret. It spends **your LibOTP wallet balance**.

Invalid key → plain text `BAD_KEY` (HTTP 401).

---

## Actions

Pass `action=<name>`.

### 1. getBalance

Returns wallet balance.

```
GET /api/handler_api.php?api_key=YOUR_KEY&action=getBalance
```

Success:

```
ACCESS_BALANCE:123.45
```

### 2. getCountries

Lists allowed country codes (LibOTP opaque codes — not numeric provider ids).

```
GET /api/handler_api.php?api_key=YOUR_KEY&action=getCountries
```

Response (one per line):

```
IN:India
US:United States
GB:United Kingdom
...
```

### 3. getServices

Lists allowed service codes.

```
GET /api/handler_api.php?api_key=YOUR_KEY&action=getServices
```

Response (one per line):

```
whatsapp:WhatsApp
telegram:Telegram
instagram:Instagram
...
```

### 4. getNumber / getNumberV2

Buy a temporary number. Charges wallet immediately.

```
GET /api/handler_api.php?api_key=YOUR_KEY&action=getNumber&service=whatsapp&country=IN
```

| Param | Required | Notes |
|-------|----------|--------|
| `service` | yes | Must be a code from `getServices` (e.g. `whatsapp`). Provider-style ids like `cfiu` / `tg` are rejected. |
| `country` | no | Default `IN`. Use codes from `getCountries` (`IN`, `US`, …). Numeric ids like `22` are rejected. |
| `operator` | no | Optional. If omitted, a live operator is chosen automatically. |
| `maxPrice` | no | Cap in INR sell price; if sell price is higher → `NO_NUMBERS`. |

Success:

```
ACCESS_NUMBER:<activationId>:<phoneDigits>
```

Example:

```
ACCESS_NUMBER:a1b2c3d4-....:9198xxxxxxxx
```

Use `<activationId>` with `getStatus` / `setStatus`.

Common failures (plain text body):

| Body | Meaning |
|------|---------|
| `NO_BALANCE` | Insufficient wallet balance (HTTP may be 402) |
| `NO_NUMBERS` | No stock / operator unavailable |
| `WRONG_SERVICE` | Unknown or blocked service code |
| `WRONG_COUNTRY` | Unknown or numeric country |
| `BAD_KEY` | Invalid API key |

If admin assigned an API discount to the wallet, it applies automatically on `getNumber`.

### 5. getStatus

Poll for SMS / OTP.

```
GET /api/handler_api.php?api_key=YOUR_KEY&action=getStatus&id=ACTIVATION_ID
```

| Param | Required |
|-------|----------|
| `id` | yes (activation id from `ACCESS_NUMBER`) |

Possible replies:

```
STATUS_WAIT_CODE
STATUS_OK:123456
STATUS_WAIT_RETRY:123456
STATUS_CANCEL
NO_ACTIVATION
```

When you see `STATUS_OK:<code>`, the OTP is ready. You may keep polling for another SMS on multi-SMS services.

### 6. setStatus

Cancel, finish, or request another SMS.

```
GET /api/handler_api.php?api_key=YOUR_KEY&action=setStatus&id=ACTIVATION_ID&status=8
```

| `status` | Meaning |
|----------|---------|
| `8` | Cancel (refund rules apply; may return `EARLY_CANCEL_DENIED` if too soon on some paths) |
| `6` | Mark complete / finish |
| `3` | Request next SMS |

Success examples: `ACCESS_CANCEL`, `ACCESS_READY`, `ACCESS_RETRY_GET`.

---

## Country codes (opaque)

Use only these (examples). Full live list: `action=getCountries`.

| Code | Country |
|------|---------|
| IN | India (default) |
| US | United States |
| GB | United Kingdom |
| ID | Indonesia |
| PH | Philippines |
| NG | Nigeria |
| BR | Brazil |
| RU | Russia |
| DE | Germany |
| FR | France |
| CA | Canada |
| MX | Mexico |
| TR | Turkey |
| VN | Vietnam |
| TH | Thailand |
| MY | Malaysia |
| PK | Pakistan |
| BD | Bangladesh |
| EG | Egypt |
| ZA | South Africa |
| AE | UAE |
| SA | Saudi Arabia |

Do **not** send numeric country ids.

---

## Service codes (opaque)

Use only friendly LibOTP codes. Full live list: `action=getServices`.

Popular:

| Code | App |
|------|-----|
| whatsapp | WhatsApp |
| telegram | Telegram |
| instagram | Instagram |
| google | Google |
| facebook | Facebook |
| myjio | My Jio |
| meesho | Meesho |
| airtel | Airtel |
| truecaller | Truecaller |
| discord | Discord |
| snapchat | Snapchat |
| tiktok | TikTok |
| twitter | X / Twitter |
| microsoft | Microsoft |
| apple | Apple |
| linkedin | LinkedIn |
| paypal | PayPal |
| steam | Steam |
| netflix | Netflix |
| binance | Binance |
| openai | ChatGPT / OpenAI |
| amazon | Amazon |
| flipkart | Flipkart |
| swiggy | Swiggy |
| zomato | Zomato |
| uber | Uber |

Do **not** send third-party short codes (e.g. `cfiu`, `tg`, `sto`).

---

## Minimal client flow (for AI implementers)

1. Call `getBalance` — ensure funds.
2. Call `getNumber` with `service` + `country=IN` (or other).
3. Parse `ACCESS_NUMBER:id:phone`.
4. Loop `getStatus&id=...` every 3–5s until `STATUS_OK:code` or timeout (~2–20 min).
5. Optionally `setStatus&status=8` to cancel if no SMS.

### curl examples

```bash
curl "https://libotp.online/api/handler_api.php?api_key=YOUR_KEY&action=getBalance"

curl "https://libotp.online/api/handler_api.php?api_key=YOUR_KEY&action=getServices"

curl "https://libotp.online/api/handler_api.php?api_key=YOUR_KEY&action=getNumber&service=whatsapp&country=IN"

curl "https://libotp.online/api/handler_api.php?api_key=YOUR_KEY&action=getStatus&id=ACTIVATION_ID"

curl "https://libotp.online/api/handler_api.php?api_key=YOUR_KEY&action=setStatus&id=ACTIVATION_ID&status=8"
```

### Pseudocode

```
key = env.LIBOTP_API_KEY
base = "https://libotp.online/api/handler_api.php"

balance = GET base + "?api_key=" + key + "&action=getBalance"
# expect ACCESS_BALANCE:n

buy = GET base + "?api_key=" + key + "&action=getNumber&service=whatsapp&country=IN"
# parse ACCESS_NUMBER:id:phone

loop:
  st = GET base + "?api_key=" + key + "&action=getStatus&id=" + id
  if st starts with STATUS_OK: → return otp
  if st == STATUS_CANCEL → fail
  sleep 4s
```

---

## Rate limits & errors

- Per API key ~60 requests/minute (may return `TOO_MANY_REQUESTS`).
- Global IP limits also apply.
- Unknown `action` → `BAD_ACTION`.
- Server fault → `ERROR`.

This API does **not** grant admin access. Keys only operate on the owning wallet.

---

## Recharge

Users recharge the LibOTP wallet in the web app (UPI / USDT): https://libotp.online/app/recharge

---

## Support

- Site: https://libotp.online  
- Docs HTML: https://libotp.online/docs/api  
- Docs Markdown (this file): https://libotp.online/docs/api.md  
- Telegram: https://t.me/libonetp  
- Help: https://libotp.online/docs  

When building an integration with an AI assistant, paste this URL:  
https://libotp.online/docs/api.md
