API Reference
All endpoints are prefixed with your domain. Authentication uses Bearer tokens — include your API key in the Authorization header. Public OTP endpoints accept API keys (sk_test_... / sk_live_...).
OTP — SMS
POST /api/v1/otp/sms/send
Send an OTP via SMS.
Auth: API key
Request body
| Field | Type | Required | Description | | --- | --- | --- | --- | | phone | string | Yes | Recipient phone in E.164 format |
Response 200
{
"message": "OTP sent successfully",
"expiresAt": "2026-06-09T10:05:00.000Z"
}Errors: 400 missing phone · 402 insufficient balance · 500 carrier error
POST /api/v1/otp/sms/verify
Verify an SMS OTP.
Auth: API key
Request body
| Field | Type | Required | Description | | --- | --- | --- | --- | | phone | string | Yes | Phone number that received the OTP | | code | string | Yes | Code entered by the user |
Response 200 — success
{ "success": true, "message": "OTP verified successfully", "phone": "+628123456789" }Response 400 — failure
{ "success": false, "message": "Invalid or expired OTP" }OTP — WhatsApp
POST /api/v1/otp/whatsapp/send
Send an OTP via WhatsApp.
Auth: API key
Request body
| Field | Type | Required | Description | | --- | --- | --- | --- | | phone | string | Yes | Recipient phone in E.164 format | | whatsappAccountId | number | No | ID of a specific WhatsApp account (PREMIUM / ADMIN only). Defaults to system default. |
Response 200
{
"message": "OTP sent successfully",
"expiresAt": "2026-06-09T10:05:00.000Z"
}Errors: 400 missing phone · 402 insufficient balance · 403 not PREMIUM/ADMIN · 403 account ownership · 404 account not found · 503 no default account configured
POST /api/v1/otp/whatsapp/verify
Verify a WhatsApp OTP.
Auth: API key
Request body
| Field | Type | Required | Description | | --- | --- | --- | --- | | phone | string | Yes | Phone number that received the OTP | | code | string | Yes | Code entered by the user |
Response 200 — success
{ "success": true, "message": "OTP verified successfully", "phone": "+628123456789" }Response 400 — failure
{ "success": false, "message": "Invalid or expired OTP" }API Tokens
GET /api/token
List all API tokens for the authenticated user.
Auth: Console session token
Response 200
{
"tokens": [
{
"id": 1,
"name": "Production",
"key": "sk_live_••••••••...abcd",
"environment": "LIVE",
"active": true,
"allowedDomains": "example.com",
"lastUsedAt": "2026-06-09T08:00:00.000Z",
"createdAt": "2026-01-01T00:00:00.000Z"
}
]
}Keys are masked in the list response. The full key is only available at creation time.
POST /api/token
Create a new API token.
Auth: Console session token
Request body
| Field | Type | Required | Description | | --- | --- | --- | --- | | name | string | Yes | Human-readable label | | environment | string | No | TEST or LIVE. Default: LIVE | | allowedDomains | string | No | Comma-separated allowed domains |
Response 201 — includes full key (only shown once)
{
"token": {
"id": 2,
"name": "Staging",
"key": "sk_test_abc123def456...",
"environment": "TEST",
"active": true,
"allowedDomains": "",
"createdAt": "2026-06-09T08:00:00.000Z"
}
}PUT /api/token/:id
Revoke (deactivate) a token.
Auth: Console session token (must own the token)
Response 200
{ "token": { "id": 1, "name": "Production", "environment": "LIVE", "active": false } }DELETE /api/token/:id
Permanently delete a token.
Auth: Console session token (must own the token)
Response 204 — no body
Settings
GET /api/settings
Retrieve the authenticated user's notification and webhook settings.
Auth: Console session token
Response 200
{
"data": {
"webhookUrl": "https://your-server.com/hooks/secureotp",
"notifyLowCredit": true,
"notifyWeeklyReport": false,
"notifyNewLogin": false,
"notifyLoginActivity": false,
"twoFactorEnabled": false
}
}PUT /api/settings
Update notification and webhook settings.
Auth: Console session token
Request body — all fields optional
| Field | Type | Description | | --- | --- | --- | | webhookUrl | string | HTTPS URL to receive event callbacks. Pass "" or null to clear. | | notifyLowCredit | boolean | Notify on low balance | | notifyWeeklyReport | boolean | Weekly activity report | | notifyNewLogin | boolean | Alert on new login | | notifyLoginActivity | boolean | Log all login events | | twoFactorEnabled | boolean | Enable 2FA |
Response 200 — returns updated settings object
Orders
GET /api/order
List credit top-up orders.
Auth: Console session token
Query parameters
| Parameter | Description | | --- | --- | | page | Page number (default 1) | | limit | Results per page, max 100 (default 20) | | status | Filter by PENDING, COMPLETED, or FAILED | | dateFrom | Start date (ISO 8601) | | dateTo | End date (ISO 8601) |
Response 200
{
"data": [
{
"id": 1,
"invoiceNo": "ORD-20260609-A1B2C",
"amount": 100000,
"status": "COMPLETED",
"paymentVia": "BANK_TRANSFER",
"paymentDate": "2026-06-09T09:00:00.000Z",
"createdAt": "2026-06-09T08:00:00.000Z"
}
],
"lifetimeTotal": 250000,
"pagination": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 }
}POST /api/order
Create a new top-up order.
Auth: Console session token
Request body
| Field | Type | Required | Description | | --- | --- | --- | --- | | amount | number | Yes | Credit amount to purchase | | paymentVia | string | Yes | BANK_TRANSFER, QRIS, CREDIT_CARD, DEBIT_CARD, E_WALLET, or CASH | | notes | string | No | Optional payment notes |
Response 201
{
"order": {
"id": 2,
"invoiceNo": "ORD-20260609-X9YZ1",
"amount": 50000,
"status": "PENDING",
"paymentVia": "QRIS",
"createdAt": "2026-06-09T10:00:00.000Z"
}
}HTTP Status Codes
| Code | Meaning | | --- | --- | | 200 | Success | | 201 | Created | | 204 | No content | | 400 | Bad request — missing or invalid parameters | | 401 | Unauthorized — missing, invalid, or revoked API key | | 402 | Payment required — insufficient credit balance | | 403 | Forbidden — role does not permit this action | | 404 | Not found | | 422 | Unprocessable — validation error | | 500 | Internal server error | | 503 | Service unavailable — WhatsApp account not configured |