Docs / Use cases / OTP email
Use case

OTP email API

Send one-time passcodes (OTP) for login, transaction confirmation, and step-up authentication with a single Mailbot API call.

Answer first: to send an OTP email, POST https://api.mailbot.id/v1/send with your Bearer key and a JSON body where subject states the code's purpose and text/html contains the generated code. Generate and store the code (with a short expiry) in your own backend — Mailbot delivers the message, it does not generate or verify codes.

Use case

An OTP (one-time passcode) email delivers a short numeric or alphanumeric code that the user types back into your app to prove control of their email address. Common for passwordless login, two-factor authentication, and confirming sensitive actions like payments or profile changes.

Your backend owns the security logic: generate the code, hash and store it with an expiry and attempt limit, and verify it on submission. Mailbot accepts the email payload and returns a message id you can log with the OTP request.

When to send

  • A user requests a login code or one-time sign-in link.
  • A second factor is required during authentication (2FA/step-up).
  • A sensitive action needs confirmation (payout, password change, new device).
Rate-limit on your side. Throttle OTP requests per user and per IP, set a short code expiry (for example 5–10 minutes), and cap verification attempts to limit abuse and cost.

API call

cURL
curl https://api.mailbot.id/v1/send \
  -H "Authorization: Bearer $MAILBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "noreply@yourdomain.com",
    "to": "user@example.com",
    "subject": "Your login code: 884921",
    "text": "Your one-time code is 884921. It expires in 10 minutes.",
    "idempotency_key": "otp-user-142-1718000000"
  }'
Node.js (fetch)
const code = generateOtp();            // your code generator
await storeOtp(user.id, code, 600);    // hash + store, 10-min expiry

const res = await fetch("https://api.mailbot.id/v1/send", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.MAILBOT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "noreply@yourdomain.com",
    to: user.email,
    subject: `Your login code: ${code}`,
    text: `Your one-time code is ${code}. It expires in 10 minutes.`,
    idempotency_key: `otp-${user.id}-${loginAttemptId}`,
  }),
});
if (!res.ok) throw new Error("OTP email failed");

Required fields

FieldRequiredNotes
toYesThe user's email address.
subjectYesState the purpose; including the code can improve UX but exposes it in notifications.
text or htmlYesThe body containing the code and its expiry.
fromNoVerified sender; defaults to the configured sender if omitted.
idempotency_keyRecommendedTie to the login attempt so retries don't double-send.

Example payload

JSON request
{
  "from": "noreply@yourdomain.com",
  "to": "user@example.com",
  "subject": "Your login code: 884921",
  "text": "Your one-time code is 884921. It expires in 10 minutes. If you didn't request this, ignore this email.",
  "html": "<p>Your one-time code is <strong>884921</strong>.</p><p>It expires in 10 minutes.</p>",
  "idempotency_key": "otp-user-142-1718000000"
}

Example response

202 · queued
{
  "ok": true,
  "id": "msg_3f8c1a...",
  "status": "queued"
}

For delivered messages, the response can include status: "sent" and a delivery_id. See the API reference for every response shape.

Errors

StatusMeaningWhat to do
400Invalid payload (bad to, missing subject/body).Fix the request; read details.
401Bad or missing API key.Check the Bearer header.
429Send limit reached.Back off; surface a "try again later" message to the user.
502 / 503Mailbot is temporarily unable to accept the send.Retry with backoff using the same idempotency_key.

Full error handling guidance is in the integration guide.

Testing

For safe setup, use the standard test endpoint to confirm delivery reaches an address you control:

cURL · test email
curl https://api.mailbot.id/v1/test-email \
  -H "Authorization: Bearer $MAILBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "to": "safe@yourdomain.com", "label": "OTP test" }'

Production checklist

  • Codes generated, hashed, stored with a short expiry, and attempt-capped in your backend.
  • OTP requests throttled per user and per IP.
  • idempotency_key tied to the login attempt.
  • Verified sender domain; from set to a verified address.
  • API key kept server-side; never sent to the browser or mobile client.
  • 429/5xx handled with backoff and a clear user message.