Reseller API Reference
Full REST API for the NumberOTP Reseller Platform. Manage sub-users, configure markup, handle payouts, and build automations on top of real-time webhooks.
1Authentication
All reseller endpoints require a Bearer token in the Authorization header. Generate an API key from your Developer Dashboard. Keys are prefixed with notp_.
Authorization: Bearer notp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
The same key grants access to both standard activation endpoints and reseller endpoints. If the authenticated account is not a reseller, reseller endpoints return 403 Forbidden.
https://api.numberotp.com/v1All paths below are relative to this base.
2Profile & Settings
/reseller/profileGet reseller profile
Returns account info including buying balance, earned balance, reseller public ID, brand settings, webhook URL, and sub-user defaults.
/reseller/profileUpdate brand, webhook, and sub-user settings
Update brandName, logoUrl, color, webhookUrl, webhookSecret, allowSelfFundDefault, and minSubUserTopup. All fields are optional โ only provided keys are updated.
PATCH /reseller/profile โ Request Body
{
"brandName": "Acme OTP",
"logoUrl": "https://acme.com/logo.png",
"color": "#6366f1",
"webhookUrl": "https://acme.com/webhooks/numberotp",
"webhookSecret": "my-hmac-secret",
"allowSelfFundDefault": true,
"minSubUserTopup": 10
}GET /reseller/profile โ Response
{
"success": true,
"data": {
"id": "64f3a...",
"email": "you@company.com",
"name": "Jane Smith",
"buyingBalance": 42.50,
"earnedBalance": 18.75,
"resellerPublicId": "a26e2be6",
"brand": {
"name": "Acme OTP",
"logoUrl": "https://acme.com/logo.png",
"color": "#6366f1"
},
"webhookUrl": "https://acme.com/webhooks/numberotp",
"minSubUserTopup": 10,
"allowSelfFundDefault": true,
"markupVersion": 3,
"publicSignupUrl": "https://www.numberotp.com/join/a26e2be6"
}
}3Sub-User Management
/reseller/usersList sub-users
Returns all sub-users under this reseller account. Includes email, balance, and status.
/reseller/usersCreate a sub-user
Manually create a sub-user. The new account is immediately active. Optionally allocate an initial balance from your buying credits.
/reseller/users/{id}Get sub-user details
Returns full profile for a single sub-user including balance, API key count, and activation history summary.
/reseller/users/{id}Update sub-user
Adjust balance (delta), toggle suspended status, or update allowSelfFund. Balance delta is applied atomically โ positive adds funds, negative deducts.
/reseller/users/{id}Delete sub-user
Permanently deletes the sub-user account. Any remaining balance is forfeited and not returned to the reseller.
POST /reseller/users โ Request Body
{
"email": "user@example.com",
"password": "securepassword",
"name": "John Doe",
"initialBalance": 5.00,
"allowSelfFund": true
}PATCH /reseller/users/{id} โ Request Body
{
"balanceDelta": 10.00,
"suspended": false,
"allowSelfFund": true
}GET /reseller/users โ Response
{
"success": true,
"data": [
{
"id": "64f3b...",
"email": "user@example.com",
"name": "John Doe",
"balance": 12.50,
"suspended": false,
"allowSelfFund": true,
"createdAt": "2026-01-15T10:00:00.000Z"
}
]
}4Markup & Pricing
/reseller/markupGet current markup configuration
Returns the global multiplier, per-service overrides, per-country overrides, and autoAdjustLoyalty flag.
/reseller/markupUpdate markup
Set a global multiplier applied to all platform prices, plus optional per-service and per-country overrides. Busts the markup cache immediately.
PATCH /reseller/markup โ Request Body
{
"global": 2.0,
"overrides": {
"wa": 1.8,
"tg": 2.5
},
"countryOverrides": {
"67": 1.5
},
"autoAdjustLoyalty": false
}countryOverride[country] ร serviceOverride[service] ร global. Overrides multiply on top of each other. If autoAdjustLoyalty is true, the platform price used is already discounted by your loyalty tier.GET /reseller/markup โ Response
{
"success": true,
"data": {
"global": 2.0,
"overrides": { "wa": 1.8 },
"countryOverrides": {},
"autoAdjustLoyalty": false,
"version": 3
}
}5Balances & Payouts
/reseller/payout/convertConvert earned balance โ buying credits
Instantly moves funds from your earned balance (sub-user revenue) into your buying credits wallet. Zero fee. Atomic โ guarded against race conditions.
/reseller/payout/withdrawRequest crypto withdrawal
Submit a withdrawal request from your earned balance. Minimum $10. Processed within 24 hours to your specified crypto address.
/reseller/payments/cryptoTop up buying credits via crypto
Creates a NowPayments invoice to add buying credits. Minimum $20. Returns a payment URL to redirect your user to.
POST /reseller/payout/convert โ Request Body
{ "amount": 15.00 }POST /reseller/payout/withdraw โ Request Body
{
"amount": 25.00,
"currency": "USDT",
"address": "TYour...WalletAddress"
}POST /reseller/payments/crypto โ Request Body
{ "amount": 50.00, "currency": "USDT" }POST /reseller/payments/crypto โ Response
{
"success": true,
"paymentUrl": "https://nowpayments.io/payment/?iid=...",
"invoiceId": "np_123456"
}6Reseller Webhooks
Set a webhook URL in your reseller settings (via dashboard or PATCH /reseller/profile). NumberOTP posts HMAC-signed events to this URL for all reseller-relevant activity.
Events
| event | Triggered when |
|---|---|
| sub_user.created | A sub-user registers (self or via API) |
| sub_user.topup | A sub-user funds their own balance |
| sub_user.purchase | A sub-user buys a number (you earn the markup) |
Webhook Payload
{
"event": "sub_user.purchase",
"resellerId": "64f3a...",
"subUserId": "64f3b...",
"subUserEmail": "user@example.com",
"amount": 0.90,
"resellerEarned": 0.45,
"service": "wa",
"country": "67",
"timestamp": "2026-06-20T12:00:00.000Z"
}Signature Verification (HMAC-SHA256)
Every request includes the header x-numberotp-reseller-signature: sha256=<hex>. Verify it server-side:
const crypto = require('crypto')
function verifyResellerWebhook(body, signatureHeader, secret) {
const hash = crypto
.createHmac('sha256', secret)
.update(typeof body === 'string' ? body : JSON.stringify(body))
.digest('hex')
const expected = `sha256=${hash}`
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected)
)
}7Public Endpoints (No Auth)
These endpoints are called by your sub-users directly โ no API key needed. Replace {resellerPublicId} with your resellerPublicId from your profile.
/reseller-public/{resellerPublicId}/brandpublicGet reseller brand info
Returns brandName, logoUrl, and color for the white-label signup page. Used by the hosted /join page automatically.
/reseller-public/{resellerPublicId}/signuppublicRegister a sub-user
Creates a new sub-user account linked to this reseller. Requires Cloudflare Turnstile token. Returns success; email OTP verification is sent automatically.
/reseller-public/{resellerPublicId}/topup/dodopublicSub-user card top-up (Dodo Payments)
Initiates a card payment for the sub-user. Minimum amount is the reseller's configured minSubUserTopup. Returns a checkout URL.
/reseller-public/{resellerPublicId}/topup/cryptopublicSub-user crypto top-up (NowPayments)
Creates a crypto invoice for the sub-user. Returns a NowPayments payment URL.
POST /reseller-public/{id}/signup โ Request Body
{
"email": "newuser@example.com",
"password": "securepassword",
"name": "Jane Doe",
"turnstileToken": "cf-turnstile-token-here"
}POST /reseller-public/{id}/topup/crypto โ Request Body
{
"amount": 20.00,
"currency": "USDT",
"subUserEmail": "user@example.com"
}8Error Codes
| Status | Meaning |
|---|---|
| 401 | Missing or invalid Bearer token |
| 403 | Authenticated but not a reseller, or accessing another reseller's sub-user |
| 404 | Resource not found (sub-user, reseller public ID, etc.) |
| 409 | Conflict โ e.g. sub-user email already registered |
| 422 | Validation error โ missing or invalid fields in request body |
| 402 | Insufficient balance for the requested operation |
| 429 | Rate limit exceeded (100 req/min per IP for public endpoints) |
Error Response Shape
{ "error": "Human-readable error message" }