Docs / Overview

Optora API

Lightweight OTP email verification with code-based and one-click magic-link flows. Built on Node.js, Express, and MongoDB. Vercel-ready.

Spam-protected Rate limited Webhook support Vercel-ready
5
API endpoints
2
Verification modes
<1s
Typical latency

Overview

Optora provides two verification flows. Choose the one that fits your product:

Code Mode
1POST to /api/otp/generate with email
2User receives a 6-digit code in their inbox
3POST to /api/otp/verify with email + code
Magic Link Mode
1POST to /api/otp/send-link with email
2User clicks the one-click link in their inbox
3Your server receives a webhook notification

Authentication

Optora uses no API keys by default — it is designed to run as your own private backend. Protect your endpoints with your own authentication layer (middleware, API gateway, etc.) before deploying publicly.

All endpoints should only be called from your backend, not directly from a browser or mobile client, to prevent abuse.

Quick Start

Base URL — detected automatically from the request host. Override with DOCS_API_URL:

url
https://your-app.vercel.app

Send a verification code:

bash
curl -X POST https://your-app.vercel.app/api/otp/generate \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com"}'
Quick Test Playground
Try the API in real-time — sends actual emails

Generate OTP

Generates a one-time code and emails it to the user. If an unexpired, unverified OTP exists for the email, it is reused (up to 3 attempts).

POST /api/otp/generate Generate and email a verification code

Request body

FieldTypeRequiredDescription
emailstringrequiredRecipient email address
typestringoptionalnumeric, alphanumeric, or alphabet. Default: "numeric"
organizationstringoptionalSender name in the email. Default: "Verification"
subjectstringoptionalEmail subject line. Default: "Your verification code"

Example request

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

Response

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

Emails a one-click verification link. When the user clicks the link, their browser opens a confirmation page and — if configured — your server receives a webhook.

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 livesends 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

Called automatically when the user clicks the link in their email. Returns an HTML confirmation page — you do not need to call this 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, 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 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-31T10:30:00.000Z",
  "expiresAt": "2026-05-31T10: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 Limitingfires 6 rapid requests

Webhooks

Optora notifies 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-31T10: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

Environment Variables

Copy sample.env 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_MINUTESoptional5How long a code or link token stays valid
OTP_SIZEoptional6Number 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
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 only5000Dev 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 server error. Check logs