success field is false, the data field is null, and the error object contains a machine-readable code and a human-readable message. Always branch your error-handling logic on error.code — the code string is stable across API versions, while the message text may change without notice.
Quick reference
Authentication errors
UNAUTHORIZED — HTTP 401
Your request did not include a valid API key.
Causes:
- The
Authorizationheader is missing entirely - The header is present but empty or malformed (e.g., not using
Bearer <api_key>) - The API key has been revoked or does not exist
message varies by cause: "Sertakan header Authorization: Bearer <api_key>" (header missing/malformed), "API key kosong" (empty token), or "API key tidak dikenal" (unknown key).
How to resolve: Verify that every request includes the header Authorization: Bearer <api_key> using your key from the OTP.ID dashboard (Integration page → API Key). If the key was recently rotated, update it in your application configuration.
IP_NOT_ALLOWED — HTTP 403
The request came from an IP address that is not in the merchant’s API V3 IP whitelist. IP allowlisting is independent of API-key authentication and is evaluated after the API key is validated.
How to resolve: Ask OTP.ID to configure the whitelist for your merchant, then provide the exact public IPv4 or IPv6 addresses of the servers that call the API. Multiple addresses are comma-separated, for example 203.0.113.10,198.51.100.7,2001:db8::7. Whitespace around commas is ignored; CIDR ranges are not accepted. An empty whitelist allows requests from any resolved IP.
OTP.ID resolves the client IP automatically in this order: CF-Connecting-IP, X-Real-IP, the first address in X-Forwarded-For, then the direct connection address. If Cloudflare is bypassed, the resolver automatically falls back to the next available value.
Example response:
Request validation errors
VALIDATION_ERROR — HTTP 400
The request body is missing required fields or contains values that fail validation.
Causes:
- On
POST /v3/send: theotpfield is missing, contains non-digit characters, or its length is outside 4–8 - On
POST /v3/verify:otp_idorotpis missing from the request body
ttl and otp_length values do not trigger this error — they are silently clamped to the valid range.
Example response:
POST /v3/verify uses the message "otp_id dan otp wajib diisi".)
How to resolve: Check the error.message text for the specific field that failed validation, then correct your request body. Consult the API Reference for each endpoint’s required and optional fields.
INVALID_CHANNEL — HTTP 400
The channel value you provided is not recognised, or you used a channel that is not permitted on the specified endpoint.
Causes:
- The
channelstring does not match any supported value (whatsapp,sms,email,misscall,whatsapp_inbound) - You passed
misscallorwhatsapp_inboundtoPOST /v3/send— these channels are only valid withPOST /v3/request
POST /v3/request endpoint. See the Missed Call guide and WhatsApp Inbound guide for the full flows.
INVALID_NUMBER — HTTP 400
The destination phone number or email address could not be parsed or validated.
Causes:
- The phone number contains non-digit characters (e.g. a leading
+, spaces, or dashes) - The phone number is shorter than 10 digits
- The email address is syntactically invalid
- The
destinationfield is missing on a channel that requires it
+ (e.g., 6281234567890 for an Indonesian number). For email, ensure the address conforms to standard format (user@example.com).
Credit errors
INSUFFICIENT_BALANCE — HTTP 402
Your merchant account does not have enough prepaid credits to cover the cost of this transaction.
Causes:
- Your credit balance has fallen below the per-transaction price for the requested channel
- A recent top-up has not yet been applied to your account
POST /v3/topups. Review the Billing guide for per-channel pricing details. Consider setting up a low-credit alert to avoid disruption in production.
Not found errors
OTP_NOT_FOUND — HTTP 404
The otp_id you referenced does not exist or is not owned by the API key making the request.
Causes:
- The
otp_idwas typed or stored incorrectly - The OTP was created with a different API key (cross-merchant access is not permitted)
otp_id value exactly as returned by POST /v3/request or POST /v3/send. Ensure you are using the same API key that created the OTP transaction.
Conflict errors
DUPLICATE_EXTERNAL_ID — HTTP 409
You submitted an external_id that has already been used for a different OTP transaction with different parameters.
Causes:
- Your system retried a request with the same
external_idbut changed thechannel,destination, or other parameters between retries - Two concurrent processes generated the same
external_idwith different payloads
This is the only error code that includes an
error.details object. The existing_otp_id field gives you the otp_id of the original transaction. Use GET /v3/otp/{otp_id} to retrieve its current status and continue the verification flow without creating a new OTP.error.details.existing_otp_id and look up the original transaction with GET /v3/otp/{otp_id}. If the parameters genuinely need to change (different channel, different destination), generate a new unique external_id. See the Idempotency guide for best practices on external_id generation.
Terminal state errors (422)
These three errors indicate that the OTP transaction has reached a point from which it cannot recover. You must create a new OTP transaction — calling verify again on the sameotp_id will continue to fail.
OTP_EXPIRED — HTTP 422
The OTP’s time-to-live (TTL) has elapsed and the code is no longer valid.
Causes:
- The user took too long to enter the code
- Your application delayed the verify call beyond the OTP’s expiry time (visible in
data.expires_aton the send response)
POST /v3/request or POST /v3/send again. Display a “resend OTP” option in your UI before the expiry time so users can request a new code proactively.
TOO_MANY_ATTEMPTS — HTTP 422
The user has submitted the maximum number of incorrect codes for this OTP transaction. The default limit is 5 failed attempts; Missed Call transactions have a stricter limit of 2 because their caller-ID OTP is four digits. Further verification attempts are blocked.
Example response:
200, while subsequent verification calls return this 422 error. Consider adding client-side attempt tracking to disable the submit button after several failures before reaching the server-side limit.
ALREADY_USED — HTTP 422
The OTP has already been successfully verified and cannot be used again.
Causes:
- Your application called
POST /v3/verifya second time after receivingdata.verified: true - A race condition caused two concurrent verify calls to be sent for the same
otp_id
data.verified: true), store the result and do not call verify again for the same otp_id. If your architecture has concurrent processes, add a local lock or idempotency check before calling the verify endpoint.
Rate limit errors
RATE_LIMITED — HTTP 429
Your API key has exceeded the global rate limit of 20 requests per second across all /v3/* endpoints.
Example response:
DESTINATION_RATE_LIMITED — HTTP 429
You have sent too many OTPs to the same destination phone number or email address in a short window.
Limits for standard channels:
- Maximum 5 OTPs per 10 minutes to the same destination
- Maximum 10 OTPs per hour to the same destination
misscall:
- Maximum 2 OTPs per 10 minutes to the same destination
- Maximum 4 OTPs per hour to the same destination
Infrastructure errors
CHANNEL_UNAVAILABLE — HTTP 503
The delivery channel or underlying vendor is temporarily unavailable.
Causes:
- The WhatsApp, SMS, email, or missed call provider is experiencing an outage or maintenance window
- A transient network issue between OTP.ID and the delivery vendor
INTERNAL_ERROR — HTTP 500
An unexpected error occurred on the OTP.ID server.
Example response:
otp_id or external_id values from the affected requests to help with investigation.

