Skip to main content
Network conditions are unpredictable. A request to POST /v3/request or POST /v3/send might time out before your code receives the response — but the server may have already processed it and charged your balance. If you retry blindly, you risk sending your user two OTP messages and paying twice. The external_id field solves this: by attaching your own unique key to a request, you make it safe to retry any number of times.

What external_id does

Pass a unique string in the external_id field of any POST /v3/request or POST /v3/send call. OTP.ID stores this key alongside the transaction for 24 hours. If you submit the same external_id again within that window with identical parameters (same channel, number, and — for /v3/send — the same otp), OTP.ID returns the original transaction record instead of creating a new one. No new message is sent to the user and no additional balance is deducted. This is called a replay.

Why it matters

Without external_id, a network timeout forces you to choose between two bad options: retry (risk a duplicate send) or give up (the user never gets verified). With external_id, retrying is always safe — you get back the original result.

Step-by-step example

1. First request

Your server sends the initial OTP request with a unique external_id tied to your own transaction or session.
Response:

2. Retry with the same parameters — replay

Your request timed out before you received the response above. You retry with the exact same body.
Replay response — same otp_id, balance unchanged:
OTP.ID returned the original transaction. No second message was sent. Your balance was not charged again.

3. Same external_id, different parameters — 409 error

If you accidentally (or intentionally) use the same external_id with different parameters — a different channel, number, or otp value — OTP.ID rejects the request with HTTP 409 DUPLICATE_EXTERNAL_ID.
409 response:
The error.details.existing_otp_id field tells you the ID of the original transaction. Use GET /v3/otp/{otp_id} to retrieve its full details and determine the correct next step.
A 409 DUPLICATE_EXTERNAL_ID is not a send failure — it means an OTP was already sent under this key. Inspect the original transaction before deciding whether to issue a new one.

Best practices

  • Use a meaningful, unique key. Your order ID, session ID, checkout attempt UUID, or similar identifier works well. The key must be unique per OTP attempt — don’t reuse keys across different verification events.
  • Always include external_id in production. Transient network failures are common. Without external_id, any automated retry logic can send duplicate OTPs and charge your balance twice.
  • Treat 409 as an inspection prompt, not an error. Fetch the original transaction with GET /v3/otp/{existing_otp_id}. If it’s still pending and not expired, you can direct the user to verify with that transaction. Only create a new OTP if the original has expired or already been used.
  • Keep keys unique per attempt. If a transaction expires and the user requests a new OTP, generate a fresh external_id. Reusing the old one will cause a 409 (the 24-hour window has not yet elapsed).