sdk.email.send envoie un e-mail depuis le code serveur de votre application : confirmation de
commande, notification de formulaire, lien d'invitation. L'envoi passe par le serveur SMTP
configuré pour votre application, et un appel correspond à un message.
Pour les newsletters et les envois à une liste de contacts, utilisez plutôt les campagnes décrites dans Email & MJML.
API
import { createSdk } from "@bext-stack/framework/sdk";
const sdk = createSdk("<app-id>");
await sdk.email.send({
to: ["client@example.com"],
subject: "Votre commande est confirmée",
html: "<p>Merci pour votre commande.</p>",
text: "Merci pour votre commande.",
});
Appelez-la côté serveur (loader, action, route.ts ou tâche), jamais depuis un island.
| Champ | Type | Description |
|---|---|---|
to |
string[] |
Destinataires, au moins un. Une adresse simple (a@b.com) ou avec un nom (Jeanne Martin <a@b.com>). |
subject |
string |
Objet du message. |
html |
string? |
Corps HTML. |
text |
string? |
Corps en texte brut. |
Il faut au moins html ou text. Avec les deux, le message part en version HTML avec une
alternative texte, et le client de messagerie choisit celle qu'il affiche.
send retourne true quand le serveur SMTP a accepté le message. En cas d'échec, l'appel lève une
Error qui porte le message d'erreur renvoyé par la plateforme.
Expéditeur et adresse de réponse
L'adresse d'expéditeur (From) ne fait pas partie du message : send n'a pas de champ from.
Elle vient de la configuration d'envoi de votre application, qui réunit le serveur SMTP, ses
identifiants, l'adresse et le nom d'expéditeur, et une adresse de réponse par défaut. Le client du
framework n'a pas d'appel pour lire ou modifier cette configuration. Une application sans
configuration d'envoi ne peut pas envoyer : chaque appel lève une erreur.
Si la configuration prévoit une adresse de réponse, elle s'applique à tous les messages. Pour la
remplacer sur un message (le formulaire de contact où la réponse doit aller au visiteur), ajoutez
le champ reply_to, présenté plus bas.
Champs supplémentaires : reply_to et attachments
Le client transmet l'objet du message tel quel, et la plateforme accepte deux champs que le type
BextSdk ne déclare pas :
| Champ | Type | Description |
|---|---|---|
reply_to |
string |
Adresse de réponse pour ce message. Remplace celle de la configuration. |
attachments |
tableau | Pièces jointes, chacune sous la forme { filename, content_type, content_base64 }. |
Pour chaque pièce jointe :
content_base64contient le fichier encodé en base64. Une pièce jointe sans ce champ est ignorée sans erreur ; un contenu qui n'est pas du base64 valide fait échouer l'envoi.filenamevautattachmentpar défaut.content_typevautapplication/octet-streampar défaut. Un type MIME mal formé fait échouer l'envoi.
Comme ces champs ne figurent pas dans le type, construisez le message dans une variable avant de
le passer à send : TypeScript refuserait un champ inconnu écrit directement dans l'appel.
const msg = {
to: ["compta@example.com"],
subject: "Facture F-2026-0142",
text: "Vous trouverez la facture en pièce jointe.",
reply_to: "facturation@example.com",
attachments: [
{
filename: "F-2026-0142.pdf",
content_type: "application/pdf",
content_base64: pdfBase64,
},
],
};
await sdk.email.send(msg);
Le base64 grossit le fichier d'environ un tiers, et tout le message voyage dans une seule requête : gardez les pièces jointes légères, et envoyez plutôt un lien pour un fichier volumineux.
Exemple : formulaire de contact
// src/app/api/contact/route.ts
import { json } from "@bext-stack/framework";
import { createSdk } from "@bext-stack/framework/sdk";
const sdk = createSdk("<app-id>");
const esc = (s: string) =>
s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
export async function POST(request: Request) {
const { name, email, message } = await request.json();
const msg = {
to: ["contact@example.com"],
subject: `Demande de contact : ${name}`,
text: `${name} <${email}>\n\n${message}`,
html: `<p><strong>${esc(name)}</strong> (${esc(email)})</p><p>${esc(message)}</p>`,
reply_to: email,
};
try {
await sdk.email.send(msg);
return json({ ok: true });
} catch (e) {
return json({ ok: false, error: String(e) }, 502);
}
}
reply_to permet de répondre directement au visiteur depuis la boîte de réception, alors que le
message part de l'adresse d'expéditeur de l'application.
Erreurs
| Cause | Nature |
|---|---|
to vide ou absent |
définitive |
Adresse de destinataire ou reply_to invalide |
définitive |
Ni html ni text |
définitive |
| Pièce jointe au base64 invalide ou au type MIME mal formé | définitive |
| Aucune configuration d'envoi pour l'application | définitive |
| Refus définitif du serveur SMTP (code 5xx, identifiants refusés par exemple) | définitive |
| Serveur SMTP injoignable, ou refus temporaire (code 4xx) | temporaire |
Une erreur définitive se reproduira à l'identique : corrigez le message avant de réessayer. Une
erreur temporaire peut passer au prochain essai. Le message de l'Error contient le détail
renvoyé par la plateforme ou le serveur SMTP.
L'appel est aussi borné par le timeoutMs du client (30 secondes par défaut). Un délai dépassé
lève une erreur, mais ne prouve pas que le message n'est pas parti.
À savoir
- Un seul essai :
sendtente l'envoi une fois, sans file ni nouvelle tentative. Pour réessayer automatiquement, envoyez depuis un handler de Queue qui répond500en cas d'échec. Une erreur définitive y sera retentée inutilement jusqu'à la dead-letter : gardez letry/catchet répondez200quand le message lui-même est en cause. - Tous les destinataires sont visibles : chaque adresse de
tofigure dans l'en-têteTo, et chaque destinataire voit les autres. Il n'y a ni copie (cc) ni copie cachée (bcc). Pour des envois individuels, appelezsendune fois par destinataire. - Le HTML part tel quel : rien n'est échappé ni nettoyé. Échappez les valeurs saisies par vos
visiteurs avant de les insérer dans
html, comme dans l'exemple. - Pas de suivi :
sendne retourne quetrue. Il n'y a ni identifiant de message exposé par le client, ni statut de remise, d'ouverture ou de rebond.
Et ensuite
| Page | Ce qu'elle couvre |
|---|---|
| Queue | Envoyer depuis un handler, avec nouvelles tentatives et dead-letter |
| Email & MJML | Newsletters, campagnes et statistiques d'envoi |
| Secrets | Valeurs et configuration propres à l'application |
| Présentation du SDK | Le client createSdk et l'interface BextSdk |