<Callout variant="accent">
  <p><span className="text-saffron font-medium">Who this is for:</span> MiniMoth is designed for indie developers, startups, and anyone building for Indian users who needs WhatsApp/SMS OTP without the overhead of a WhatsApp Business Account, DLT registration, or session management.</p>
  <p className="mt-2"><span className="text-saffron font-medium">Delivery:</span> Every OTP is sent over WhatsApp first, falling back to SMS automatically if WhatsApp delivery fails. Both channels run on MiniMoth's own infrastructure — a verified WhatsApp Business Account and a TRAI DLT-registered SMS sender — so you never set up a WABA, register a sender ID, or manage compliance for either one. You get one simple API; MiniMoth handles both channels behind it.</p>
</Callout>

<div className="my-8">
<Section label="How it works">
  <FlowSteps steps={[
    { marker: '1', title: 'Your server calls <code class="text-saffron font-mono">POST /v1/otp/send</code>', description: 'MiniMoth generates a 6-digit code and delivers it via WhatsApp first, falling back to SMS automatically if WhatsApp fails. You are charged ₹0.35 at this point.' },
    { marker: '~', title: 'Optionally check delivery status', description: 'Call <code class="font-mono">GET /v1/otp/status/:otp_id</code> using the <code class="font-mono">otp_id</code> from step 1. Tells you whether the code landed on WhatsApp or SMS. Useful for showing delivery confirmation in your UI before the user enters the code.', tone: 'muted' },
    { marker: '2', title: 'User enters the code in your app', description: 'You collect the 6-digit code from your UI and send it to your server.' },
    { marker: '3', title: 'Your server calls <code class="text-saffron font-mono">POST /v1/otp/verify</code>', description: 'MiniMoth checks the code and returns an <code class="font-mono text-ink/70">access_token</code> and <code class="font-mono text-ink/70">refresh_token</code> on success.' },
    { marker: '✓', title: 'Phone number verified — user is authenticated', description: 'Use the tokens to validate every subsequent request, or bring your own session system. See <a href="/docs/sessions" class="text-saffron hover:underline">Sessions</a> and <a href="/docs/recipes" class="text-saffron hover:underline">Recipes</a>.', tone: 'final' },
  ]} />
</Section>
</div>

## 1. Get an API key

Create a project in the [dashboard](https://app.minimoth.dev/register). Every project gets an API key in the format `mm_live_...`, shown once on creation and available any time under Project Settings.

<h2 id="send-an-otp" className="font-medium text-xl mt-10 mb-3">2. Send and verify an OTP with the Node.js SDK</h2>

[@minimoth/sdk-node](https://www.npmjs.com/package/@minimoth/sdk-node) is the fastest way in — it handles phone normalisation, token management, and auto-refresh, so there's no boilerplate around the raw API.

```bash
npm install @minimoth/sdk-node
```
<div className="mt-4">
  ```typescript
import { MiniMoth } from '@minimoth/sdk-node'

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

// 1. Send OTP — delivered via WhatsApp first, falls back to SMS automatically
const { otpId } = await mm.otp.send({ phone: '+919876543210' })

// 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' | ...
} else {
  // Phone is verified — result.accessToken, result.refreshToken, result.sessionId
}
```
</div>

**Accepted phone formats:** `+91XXXXXXXXXX` or bare 10 digits `XXXXXXXXXX`. The 10-digit number must start with 6–9.

`accessToken` is valid for 5 minutes. `refreshToken` is valid for your project's configured refresh window (default 10 days, max 30 days). See [the full Node.js SDK guide →](/docs/sdk-nodejs) for auto-refresh, session validation, and framework examples.

## Using another language, or calling the API directly

Not on Node.js, or want to talk to the REST API yourself? Two endpoints, no SDK required:

<h3 id="send-an-otp-raw" className="font-medium text-base mt-6 mb-2 text-ink/80">Send an OTP</h3>

```javascript
const res = 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' }),
})
const { otp_id } = await res.json()
// otp_id can be used with GET /v1/otp/status/:otp_id to check delivery
```

The response includes an `otp_id` that can be used to poll delivery status.

### Check delivery status (optional)

```javascript
// Optional — poll delivery status before showing "OTP sent" in your UI
const res = await fetch(`https://api.minimoth.dev/v1/otp/status/${otp_id}`, {
  headers: { 'X-Api-Key': 'mm_live_...' },
})
const status = await res.json()
// { otp_id, channel: 'whatsapp' | 'sms', status: 'queued' | 'delivered' | 'failed', delivered_at }
```

MiniMoth first tries WhatsApp; if that fails, SMS is sent automatically. The `channel` field tells you which was used. This step is optional — you can call `otp/verify` directly without checking status first.

### Verify the code

```javascript
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' }),
})
const { access_token, refresh_token, expires_at, identity_id } = await res.json()
// expires_at is the access_token expiry (ISO 8601)
// identity_id is stable across every future login from this same phone number
```

`access_token` is valid for 5 minutes. `refresh_token` is valid for your project's configured refresh window (default 10 days, max 30 days). See [Sessions](/docs/sessions) for how to refresh and validate tokens, and [Identity](/docs/identity) for what `identity_id` is used for.

<div className="mt-8">
  <Callout>
    <p><span className="text-ink font-medium">Just need OTP delivery?</span> You don't have to use MiniMoth's session tokens at all. If you already have your own auth system, call <code className="text-saffron font-mono text-xs">otp/send</code> + <code className="text-saffron font-mono text-xs">otp/verify</code> to confirm the phone number, then create a session in your own system. See the <a href="/docs/recipes#otp-only" className="text-saffron hover:underline">OTP-only recipe</a>.</p>
  </Callout>
</div>

---

Full documentation index: https://minimoth.dev/llms.txt
