How to Send OTP with Firebase Functions (2026)
Firebase Functions OTP tutorial using StartMessaging. Callable functions, Firestore for rate-limit, Firebase Auth custom-token issuance after OTP verification.
StartMessaging Team
Engineering
Firebase is convenient for app backends, but Firebase Phone Auth is not always the right pick for India. This tutorial replaces it with custom OTP via StartMessaging while keeping Firebase Auth for session management.
Why Not Firebase Phone Auth?
See our deep dive on Firebase Auth vs custom OTP.
Setup
firebase init functions
cd functions
npm install firebase-admin firebase-functionsCallable Functions
// functions/src/index.ts
import { initializeApp } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';
import { onCall, HttpsError } from 'firebase-functions/v2/https';
import { defineSecret } from 'firebase-functions/params';
import { randomUUID } from 'node:crypto';
initializeApp();
const SM_KEY = defineSecret('SM_API_KEY');
export const sendOtp = onCall({ secrets: [SM_KEY] }, async (req) => {
const { phoneNumber } = req.data;
const r = await fetch('https://api.startmessaging.com/otp/send', {
method: 'POST',
headers: { 'X-API-Key': SM_KEY.value(), 'Content-Type': 'application/json' },
body: JSON.stringify({ phoneNumber, idempotencyKey: randomUUID() }),
});
if (!r.ok) throw new HttpsError('unavailable', 'OTP send failed');
return (await r.json()).data;
});
export const verifyOtp = onCall({ secrets: [SM_KEY] }, async (req) => {
const { requestId, otpCode, phoneNumber } = req.data;
const r = await fetch('https://api.startmessaging.com/otp/verify', {
method: 'POST',
headers: { 'X-API-Key': SM_KEY.value(), 'Content-Type': 'application/json' },
body: JSON.stringify({ requestId, otpCode }),
});
if (!r.ok) throw new HttpsError('unauthenticated', 'invalid OTP');
// mint Firebase Auth custom token
const uid = `phone:${phoneNumber}`;
const token = await getAuth().createCustomToken(uid, { phoneNumber });
return { token };
});Rate Limiting via Firestore
Use a Firestore document per phone with TTL field for hourly limits. Increment with FieldValue.increment(1).
Firebase Auth Custom Token
After successful verify, mint a custom token. Client signs in with signInWithCustomToken — and you have a Firebase Auth identity backed by your own OTP flow.
FAQ
Same idea on Cloud Run? See our Cloud Run guide.
Related Articles
When should you use Firebase phone auth versus a custom OTP API? Compare vendor lock-in, pricing, customization, India delivery, and data control.
Cloud Run OTP tutorial using StartMessaging. Containerised Node service, Secret Manager for keys, Memorystore for rate limit, deployed via gcloud.
Step-by-step Node.js tutorial to send and verify OTP via SMS using the StartMessaging API. Includes fetch examples, error handling, and verification flow.
Ready to Send OTPs?
Integrate StartMessaging in 5 minutes. No DLT registration required.