Trackily Docs

SMTP Servers

Un SMTP server, c'est le relais qui livre tes emails. Trackily ne route pas directement vers les MX des destinataires — il déléguer à un MTA tiers (Mailgun, SendGrid, Postmark, SES, Gmail) via SMTP authentifié. Tu configures un ou plusieurs serveurs, tu les attaches à des listes, et le moteur d'envoi s'occupe du reste.

Email — SMTP

Concept

Pourquoi pas du SMTP direct vers gmail.com / outlook.com ? Trois raisons :

  1. Réputation IP. Les MTA pros maintiennent des IPs warmed-up, signent SPF + DKIM + DMARC pour toi, et négocient avec les opérateurs (Gmail, Yahoo, Outlook) bien mieux qu'un VPS isolé.
  2. Feedback loop. Mailgun / SES te renvoient des webhooks sur les bounces / complaints — sans ça, tu n'as aucun moyen d'apprendre que tu as cramé un email et tu pollues email_suppression à l'aveugle.
  3. Délivrabilité. Un envoi via Postmark depuis une IP partagée mais bien notée passe en inbox ; le même contenu depuis ton VPS résidentiel atterrit en spam.

Choisir un provider

Provider Quand l'utiliser Notes
Mailgun Cas le plus courant. Bon compromis prix/délivrabilité. Webhooks bounce/complaint propres. Plan free 100/jour ; payant à partir de ~35$/mois.
SendGrid Volume élevé (>100k/mois). API riche, dashboard correct. Plan free 100/jour ; payant rapidement cher au-delà.
Postmark Transactionnel pur (reçus, magic-links, notifications). Délivrabilité inbox excellente. Refuse les listes marketing — ne JAMAIS l'utiliser pour des sequences.
Amazon SES Tu as déjà un compte AWS et tu veux le prix au plus bas. Setup plus rude (SPF/DKIM à faire toi-même), mais $0.10/1000 emails.
Gmail / Workspace Très faible volume (<50/jour), strictement personnel. Bloque à 500/jour ; les comptes "envoi de masse" se font suspendre.

Recommandation par défaut : Mailgun pour les sequences, Postmark pour le magic-link et les reçus de commande. Deux SMTP server rows séparées, deux rôles bien isolés.

Le modèle de données

Schema email_smtp_servers :

Colonne Type Note
id SERIAL PK
name TEXT label humain ("Mailgun production")
host TEXT ex: smtp.mailgun.org
port INTEGER 587 (STARTTLS) ou 465 (TLS)
secure BOOLEAN true pour TLS direct (port 465), false pour STARTTLS (port 587)
auth_user TEXT nom d'utilisateur SMTP (souvent apikey ou postmaster@…)
auth_pass TEXT mot de passe / API key. Chiffré au repos via AES-256-GCM (SECRETS_MASTER_KEY). Jamais retourné dans les API responses.
from_email TEXT NOT NULL adresse expéditeur par défaut (peut être écrasée par la liste)
from_name TEXT nom d'expéditeur par défaut
reply_to TEXT reply-to par défaut
daily_limit INTEGER hard cap par jour (par défaut 5000)
hourly_limit INTEGER hard cap par heure (par défaut 500)
send_count_today INTEGER compteur courant, reset à minuit UTC
send_count_hour INTEGER compteur courant, reset à l'heure pleine
is_active BOOLEAN si false, aucun send n'utilise ce serveur
is_default BOOLEAN sélectionné par les tools qui ne précisent pas smtp_server_id (ex: magic-link, send_test_email sans list_id)
notes TEXT libre

Créer un SMTP server

Via MCP (Mailgun)

// First call — Tier-2 → returns confirmation_required
{
  "name": "create_email_smtp_server",
  "arguments": {
    "name": "Mailgun production",
    "host": "smtp.mailgun.org",
    "port": 587,
    "secure": false,
    "auth_user": "postmaster@mg.brandfr.com",
    "auth_pass": "redacted-api-key-from-mailgun",
    "from_email": "marie@brandfr.com",
    "from_name": "Marie de Brand FR",
    "reply_to": "marie@brandfr.com",
    "daily_limit": 10000,
    "hourly_limit": 1000
  }
}

// Response (first call)
{
  "status": "confirmation_required",
  "tool": "create_email_smtp_server",
  "summary": "Will create SMTP relay 'Mailgun production' (smtp.mailgun.org:587). Password is sensitive — confirm to proceed.",
  "preview": { "name": "Mailgun production", "host": "smtp.mailgun.org", ... },
  "confirm_token": "cfm_a1b2c3d4e5f6g7h8i9j0k1l2"
}

// Second call — same args + confirm_token
{
  "name": "create_email_smtp_server",
  "arguments": {
    "name": "Mailgun production",
    "host": "smtp.mailgun.org",
    "port": 587,
    "secure": false,
    "auth_user": "postmaster@mg.brandfr.com",
    "auth_pass": "redacted-api-key-from-mailgun",
    "from_email": "marie@brandfr.com",
    "from_name": "Marie de Brand FR",
    "reply_to": "marie@brandfr.com",
    "daily_limit": 10000,
    "hourly_limit": 1000,
    "confirm_token": "cfm_a1b2c3d4e5f6g7h8i9j0k1l2"
  }
}

// Response (second call)
{ "status": "ok", "smtp_server": { "id": 1, "name": "Mailgun production", ... } }

C'est un tool Tier-2 parce que le password est sensible et qu'on veut éviter qu'un LLM hallucinant créé 10 relays avec des credentials random. Scopes requis : email:write ET settings:write.

Recettes par provider

Mailgun

host       smtp.mailgun.org
port       587
secure     false
auth_user  postmaster@mg.tondomaine.com   ← vu dans Mailgun Dashboard → Domains
auth_pass  <SMTP password>                 ← Dashboard → Domains → Domain settings → SMTP credentials

Vérifie que le domaine d'envoi est vérifié SPF + DKIM dans Mailgun avant de balancer du trafic. Sinon délivrabilité catastrophique.

SendGrid

host       smtp.sendgrid.net
port       587
secure     false
auth_user  apikey                          ← littéralement la chaîne "apikey"
auth_pass  SG.xxxxxxxxxxxxxxxxxxxxxxxxxxxx  ← API key SendGrid avec permission "Mail Send"

Postmark

host       smtp.postmarkapp.com
port       587
secure     false
auth_user  <Server Token>                  ← Postmark → Servers → API Tokens → Server Token
auth_pass  <same Server Token>             ← oui, user et pass sont identiques chez Postmark

Réserve Postmark au transactionnel. Si tu envoies une newsletter via un Server transactionnel, ton compte est suspendu dans les 24h.

Amazon SES

host       email-smtp.<region>.amazonaws.com   (ex: email-smtp.eu-west-1.amazonaws.com)
port       587
secure     false
auth_user  AKIA...                              ← SMTP credentials générées dans la console SES
auth_pass  <SMTP password>                      ← pas l'access key — un mot de passe dédié SMTP

Avant de scaler : sors du sandbox SES (Request production access) sinon tu ne peux envoyer qu'à des emails préalablement vérifiés.

Gmail / Workspace

host       smtp.gmail.com
port       465
secure     true
auth_user  ton.email@gmail.com
auth_pass  <App Password>                       ← Google Account → Security → App passwords (2FA requis)

À éviter pour du sérieux. Gmail bloque autour de 500 envois/jour et n'envoie pas de feedback loop propre — tes bounces n'arrivent jamais dans email_suppression.

Tester un SMTP

Avant de lier un SMTP à une liste live, envoie un test :

{
  "name": "send_test_email",
  "arguments": {
    "to": "moi@example.com",
    "subject": "[Trackily test] Mailgun production",
    "body_html": "<p>Si tu lis ceci, le SMTP marche.</p>"
  }
}

Sans list_id, le tool prend le SMTP is_default=true. Avec list_id, il prend le SMTP de cette liste. Le mail part synchrone, sans passer par la queue, sans logger dans email_sends.

Quotas et compteurs

Chaque SMTP server stocke deux compteurs :

  • send_count_today — reset à minuit UTC (et au prochain send après ça)
  • send_count_hour — reset à l'heure pleine

Quand le worker veut envoyer un mail, il :

  1. Lit le SMTP de la liste (ou le is_default=true si pas de liste rattachée).
  2. Vérifie send_count_today < daily_limit ET send_count_hour < hourly_limit.
  3. Si non, le mail reste en email_sends.status='queued' et sera reconsidéré au prochain tick.
  4. Si oui, envoie via SMTP, incrémente les deux compteurs, marque le send sent.

Les limites évitent de griller ta réputation : Mailgun te coupe à ~10k/heure sur un compte fresh, SES te restreint dans les 200/sec en sandbox. Configure ces colonnes pour rester sous la limite officielle de ton provider.

Lister les SMTP

// Request
{ "name": "list_email_smtp_servers", "arguments": {} }

// Response (excerpt) — passwords NEVER returned
{
  "status": "ok",
  "smtp_servers": [
    {
      "id": 1,
      "name": "Mailgun production",
      "host": "smtp.mailgun.org",
      "port": 587,
      "auth_user": "postmaster@mg.brandfr.com",
      "from_email": "marie@brandfr.com",
      "from_name": "Marie de Brand FR",
      "daily_limit": 10000,
      "hourly_limit": 1000,
      "send_count_today": 1247,
      "send_count_hour": 89,
      "is_active": true,
      "is_default": true
    }
  ]
}

Note : auth_pass n'est jamais retourné. Le moteur le déchiffre en interne juste avant nodemailer.createTransport().

DNS — SPF, DKIM, DMARC

Branche ces 3 enregistrements DNS sur le domaine d'envoi AVANT de commencer à envoyer en volume :

  • SPF : TXT @ "v=spf1 include:mailgun.org ~all" (adapte selon le provider)
  • DKIM : enregistrement fourni par le provider lors du setup du domaine
  • DMARC : TXT _dmarc "v=DMARC1; p=quarantine; rua=mailto:dmarc@tondomaine.com"

Sans ça, Gmail / Outlook envoient direct en spam. Et leur DMARC report te dira combien d'emails sont rejetés.

Erreurs courantes

  • Invalid login: 535-5.7.8 Username and Password not accepted — credentials faux ou App Password manquant (Gmail).
  • Greeting never received — port bloqué par le firewall du host. Ouvre 587 et 465 en sortant.
  • Tous les emails en email_sends.status='failed' — vérifie is_active=true ET que le from_email est sur un domaine vérifié SPF/DKIM par le provider.
  • Compteur send_count_today qui ne reset pas — clock skew sur le VPS. timedatectl set-timezone UTC et redémarre le worker.
  • Emails arrivent en spam alors que tout est vert chez le provider — body HTML trop lourd (>100 KB), trop de liens externes, ratio image/text trop élevé, ou subject keyword-spammy ("FREE", "100% guaranteed"…).

Voir aussi

  • Lists — où on attache un SMTP à une liste
  • Sequences — ce qui passe par le SMTP
  • Suppression — où atterrissent les bounces du SMTP
  • Magic Link — utilise aussi le SMTP is_default=true