How to Send OTP with Google Cloud Run (2026)
Cloud Run OTP tutorial using StartMessaging. Containerised Node service, Secret Manager for keys, Memorystore for rate limit, deployed via gcloud.
StartMessaging Team
Engineering
Cloud Run is a tidy target for containerised Indian SaaS. This tutorial wires StartMessaging via a small Node container.
Overview
- Containerise a Node app with /send + /verify routes.
- Pull SM_API_KEY from Secret Manager at startup.
- Per-phone hourly cap in Memorystore (Redis).
- Deploy with
gcloud run deploy.
Cloud Run Service
# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV PORT=8080
CMD ["node", "src/index.js"]// src/index.ts
import express from 'express';
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
import { randomUUID } from 'node:crypto';
const sm = new SecretManagerServiceClient();
let apiKey: string | null = null;
async function getKey() {
if (apiKey) return apiKey;
const [v] = await sm.accessSecretVersion({ name: process.env.SM_API_KEY_REF! });
apiKey = v.payload!.data!.toString();
return apiKey;
}
const app = express();
app.use(express.json());
app.post('/auth/send-otp', async (req, res) => {
const k = await getKey();
const r = await fetch('https://api.startmessaging.com/otp/send', {
method: 'POST',
headers: { 'X-API-Key': k, 'Content-Type': 'application/json' },
body: JSON.stringify({ phoneNumber: req.body.phoneNumber, idempotencyKey: randomUUID() }),
});
res.status(r.status).send(await r.text());
});
app.post('/auth/verify-otp', async (req, res) => {
const k = await getKey();
const r = await fetch('https://api.startmessaging.com/otp/verify', {
method: 'POST',
headers: { 'X-API-Key': k, 'Content-Type': 'application/json' },
body: JSON.stringify(req.body),
});
res.status(r.status).send(await r.text());
});
app.listen(Number(process.env.PORT) || 8080);Secret Manager
gcloud secrets create sm-api-key --replication-policy=automatic
echo -n 'sm_live_xxx' | gcloud secrets versions add sm-api-key --data-file=-Memorystore Rate Limit
Spin a small Memorystore Redis; per-phone INCR with EXPIRE 3600.
Deploy with gcloud
gcloud run deploy otp-api --source . \
--set-env-vars SM_API_KEY_REF=projects/<id>/secrets/sm-api-key/versions/latest \
--service-account otp-runtime@<id>.iam.gserviceaccount.comFAQ
Lambda equivalent in our AWS Lambda guide.
Related Articles
Firebase Functions OTP tutorial using StartMessaging. Callable functions, Firestore for rate-limit, Firebase Auth custom-token issuance after OTP verification.
AWS Lambda OTP tutorial using StartMessaging. Function URLs, Secrets Manager for API keys, DynamoDB for rate limits, and SAM/CDK deployment patterns.
Vercel Functions OTP tutorial using StartMessaging. Edge vs Node runtime trade-offs, environment variables, signed cookies, and KV-style rate limiting.
Ready to Send OTPs?
Integrate StartMessaging in 5 minutes. No DLT registration required.