Developers
How to add OTP verification to your app
One-time passcodes are the simplest way to prove a user controls a phone number — for signup, login, or approving a sensitive action. With Bremlio it's two API calls, and the hard parts (generating the code, delivering it, expiring it, locking out brute force) are handled for you.
Step 1 — send the code
const { verification_id } = await fetch( "https://api.bremlio.com/v1/otp/send", { method: "POST", headers: { "X-Api-Key": KEY, "Content-Type": "application/json" }, body: JSON.stringify({ to: "+447700900123" }) }).then(r => r.json()); // store verification_id against the pending session
Step 2 — verify what the user typed
const { verified } = await fetch( "https://api.bremlio.com/v1/otp/verify", { method: "POST", headers: { "X-Api-Key": KEY, "Content-Type": "application/json" }, body: JSON.stringify({ verification_id, code: userInput }) }).then(r => r.json()); if (verified) { /* authenticate the user */ }
The failure cases most integrations miss
- Expiry. Codes last 10 minutes. Verifying an expired code returns a clear error — show "code expired, request a new one", don't just say "wrong code".
- Attempt limits. After 5 wrong tries the code locks. Handle the lockout in your UI so users aren't stuck retyping.
- Idempotency. An already-verified id returns verified again — safe, but don't treat a second verify as a fresh login on its own.
- Don't trust the client. Only your server should call
/verify; never expose the result to the browser as the sole gate.
Build without spending credits
Configure a test number so the flow runs end to end with a fixed code and no carrier send — ideal for local development and CI.