Guide Next.js
Email, SMS et WhatsApp depuis Next.js,
en une seule fonction serveur.
Un fichier helper, appelé depuis une Server Action ou un Route Handler, envoie l'email de bienvenue, le SMS de commande ou le rappel WhatsApp depuis votre boîte mail, votre téléphone et votre numéro. Pas de code SMTP, pas de frais par message, et la clé n'atteint jamais le navigateur.
Pressé ? Résumez cet article avec une IA
La réponse courte
Comment envoyer email, SMS et WhatsApp depuis Next.js gratuitement ?
Placez un helper côté serveur dans lib/notify.ts qui appelle POST /message sur Unlimited Messaging, puis appelez-le depuis une Server Action ou un Route Handler. La même requête envoie l'email depuis votre boîte mail, le SMS depuis votre téléphone Android et le WhatsApp depuis votre numéro, et le plan Free ne facture rien au message.
Le montage habituel, c'est un service par canal. Nodemailer est gratuit mais ne parle que SMTP, exige le runtime Node.js et vous laisse au plafond de votre boîte, 500 emails par jour sur Gmail. Une API email comme Resend est gratuite jusqu'à 3 000 emails par mois et 100 par jour. Le SMS via Twilio est facturé au segment après l'essai, qui ne joint que 5 numéros vérifiés. WhatsApp fait une quatrième intégration.
Next.js vous donne déjà les deux points d'entrée serveur nécessaires. Une Server Action gère vos formulaires (inscription, paiement), un Route Handler sert les autres appelants (une tâche cron, un worker, un autre service). Les deux tournent sur le serveur, où les variables d'environnement sans préfixe NEXT_PUBLIC_ restent hors du bundle navigateur.
Depuis Next.js 15.1, after() est stable : il exécute du code une fois la réponse envoyée, l'utilisateur n'attend donc jamais le départ d'un email ou d'un SMS.
À lire aussi : le SDK TypeScript en détail, et connecter une boîte Gmail avec un mot de passe d'application.

Pas à pas
Comment l'ajouter à une app Next.js, étape par étape ?
Cinq étapes, App Router, environ 10 minutes. Il vous faut un compte Unlimited Messaging gratuit avec au moins un canal connecté.
Gardez la clé côté serveur
Connectez votre boîte mail, votre numéro WhatsApp ou l'application Android dans Unlimited Messaging, créez une clé API et placez-la dans .env.local sans préfixe NEXT_PUBLIC_. Next.js n'intègre au bundle navigateur que les variables NEXT_PUBLIC_ ; les autres restent sur le serveur.
.env.local.env.local# .env.local (gitignored by create-next-app) # No NEXT_PUBLIC_ prefix: the key stays on the server UM_API_KEY=um_live_xxxx # Only for the internal route handler of step 4 INTERNAL_SECRET=change-me-to-a-long-random-stringÉcrivez un helper réservé au serveur
lib/notify.ts enveloppe POST /message. import 'server-only' fait échouer le build si un composant client l'importe, la clé ne peut donc pas fuiter par erreur. Il retire les canaux sans destinataire et marque le message comme transactionnel.
lib/notify.tsTypeScript// lib/notify.ts import 'server-only'; // the build fails if a client component imports this file export type Channel = 'EMAIL' | 'WHATSAPP' | 'SMS'; export async function notify(input: { email?: string; phone?: string; // E.164, e.g. +33612345678 channels: Channel[]; subject: string; text: string; }) { // Drop the channels that have no destination const channels = input.channels .filter((c) => (c === 'EMAIL' ? input.email : input.phone)) .map((channel) => ({ channel })); if (channels.length === 0) return null; const res = await fetch('https://api.unlimitedmessaging.app/message', { method: 'POST', headers: { Authorization: `Bearer ${process.env.UM_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ to: { email: input.email, phone: input.phone }, channels, subject: input.subject, text: input.text, priority: 'transactional', }), }); if (!res.ok) throw new Error(`Unlimited Messaging ${res.status}: ${await res.text()}`); return res.json(); // { dispatchId, messages: [...] } }Appelez-le depuis une Server Action
À l'inscription, envoyez l'email et le WhatsApp de bienvenue dans after() : le formulaire répond tout de suite et le message part juste après. Une Server Action est accessible par n'importe quel POST : validez les entrées et limitez le débit comme pour un endpoint public.
app/signup/actions.tsServer Action// app/signup/actions.ts 'use server'; import { after } from 'next/server'; import { notify } from '@/lib/notify'; export async function signUp(formData: FormData) { const email = String(formData.get('email')); const phone = String(formData.get('phone') ?? '') || undefined; // ...validate the input, rate limit, create the user // Runs after the response is sent: the form never waits for the message after(() => notify({ email, phone, channels: ['EMAIL', 'WHATSAPP'], subject: 'Welcome to Acme', text: 'Your account is ready. Reply to this message if you need help.', }).catch(console.error), ); }Ou depuis un Route Handler
Pour les appelants hors de vos formulaires, une tâche cron, un worker, un autre service, exposez un Route Handler protégé par un secret partagé. Ici il envoie un email et un SMS depuis votre téléphone Android.
app/api/notify/route.tsRoute Handler// app/api/notify/route.ts import { notify } from '@/lib/notify'; export async function POST(req: Request) { // For your own jobs and services only, never a public endpoint if (req.headers.get('authorization') !== `Bearer ${process.env.INTERNAL_SECRET}`) { return new Response('Unauthorized', { status: 401 }); } const { email, phone, subject, text } = await req.json(); const result = await notify({ email, phone, channels: ['EMAIL', 'SMS'], subject, text }); return Response.json(result); }Testez
Lancez next dev et appelez la route. La réponse liste un message par canal avec son propre statut, regroupés par un dispatchId, et les messages apparaissent dans votre boîte de réception Unlimited Messaging.
Terminalcurlcurl -X POST http://localhost:3000/api/notify \ -H "Authorization: Bearer $INTERNAL_SECRET" \ -H "Content-Type: application/json" \ -d '{"email":"[email protected]","phone":"+33612345678", "subject":"Order #1042 shipped","text":"Your order #1042 is on its way."}'
Vibe coding
Vous codez avec Cursor, v0 ou Claude Code ? Collez ce prompt
Le prompt donne à votre éditeur IA le bon schéma : un helper réservé au serveur, la clé hors NEXT_PUBLIC_, after() pour la rapidité. Il vous demande vos contacts de test avant tout envoi.
Ajoute l'envoi de messages transactionnels à ce projet Next.js (App Router) avec Unlimited Messaging : email, SMS et WhatsApp envoyés depuis mes propres comptes.
1. Ajoute UM_API_KEY dans .env.local et .env.example. N'utilise jamais de préfixe NEXT_PUBLIC_ et ne lis jamais la clé dans un composant client.
2. Crée lib/notify.ts qui commence par import 'server-only'. Exporte notify({ email, phone, channels, subject, text }) qui envoie un POST https://api.unlimitedmessaging.app/message avec l'en-tête Authorization: Bearer UM_API_KEY et le JSON { to: { email, phone }, channels: [{ channel: 'EMAIL' | 'WHATSAPP' | 'SMS' }], subject, text, priority: 'transactional' }. Ignore les canaux sans destinataire ; les numéros sont au format E.164.
3. Appelle notify depuis ma Server Action d'inscription dans after() de next/server, pour que le formulaire n'attende pas. Valide les entrées et garde les vérifications d'authentification existantes.
4. Ajoute un Route Handler app/api/notify/route.ts protégé par Authorization: Bearer INTERNAL_SECRET pour mes tâches cron.
5. Référence de l'API : https://docs.unlimitedmessaging.app. Je crée la clé API sur https://app.unlimitedmessaging.app.
Montre-moi le diff, puis demande-moi mon email et mon numéro de test avant d'envoyer quoi que ce soit.Les options
Nodemailer, Resend, Twilio ou une seule API : quoi choisir pour Next.js ?
Quatre façons courantes d'envoyer des messages transactionnels depuis Next.js, comparées sur ce qu'elles envoient, ce qui est gratuit et ce que vous maintenez.
| Nodemailer + GmailBibliothèque SMTP | ResendAPI email | TwilioAPI SMS et WhatsApp | Unlimited MessagingEmail, WhatsApp et SMS | |
|---|---|---|---|---|
| Canaux | SMS et WhatsApp, facturés au message | Email, WhatsApp et SMS en une requête | ||
| Gratuit | Bibliothèque gratuite, Gmail plafonne à 500 emails par jour | 3 000 emails par mois, 100 par jour | Essai : 5 numéros vérifiés au maximum | Plan Free sans frais par message |
| Runtime | Node.js uniquement (sockets SMTP) | Tout runtime (HTTP) | Tout runtime (HTTP) | Tout runtime (HTTP) |
| Expéditeur | Votre boîte mail | Votre domaine vérifié | Un numéro Twilio ou un expéditeur WhatsApp enregistré | Votre boîte mail, votre numéro WhatsApp et votre téléphone |
| À maintenir | Réglages SMTP, erreurs, nouvelles tentatives | Un SDK, des enregistrements DNS | Un SDK par produit, modèles Meta pour WhatsApp | Un seul fetch pour tous les canaux |
| Idéal pour | Quelques emails depuis votre boîte | L'email en gros volume | Le SMS en volume, le WhatsApp officiel | Les messages d'une app un par un, gratuit pendant la croissance |
Vérifié le 9 novembre 2026 sur nodemailer.com, resend.com/pricing, la documentation d'essai de Twilio et l'aide Gmail (liens dans les sources). Les offres gratuites changent : suivez les liens avant de construire dessus.
Les contreparties
Quelles sont les limites ?
Gratuit supprime la facture au message et le code SMTP. Pas la sécurité, les règles de WhatsApp ni le rythme d'un téléphone.
Une action publique peut être détournée
Une action d'inscription qui envoie des messages permet à n'importe qui de déclencher un WhatsApp ou un SMS vers n'importe quel numéro. Ajoutez un captcha ou une limite de débit, et n'écrivez à un numéro qu'une fois qu'il est fiable.
WhatsApp est non officiel, et le spam fait bannir les numéros
Votre numéro est lié comme un appareil, hors de la plateforme Business de Meta. Les messages demandés par l'utilisateur sont peu risqués ; des envois en masse à des inconnus peuvent faire bannir le numéro. Gardez WhatsApp pour le transactionnel.
Le SMS va au rythme d'un téléphone
Les SMS partent de la SIM de votre téléphone Android, sur votre forfait. Android demande une confirmation au-delà de 30 SMS en 30 minutes, l'application espace donc les envois à environ 30 toutes les 30 minutes, et le téléphone doit rester allumé.
L'email suit les limites de votre boîte mail
Les emails partent de votre boîte en SMTP, son plafond quotidien s'applique donc : 500 par jour sur un compte Gmail. Pour une newsletter, prenez un service d'emailing.
Les messages du plan Free portent une signature
Sur le plan Free, les messages se terminent par une courte ligne "Sent with UnlimitedMessaging.app", et les médias et les webhooks sont payants. Medium et Max retirent la ligne.
after() n'est pas une file d'attente
Si l'appel échoue après l'envoi de la réponse, rien ne le relance. Pour un reçu qu'il ne faut pas perdre, enregistrez la tâche en base ou dans une file et relancez depuis là.
FAQ
Questions sur l'envoi de messages depuis Next.js
Sources
Sources & références
Pour vérifier par vous-même les informations de cet article :
- Mutating Data (Server Functions et Server Actions)Next.js Docs
- after (stable depuis la v15.1.0)Next.js Docs
- How to use environment variables in Next.jsNext.js Docs
- Server and Client Components (server-only)Next.js Docs
- Nodemailernodemailer.com
- Pricing (Free : 3 000 emails par mois, 100 par jour)Resend
- How to use your free trial account (Messaging)Twilio Docs
- Limites d'envoi et de réception (500 emails par jour)Aide Gmail
- SmsUsageMonitor (seuil de 30 SMS en 30 minutes)Android Open Source Project
- Référence de l'API : POST /messageUnlimited Messaging Docs

Écrit par
Ilian · Senior Software Engineer
Ingénieur logiciel senior, il fonde et dirige plusieurs sociétés tech. Il conçoit des produits autour des API de messagerie, de l’automatisation et de l’intégration de l’IA, et partage ici ce qu’il apprend en construisant.
Ajoutez email, SMS et WhatsApp à votre app Next.js aujourd'hui.
Créez un compte gratuit, connectez une boîte mail, un numéro WhatsApp ou un téléphone Android, collez le helper. Sans carte bancaire, sans frais par message.