minimoth
← Blog

MiniMoth as a Phone Verification API — No Sessions

3 August 2026

A question we hear from developers evaluating MiniMoth: “We already have our own auth system — sessions, JWTs, the works. Do we have to switch to MiniMoth’s session model just to get phone OTP?”

Short answer: MiniMoth works fine as a standalone phone verification API. Sending and verifying the code is one job; issuing a session is a separate, optional one — you can use the first without ever touching the second.

Two different jobs, bundled by default

MiniMoth does two things: it delivers and verifies a one-time code, and it can optionally hand you a session (an access_token and refresh_token) once that code checks out. Most of the docs walk through both together, because most developers building something new want both. But they’re not the same job, and nothing forces you to take the second one.

If you already have users in a database, your own JWTs, or an auth provider you’re not looking to replace, you can call MiniMoth for exactly one thing — confirming that a phone number is real and reachable — and throw away everything else it hands back. (Same underlying idea as why your OTP doesn’t need your brand name: MiniMoth is a layer underneath your product, not a replacement for parts of it you already own.)

Phone verification in code

Using the Node.js SDK

import { MiniMoth } from '@minimoth/sdk-node'

const mm = new MiniMoth({ apiKey: process.env.MINIMOTH_API_KEY })

// Step 1: send the OTP
await mm.otp.send({ phone: '+919876543210' })

// Step 2: verify the code — never throws, always returns a result
const result = await mm.otp.verify({ phone: '+919876543210', otp: '123456' })
if (!result.valid) {
  // result.code: 'INVALID_OTP' | 'OTP_NOT_FOUND' | 'VERIFY_RATE_LIMITED' | ...
  throw new Error(result.code)
}

// result.accessToken and result.refreshToken are right there, but you don't have to touch them.
// result.valid === true is your answer: this phone number is confirmed.
// Create your own session however you already do it — a row in your own table, a cookie, your own JWT.

Not on Node.js, or want to call the REST API directly

// Step 1: send the OTP
await fetch('https://api.minimoth.dev/v1/otp/send', {
  method: 'POST',
  headers: { 'X-Api-Key': 'mm_live_...', 'Content-Type': 'application/json' },
  body: JSON.stringify({ phone: '+919876543210' }),
})

// Step 2: verify the code
const res = await fetch('https://api.minimoth.dev/v1/otp/verify', {
  method: 'POST',
  headers: { 'X-Api-Key': 'mm_live_...', 'Content-Type': 'application/json' },
  body: JSON.stringify({ phone: '+919876543210', code: '123456' }),
})

if (!res.ok) {
  // wrong code, too many attempts, or OTP expired
  throw new Error('OTP verification failed')
}

// access_token and refresh_token are right there in the response, but you don't have to touch them.
// A 2xx here is your answer: this phone number is confirmed.
// Create your own session however you already do it — a row in your own table, a cookie, your own JWT.

What you get: a confirmed phone number. What you own: everything after that — sessions, cookies, tokens, logout.

Why this is a legitimate way to use it, not a workaround

Billing is per OTP sent, not per session created — so there’s no cost difference between using MiniMoth’s sessions and ignoring them entirely. And DLT compliance, WhatsApp-first delivery, and the ₹0.35 rate all apply the same way regardless of whether you ever look at the token fields in the response.

This is also the right call, not just an allowed one, if you need session behavior MiniMoth doesn’t try to support — multi-device sessions, custom claims baked into your own tokens, or an auth system you’re already deep into and have no reason to migrate off. MiniMoth’s session layer is deliberately simple (one access token, one refresh token, a 5-minute/configurable-day split) because it’s built for developers who don’t already have something better. If you do, use that instead — and let MiniMoth just be the part that gets a code onto a phone and confirms it came back correctly.

See the OTP-only recipe for the full pattern, the Node.js SDK docs for everything mm.otp exposes, or the quickstart if you’re starting from scratch either way.