Optora
Optora / Overview

Optora API v2

A lightweight, production-ready OTP email verification API. Send one-time codes or magic links, verify them in one step, and get notified via webhooks — all over a simple REST interface.

No API key required Self-hosted Rate limited Webhook ready
2
Verification modes
5
API endpoints
<1s
Email delivery

How it works

Code flow
1POST to /api/otp/generate with an email
2User receives a 6-digit code
3POST code to /api/otp/verify
Link flow
1POST to /api/otp/send-link with an email
2User clicks the link in their inbox
3Optora verifies and fires your webhook

Authentication

Optora is a self-hosted API — no API keys or tokens are required on requests. Secure it at the network or proxy level (firewall, reverse proxy, allowlist) for production use.

Because there is no authentication layer, do not expose this API directly to the public internet in production. Call it only from your backend server.

Base URL

All endpoints are relative to your deployment URL. The panel below shows your current deployment's base URL, detected automatically.

Your deployment
detecting...

API Playground

Test all endpoints live from your browser. Requests go directly to this deployment — real emails will be sent.

Interactive Tester
Pick a tab below, fill in the fields and hit send.
sends a real email

Enter the email and code that was sent in the Send Code tab above.

Paste the requestId returned from Send Code or Send Link.

Fires 6 rapid requests to the same email — the last few should return 429 Too Many Requests.

Send Verification Code

Generates a one-time code and emails it to the user. Use the returned requestId to check verification status later.

POST /api/otp/generate Send a numeric or alphanumeric code via email

Request body

FieldTypeRequiredDescription
emailstringrequiredRecipient email address
organizationstringoptionalShown in the email as the sender name. Default: "Verification"
subjectstringoptionalEmail subject line. Default: "Your verification code"
type"numeric" | "alpha" | "alphanumeric"optionalOTP character set. Default: "numeric"

Example request

bash
curl -X POST https://your-app.vercel.app/api/otp/generate \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "organization": "Acme Inc",
    "type": "numeric",
    "subject": "Your verification code"
  }'

Response

json
{
  "message": "Verification code sent to your email",
  "mode": "code",
  "requestId": "b7e3a2f1c4d8e9a0b1c2d3e4",
  "validityMinutes": 5
}
Try it live sends a real email

Emails a one-click verification link. When clicked, the link verifies the token server-side and optionally fires a webhook to your backend.

POST /api/otp/send-link Email a one-click magic link

Request body

FieldTypeRequiredDescription
emailstringrequiredRecipient email address
organizationstringoptionalSender name shown in the email. Default: "Verification"
subjectstringoptionalEmail subject line. Default: "Verify your email"
webhookUrlstringoptionalURL to POST to after the user clicks the link. Must start with http:// or https://

Example request

bash
curl -X POST https://your-app.vercel.app/api/otp/send-link \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "organization": "Acme Inc",
    "webhookUrl": "https://your-server.com/hooks/verified"
  }'

Response

json
{
  "message": "Verification link sent to your email",
  "mode": "link",
  "requestId": "c9f4b3e2d5a6b7c8d9e0f1a2",
  "validityMinutes": 5,
  "webhookRegistered": true
}
Try it live sends a real email

Verify Code

Validates an OTP submitted by the user. Returns success if the code matches and has not expired.

POST /api/otp/verify Validate a submitted OTP code

Request body

FieldTypeRequiredDescription
emailstringrequiredThe email address used when generating the OTP
otpstring | numberrequiredThe code entered by the user

Example request

bash
curl -X POST https://your-app.vercel.app/api/otp/verify \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "otp": "482910"}'
Try it live

This endpoint is called automatically when the user clicks the link in their email. You do not need to call it from your backend.

GET /api/otp/verify-link Verify a magic link token

Query parameters

ParamTypeRequiredDescription
tokenstringrequiredThe verification token included in the magic link
This endpoint returns an HTML page (the verification result page), not JSON. It is visited by the user's browser, not called by your server.
Try it live

Check Status

Poll the verification status of a request by its ID. Useful as a reliable fallback when webhooks are not available.

GET /api/otp/status/:requestId Poll verification status

Path parameters

ParamTypeRequiredDescription
requestIdstringrequiredID returned by /otp/generate or /otp/send-link

Response

json
{
  "requestId": "b7e3a2f1c4d8e9a0",
  "email": "user@example.com",
  "verified": true,
  "verifiedAt": "2026-05-28T10:30:00.000Z",
  "expiresAt": "2026-05-28T10:35:00.000Z"
}
Try it live

Rate Limiting

Requests are rate-limited per IP and email address. Configure thresholds with environment variables.

SettingDefaultDescription
Max requests5Per IP or email per window. Set via RATE_LIMIT_MAX_REQUESTS
Window15 minRolling window duration. Set via RATE_LIMIT_WINDOW_MINUTES
Response429Includes retryAfterSeconds in the JSON body
Test Rate Limiting fires 6 rapid requests

Webhooks

Optora can notify your server when a magic-link verification completes. Pass webhookUrl in the /otp/send-link request.

Payload delivered to your server

json
{
  "event": "email.verified",
  "email": "user@example.com",
  "requestId": "b7e3a2f1...",
  "verifiedAt": "2026-05-28T10:30:00.000Z"
}
  • Fire-and-forget — the confirmation page does not wait for your server to respond
  • 5-second timeout. Slow or unreachable URLs are aborted and logged server-side
  • Only http:// and https:// URLs are accepted
  • No automatic retries — use GET /api/otp/status/:requestId as a reliable fallback
Test Webhook Use requestbin.com for testing

Environment Variables

All variables live in sample.env. Copy it to .env for local development. For Vercel, add them under Settings → Environment Variables.

VariableRequiredDefaultDescription
MONGODB_URIrequiredMongoDB connection string
GMAIL_USERrequiredSender Gmail address. Must have an App Password enabled
GMAIL_PASSrequired16-character Gmail App Password
OTP_VALIDITY_PERIOD_MINUTESrequired5How long a code or link token stays valid
OTP_SIZErequired6Number of characters in the OTP
APP_BASE_URLoptionalautoPublic URL for magic links. Falls back to $VERCEL_URL then request host
DOCS_API_URLoptionalautoBase URL used by live Try It panels on the docs page. Defaults to same origin.
RATE_LIMIT_MAX_REQUESTSoptional5Max requests per window per IP or email
RATE_LIMIT_WINDOW_MINUTESoptional15Length of the rate-limit rolling window in minutes
BLOCK_KEYWORDS_RULESoptionalComma-separated keywords that auto-reject requests
ALLOWED_DOMAINSoptionalComma-separated domain allow-list. Empty = all accepted
PORTlocal only5001Dev server port. Vercel ignores this

Error Reference

All errors return { "error": "message" }.

StatusErrorCause
400Invalid emailMissing or malformed email field
400Spam detectedIP or email is blocklisted, or body contains a blocked keyword
400Maximum attempts reachedSame email hit the 3-attempt limit within the validity window
400Invalid OTPCode is wrong, expired, or already used
400Verification link is invalid or has expiredNo matching token found, or token has expired
429Too many requestsRate limit exceeded. Check retryAfterSeconds in the response
500Internal server errorUnexpected database or runtime error

Optora

MIT License · GitHub