Skip to main content
After you send an OTP with POST /v3/request or POST /v3/send, the user enters the code in your interface. You then call POST /v3/verify to check whether the code is correct. Understanding exactly how OTP.ID reports wrong codes, expired codes, and exhausted attempts is critical — a wrong code does not produce an HTTP error, so you must inspect the response body.

Endpoint

Request body

How verification works

1

User enters the code

Your frontend captures the OTP the user types and sends it to your backend.
2

Your backend calls POST /v3/verify

Pass otp_id (from when you sent the OTP) and otp (what the user entered).
3

Check data.verified in the response

A correct code returns "verified": true. A wrong code returns HTTP 200 with "verified": false — this is not a 4xx error. You must check the field explicitly.
4

Handle terminal error conditions

If the OTP has expired, been used, or the attempt limit is reached, OTP.ID returns HTTP 422. Catch these and guide the user accordingly.

curl example

Response reference

✅ Successful verification (HTTP 200)

The code matched. The transaction is now marked as used and cannot be verified again.

❌ Wrong code — mismatch (HTTP 200)

A wrong code returns HTTP 200, not a 4xx error. The success field is true because the API call itself succeeded — the OTP simply did not match. Always check data.verified in your application code, never rely on HTTP status alone.
Each wrong attempt increments an internal counter. You can inspect the current attempt count at any time with GET /v3/otp/{otp_id}. After 5 wrong attempts, the transaction is permanently locked and returns TOO_MANY_ATTEMPTS.

🚫 Terminal errors (HTTP 422)

These conditions permanently end the transaction. You must issue a new OTP if the user needs to try again. OTP_EXPIRED
TOO_MANY_ATTEMPTS
ALREADY_USED

Application logic example

The example below shows how to handle all verification outcomes correctly in a Node.js/Express backend.

Webhooks

A successful verification also fires the otp.verified webhook asynchronously to your configured endpoint. The webhook is useful for event-driven flows — for example, granting access on the server side without polling. See the Webhooks guide for payload details and signature verification.
The webhook fires after both successful POST /v3/verify calls and automatic WhatsApp Inbound matches. It does not fire for failed or mismatched attempts.