Fast, Secure, Developer-Friendly and Self-Managed (No Admin).
Create an account at otply.xyz/signup.html and copy your API Key and API Secret from the dashboard. Every account starts on the Trial Plan โ 10 free OTP emails per month (refills monthly, no card required). Subscribe for unlimited emails at $9.99/month or $99.99/year.
https://api.otply.xyz:8246/api/v1
X-API-Key: otpm_your_key_here
X-API-Secret: your_secret_here
/send-otp{
"email": "user@example.com",
"purpose": "login", // optional, default "verification"
"expiry_minutes": 10 // optional, 1-60, default 10
}
{
"success": true,
"message": "OTP sent successfully",
"expires_in": 600,
"reference_id": "42"
}
On the Trial Plan the message also shows remaining quota, e.g. (Trial Plan: 9 of 10 remaining this month).
login can only be verified with login. Use separate purposes per flow โ signup, login, password-reset, confirm-action โ so they never collide./verify-otp{
"email": "user@example.com",
"otp": "482913",
"purpose": "login" // must match the purpose used when sending
}
{ "success": true, "message": "OTP verified successfully", "verified_at": "2026-07-29T18:45:00" }
{ "success": false, "message": "Invalid OTP. 2 attempt(s) remaining." }
success field, not the status code.429 with the wait time| Use case | Flow |
|---|---|
| Signup email verification | Create account unverified โ send-otp (purpose signup) โ user enters code โ verify-otp โ mark verified |
| Two-factor login (2FA) | Password correct โ send-otp (purpose login) โ verify-otp โ issue session/JWT |
| Password reset | send-otp (purpose password-reset) โ verify-otp โ allow new password |
| Sensitive action confirmation | send-otp (purpose confirm-action) before deletes, payouts, transfers |
curl -X POST https://api.otply.xyz:8246/api/v1/send-otp \
-H "X-API-Key: $OTPLY_KEY" -H "X-API-Secret: $OTPLY_SECRET" \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","purpose":"login"}'
const OTPLY = 'https://api.otply.xyz:8246/api/v1';
const HEADERS = {
'X-API-Key': process.env.OTPLY_KEY,
'X-API-Secret': process.env.OTPLY_SECRET,
'Content-Type': 'application/json'
};
const send = (email) => fetch(`${OTPLY}/send-otp`, {
method: 'POST', headers: HEADERS,
body: JSON.stringify({ email, purpose: 'login' })
}).then(r => r.json());
const verify = (email, otp) => fetch(`${OTPLY}/verify-otp`, {
method: 'POST', headers: HEADERS,
body: JSON.stringify({ email, otp, purpose: 'login' })
}).then(r => r.json());
import os, requests
OTPLY = "https://api.otply.xyz:8246/api/v1"
HEADERS = {"X-API-Key": os.environ["OTPLY_KEY"],
"X-API-Secret": os.environ["OTPLY_SECRET"]}
def send_otp(email, purpose="verification"):
return requests.post(f"{OTPLY}/send-otp", headers=HEADERS,
json={"email": email, "purpose": purpose}, timeout=30).json()
def verify_otp(email, otp, purpose="verification"):
r = requests.post(f"{OTPLY}/verify-otp", headers=HEADERS,
json={"email": email, "otp": otp, "purpose": purpose}, timeout=30)
return r.json().get("success", False)
<?php
function otply(string $endpoint, array $body): array {
$ch = curl_init("https://api.otply.xyz:8246/api/v1/$endpoint");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . getenv('OTPLY_KEY'),
'X-API-Secret: ' . getenv('OTPLY_SECRET'),
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode($body),
CURLOPT_TIMEOUT => 30
]);
$out = json_decode(curl_exec($ch), true);
curl_close($ch);
return $out ?: ['success' => false, 'message' => 'Connection error'];
}
// otply('send-otp', ['email' => $email, 'purpose' => 'login']);
// otply('verify-otp', ['email' => $email, 'otp' => $code, 'purpose' => 'login']);
Don't want to write the integration yourself? Paste the prompt below into Claude (claude.ai โ the free tier works) or, best of all, Claude Code, which edits your repository directly. Any current Claude model handles it. Just fill in the three bracketed lines at the top with your project details first.
I want to add email OTP authentication to my project using OTPLY, an OTP-as-a-Service API. Please integrate it for me.
MY PROJECT:
- Language/framework: [e.g. Node.js + Express / PHP / Python + Django / Laravel / Next.js]
- Flow I need: [signup email verification / 2FA login / password reset / action confirmation]
- Where to add it: [describe your existing auth files, or paste them]
OTPLY API SPEC:
- Base URL: https://api.otply.xyz:8246/api/v1
- Auth: every request needs headers X-API-Key and X-API-Secret (I have these; load them from environment variables OTPLY_KEY and OTPLY_SECRET - never hardcode them, never expose them in frontend/browser code; all OTPLY calls must go through my backend)
- Send a code: POST /send-otp with JSON {"email": "...", "purpose": "...", "expiry_minutes": 10}
-> 200 {"success": true, "message": "...", "expires_in": 600}
- Verify a code: POST /verify-otp with JSON {"email": "...", "otp": "123456", "purpose": "..."}
-> 200 {"success": true} if correct, or 200 {"success": false, "message": "Invalid OTP. 2 attempt(s) remaining."} if wrong - branch on the "success" field, NOT the HTTP status
- Rules: codes are 6 digits, single-use, max 3 wrong attempts, default 10-minute expiry; the same recipient can only be sent a new code every 30 seconds (HTTP 429 with wait time - show a resend countdown in the UI); use a distinct "purpose" string per flow (e.g. "signup", "login", "password-reset") - verification must use the same purpose as the send
- Errors: 401 = bad credentials, 403 = monthly trial quota exhausted (10 free/month; subscribers unlimited), 422 = invalid input, 429 = resend cooldown, 500 = retry later
- Full docs: https://otply.xyz/docs.html
WHAT I WANT YOU TO BUILD:
1. Backend endpoints in my framework: one to request an OTP, one to verify it, wired into my chosen flow (e.g. only mark the user verified / issue the session after successful verification)
2. A simple frontend piece: email entry -> 6-digit code entry screen with a resend button that respects the 30-second cooldown, and clear error messages for wrong code / attempts exceeded / cooldown
3. Proper handling of every error case listed above
4. Tell me exactly which files you created/changed and what environment variables I must set
| Endpoint | Method | Purpose |
|---|---|---|
/account | GET | Plan, trial quota remaining, usage stats |
/checkout | POST | Subscribe โ body {"billing_cycle":"monthly"} or "yearly", returns payment URL |
/billing-portal | GET | Manage payment method, invoices, cancellation |
/subscription/cancel | POST | Stop auto-renewal (access until period end) |
/health | GET | Service status โ no auth, use for monitoring |
| HTTP | Meaning | What to do |
|---|---|---|
401 | Bad API key/secret | Check headers against your dashboard |
403 | Trial quota exhausted | Subscribe for unlimited, or wait for next month's refill |
422 | Invalid input | Fix email format / OTP must be 6 digits / expiry 1โ60 |
429 | Resend cooldown (30 s per recipient) | Wait the indicated seconds; show a countdown in your UI |
500 | Delivery or internal failure | Retry shortly โ trial quota is auto-refunded on failed sends |
purpose values per flow so codes can't be replayed across flows