Developer documentation

Integrate phone verification in an afternoon.

Two ways to build: a drop-in widget for the fastest path, or a two-endpoint REST API for full control. No telecom expertise required.

Quickstart

Pick the integration that fits your team. You can switch later — both use the same keys and dashboard.

Managed · Widget

Drop-in widget

Embed a secure, pre-built UI. We handle input, code entry, resend and lockout.

Go to widget guide →
Direct · REST API

REST API

Build your own UI and call us from your backend. Two endpoints, signed result token.

Go to API guide →

Authentication

All API requests are authenticated with a Bearer token. Your secret key (sk_live_…) must stay on your backend — never ship it to the browser or mobile app. The widget uses a public key (pk_live_…).

# Base URL
https://api.doneotp.com

# Header on every request
Authorization: Bearer sk_live_xxx
🔐 Lock keys to your servers with an IP allowlist in the dashboard — even a leaked key won't work from another machine.

Managed widget

The fastest path. Add a container, load the script, and handle the result. We run the entire flow: phone input, country codes, OTP entry, resend timer and lockout.

1
Add a container
<div id="doneotp"></div>
2
Load & initialize
<script src="https://cdn.doneotp.com/widget.js"></script>
<script>
  DoneOTP.init({
    apiKey: "pk_live_xxx",        // public key
    elementId: "doneotp",
    theme: { brand: "#10B981" },  // match your UI
    onVerified: (res) => {
      // send the token to your backend to confirm
      verifyOnServer(res.token);
    },
    onFailed: (err) => console.warn(err)
  });
</script>
3
Confirm the token on your backend — call /v1/verify/validate-token (see below) so you never trust the browser.

REST API

Build your own UI and call two endpoints from your server. Example: send a code, then check it.

# 1 · Send a code
curl https://api.doneotp.com/v1/verify/send \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "phone_number": "+14155552671" }'

# 2 · Check the code
curl https://api.doneotp.com/v1/verify/check \
  -H "Authorization: Bearer sk_live_xxx" \
  -d '{ "phone_number": "+14155552671", "code": "493812" }'

Endpoints

POST /v1/verify/send

Generates a one-time code and sends it via SMS. Returns immediately with pending.

// 200 OK
{ "status": "pending", "expires_in": 300 }
POST /v1/verify/check

Validates the user's code. On success returns a signed, single-use token.

// 200 OK
{ "status": "verified", "token": "tok_xxx" }
POST /v1/verify/validate-token

For the widget (Managed mode): your backend confirms the token is genuine and unused. Not needed in Direct mode — there your backend already gets the result.

// 200 OK
{ "valid": true, "phone_number": "+90532****2719" }

Status codes

CodeMeaningWhen
200OKCode sent / verified
400Bad requestWrong code, attempts exhausted, or invalid token
401UnauthorizedMissing or invalid API key
402Payment requiredInsufficient credit balance
403ForbiddenIP or number blocklisted / not in allowlist
404Not foundNo active verification or code expired
429Too many requestsRate limit exceeded

Security best practices

• Keep your sk_live_… key server-side only.
• Add an IP allowlist so the key only works from your servers.
• In Managed mode, always confirm the token via /validate-token — never trust the browser.
• We mask phone & IP at the write layer and never store the OTP in plain text.