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 messagesms— SMS text messageemail— Email messagemisscall— Missed call (the OTP code is derived from the caller’s number)voice— Phone call that reads the OTP aloudwhatsapp_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:
4–8. 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:
60–900. 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 destinationpending— awaiting completion; always the initial status forwhatsapp_inbound(waiting for the user’s reply), and may also appear for some delivery vendorsfailed— 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
Voice
Voice is available only throughPOST /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
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.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 anexternal_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
Whenchannel 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
Whenchannel 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
Whenchannel 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:

