Skip to main content
Use POST /v3/request when you want OTP.ID to generate and deliver the one-time code on your behalf. You choose the channel, destination, and optional parameters — OTP.ID handles code generation and delivery. The code is never returned in the API response; it travels only to the end user via the chosen channel. On success you receive a transaction ID (otp_id) you’ll use later to verify what the user enters.

Endpoint

Request Headers

Request Body

string
required
The delivery channel for the OTP. Accepted values:
  • whatsapp — WhatsApp message
  • sms — SMS text message
  • email — Email message
  • misscall — Missed call (the OTP code is derived from the caller’s number)
  • voice — Phone call that reads the OTP aloud
  • whatsapp_inbound — User sends a WhatsApp message to OTP.ID to verify (see WhatsApp Inbound)
string
required
The destination phone number or email address. Required for all channels except whatsapp_inbound.
  • Phone numbers: digits only, minimum 10 characters (e.g. "6281234567890")
  • Email: a valid email address (e.g. "user@example.com")
string
Legacy alias for destination. It is still accepted for backward compatibility, but new integrations and all examples should use destination. If both destination and number are provided, destination takes precedence.
string
Legacy fallback for Email OTP. It is used only when both destination and number are empty.
string
The brand name included in channels that deliver a message (e.g. "MyApp"). Defaults to the brand_name configured on your merchant account. For voice, this field is required and is read aloud to the user. For misscall, this field is not needed because there is no message body that displays a brand.
integer
Number of digits in the generated OTP. Accepted range: 48. Defaults to 6. Values outside this range are clamped to the nearest bound — not rejected. For voice, the OTP is always four numeric digits, even when you provide otp_length.
integer
Time-to-live in seconds before the OTP expires. Accepted range: 60900. Defaults to 300 (5 minutes). Values outside this range are clamped to the nearest bound — not rejected.
string
Your own idempotency key for this transaction. If you submit a request with an external_id that already exists, OTP.ID returns the original otp_id without sending a new message and without deducting additional credits. See Idempotency below.

Response Fields

string
The unique transaction ID assigned by OTP.ID. Use this value in subsequent calls to POST /v3/verify and GET /v3/otp/{otp_id}. Format: OTP + YYYYMMDD + 4 letters + 6 digits (e.g. OTP20260807ABCD000001).
string
Current delivery status of the transaction:
  • sent / success — code was delivered to the destination
  • pending — awaiting completion; always the initial status for whatsapp_inbound (waiting for the user’s reply), and may also appear for some delivery vendors
  • failed — vendor delivery failed (see Delivery Failures)
string
The channel used to deliver the OTP, echoed from your request.
string
The destination address used. This response field is still named number for backward compatibility, even when your request uses destination.
integer
The number of credits charged for this transaction. This field is present even when delivery fails. No credits are deducted when delivery fails — see Delivery Failures.
integer
Your remaining credits after this transaction. Unchanged if the delivery failed or if this was an idempotency replay.
string
The datetime at which this OTP expires, in YYYY-MM-DD HH:MM:SS format (WIB, UTC+7).
object
Present only for whatsapp_inbound and misscall transactions. Contains the material your user needs to verify themselves — the shape depends on the channel.

Examples

WhatsApp

Success response:

Email

Voice

Voice is available only through POST /v3/request. Provide an explicit brand; OTP.ID always generates a four-digit numeric OTP for this channel.
Voice is not available on POST /v3/send. Use the standard POST /v3/verify flow with the otp_id from this response and the four digits entered by your user.

WhatsApp Inbound

Success response (whatsapp_inbound):
For unbound whatsapp_inbound, do not pass destination. Show the user the verification.wa_link (or the wa_number + message pair) and have them tap/send it from their own WhatsApp. OTP.ID matches the inbound message to this transaction automatically — no call to POST /v3/verify is needed.
Success response (misscall):
For misscall, brand is optional and usually omitted. The OTP is the last digits of the number that calls your user. Build your verification form around verification.prefix: render the prefix (e.g. 628559263-____), let the user fill in the last otp_length digits from their missed-call log, then submit those digits to POST /v3/verify as otp. The full calling number is never returned by the API.

Special Behaviors

Delivery Failures

When the OTP.ID vendor accepts your request but ultimately fails to deliver the message (e.g. the number is unreachable), the API still returns HTTP 200 with "status": "failed". Credits are not deducted. Check data.status in your application logic — do not rely solely on the HTTP status code. Example — delivery failure (HTTP 200):

Idempotency

If you provide an external_id and a request with that ID was already processed within the last 24 hours, OTP.ID returns the original transaction (same otp_id) without sending a new message and without deducting credits again. This lets you safely retry requests after network timeouts. Note that the replayed response reflects the transaction’s current state: status may have progressed (e.g. to verified) and last_balance shows your current credit balance.

WhatsApp Inbound

When channel is whatsapp_inbound, destination is optional. Omitting it means OTP.ID will accept the verification reply from any WhatsApp number that sends the matching token — useful when you do not yet know the user’s number. The transaction transitions to verified automatically when the matching message arrives.

Missed Call

When channel is misscall, the code is determined by the telephony vendor — it is the last digits of the calling number, so otp_length in the request body has no effect. The brand field is not required for this channel because no OTP message is sent. The response’s verification.prefix gives you the calling number without those digits so your UI can pre-render it. The verification block (with prefix only) is also included on idempotency replays and on GET /v3/otp/{otp_id} while the transaction has not been verified yet. Missed Call requests are limited to 2 per destination in 10 minutes and 4 per destination in 1 hour. If either window is exhausted, the API returns DESTINATION_RATE_LIMITED with HTTP 429. See the Missed Call guide for the separate two-attempt verification limit.

Voice

When channel is voice, send a destination phone number in destination and an explicit brand. The user receives a call that speaks the brand and a four-digit numeric OTP with each digit read separately. otp_length does not change this four-digit contract. Voice is request-only: do not use POST /v3/send for this channel. See the Voice guide for the complete request and verification flow.

Error Responses

All errors follow the shape {"success": false, "data": null, "error": {"code": "...", "message": "..."}}. Example — insufficient credits:
Example — duplicate external_id collision: