Trackily Docs

Email Lists

Une liste, c'est un conteneur d'abonnés avec ses propres réglages d'opt-in, son SMTP attaché, sa sequence par défaut, et son binding à des landings. Toute la machinerie (sends, events, suppression) s'organise autour de ce concept.

Email — Lists

Concept

Une liste répond à trois besoins :

  1. Segmenter ta base. Différentes audiences (acheteurs sweepstakes vs nutra vs SaaS) n'ont pas besoin des mêmes drips. Une liste = un segment grossier ; les custom_fields JSONB sur chaque subscriber permettent une segmentation fine au sein de la liste.
  2. Isoler les réglages d'envoi. Une liste a sa propre adresse from_email/from_name, son reply_to, son SMTP, ses politiques opt-in. Tu peux router "Newsletter brand A" via Mailgun et "Transactional brand B" via Postmark, depuis la même instance.
  3. Brancher l'enrôlement. Une landing pointe une liste via landing_pages.email_list_id. Un conversion trigger pointe une liste via email_conversion_triggers.target_list_id. Une liste, c'est l'adresse stable vers laquelle tu redirigies des subscribers depuis n'importe quelle source.

Le modèle de données

Schema email_lists (extrait) :

Colonne Type Note
id SERIAL PK
name TEXT label humain
slug TEXT UNIQUE utilisé dans les URLs de tracking
smtp_server_id INTEGER FK NULL = pas encore configuré — la liste accepte des subscribers, et leurs envois attendent : retenus jusqu'à 24 h après leur échéance, ils partent dès qu'un SMTP actif est rattaché, sinon ils échouent en smtp_indisponible
default_sequence_id INTEGER FK sequence auto-déclenchée à l'enrollment (NULL = pas de drip auto)
double_opt_in BOOLEAN si true, le subscriber doit cliquer un lien de confirmation avant de recevoir la sequence
track_opens BOOLEAN injecte le pixel d'ouverture
track_clicks BOOLEAN réécrit les liens via un redirect signé (/k/, HMAC de l'envoi + l'URL ; un lien non signé mène à une page de sortie, jamais à une redirection automatique)
unsubscribe_redirect_url TEXT URL vers laquelle on redirige après un click sur unsubscribe (vide = page interne)
custom_unsubscribe_message TEXT message affiché sur la page d'unsubscribe (vide = défaut)
confirmation_subject TEXT subject de l'email de double opt-in
confirmation_body_html TEXT HTML du même
from_name_override TEXT si non-vide, écrase le from_name du SMTP server
reply_to_override TEXT idem pour reply-to
sto_enabled BOOLEAN active Send-Time Optimization (chaque send est snappé à l'heure préférée du subscriber)
sto_default_hour SMALLINT 0-23 UTC heure de fallback quand on n'a pas encore appris la préférence d'un subscriber
is_active BOOLEAN soft-disable : aucune envoi, mais la liste reste lisible
subscriber_count INTEGER dénormalisé, mis à jour par trigger
deleted_at TIMESTAMPTZ soft-delete : la liste disparaît de l'UI mais reste queryable pour l'archive

Créer une liste

Via l'UI

Sidebar → Email Marketing → Lists → New list. Tu remplis les champs essentiels :

  • Name + slug (auto-suggéré depuis le name)
  • SMTP server — optionnel, tu peux laisser vide et brancher plus tard. En attendant, les envois de la liste sont retenus : rattache un serveur actif dans les 24 h qui suivent l'échéance d'un envoi, sinon il échoue en smtp_indisponible et ne sera pas reprogrammé. Même règle si le serveur de la liste est désactivé ou supprimé ; réaffecter la liste à un serveur actif fait partir les envois retenus
  • Double opt-in — coche si tu veux la confirmation par email avant d'envoyer la sequence
  • Track opens / Track clicks — décoche pour les listes transactionnelles où le tracking serait inutile / mal vu

Via MCP

// Request
{
  "name": "create_email_list",
  "arguments": {
    "name": "Sweepstakes Buyers FR",
    "slug": "sweeps-buyers-fr",
    "smtp_server_id": 1,
    "double_opt_in": false,
    "track_opens": true,
    "track_clicks": true
  }
}

// Response
{
  "status": "ok",
  "tool": "create_email_list",
  "message": "Email list created",
  "list": {
    "id": 7,
    "name": "Sweepstakes Buyers FR",
    "slug": "sweeps-buyers-fr",
    "smtp_server_id": 1,
    "default_sequence_id": null,
    "subscriber_count": 0,
    "is_active": true,
    "created_at": "2026-05-18T12:14:02.213Z"
  }
}

Le tool est défini dans autopilot-email-tools.js. Scope requis : email:write.

Modes d'opt-in

Mode double_opt_in Comportement
Single opt-in false Le subscriber est status='subscribed' immédiatement. La sequence démarre dès l'enrollment.
Double opt-in true Le subscriber est status='pending_confirm'. Un email de confirmation est envoyé. Tant qu'il n'a pas cliqué le lien (confirm_token), aucun email de la sequence n'est envoyé. Exception : une inscription avec skip_double_optin: true (postback de conversion, import en masse, outil MCP) entre directement subscribed — une confirmation encore en file est alors annulée.

Quand choisir lequel ?

  • Single opt-in : trafic affiliate, leads qualifiés par un formulaire prelander, conversions e-commerce. Le coût d'opportunité d'attendre un click de confirmation est trop élevé.
  • Double opt-in : list-building pur (lead magnet, newsletter), audiences européennes où le GDPR pousse à documenter le consentement, listes que tu veux protéger contre le bot signup.

Champs GDPR / compliance

Trackily stocke pour chaque subscriber :

  • enrolled_at — quand le consentement a été donné
  • confirmed_at — quand le lien double opt-in a été cliqué (si applicable)
  • ip — IP au moment de l'enrollment
  • user_agent — UA du browser
  • source — texte libre ("landing:foo-slug", "manual:admin", "trigger:postback-offer-42")
  • country_code — auto-détecté via le tracking

Ces données t'aident à prouver le consentement en cas de demande GDPR. Pas de RGPD-magic automatique : à toi d'exposer une page d'export / suppression (et de propager l'unsubscribe vers email_suppression si la personne le demande).

Subscriber counts

email_lists.subscriber_count est dénormalisé pour éviter un COUNT(*) à chaque chargement de page. Il est recalculé :

  • À chaque enroll_email_subscriber / unsubscribe_email_subscriber
  • À chaque bounce / complaint reçu via webhook MTA
  • Manuellement via un utilitaire admin si tu as bidouillé la DB en direct

Le compteur reflète les status='subscribed' uniquement — pending_confirm, unsubscribed, bounced, complained, nettoye ne comptent pas.

Langue des contacts et centre de préférences

langue_contact (en par défaut, fr, ar) règle la langue de ce que tes CONTACTS voient pour cette liste : le pied de chaque e-mail (« Se désinscrire » + « Gérer mes préférences ») et la page de préférences (de droite à gauche en arabe). Les listes existantes restent en anglais. Elle s'accepte à la création (POST /admin/api/email/lists) comme à l'édition, validée (400 langue_invalide). Par MCP : update_email_list avec langue_contact. Le détail d'une liste (GET /admin/api/email/lists/:id) rend aussi pieds_apercu : le pied dans les trois langues, tel que le moteur l'ajoute.

Depuis la page « Gérer mes préférences », un contact choisit ses listes, reçoit moins ou fait une pause : Centre de préférences.

Nettoyer une liste

Une vieille liste importée contient toujours des adresses mortes. Nettoyer la liste les trouve sans rien envoyer (syntaxe, fautes de frappe comme gmial.com, boîtes jetables, noreply@, domaines sans serveur de messagerie, adresses déjà en suppression), te montre le nombre par motif, et ne retire qu'après ta confirmation. Les lignes retirées passent « Nettoyée » ; tout est annulable 30 jours. Le guide complet : Nettoyer une liste.

Lister, inspecter, éditer

Lister toutes les listes

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

// Response (excerpt)
{
  "status": "ok",
  "lists": [
    {
      "id": 1,
      "name": "Newsletter FR",
      "slug": "newsletter-fr",
      "subscriber_count": 8421,
      "smtp_server_id": 1,
      "default_sequence_id": 3,
      "double_opt_in": false,
      "is_active": true,
      "bound_landings": 4,
      "sequences": 2
    }
  ]
}

Inspecter en profondeur

{
  "name": "get_email_list_detail",
  "arguments": { "id": 1 }
}

Retourne settings + histogramme des subscriber statuses + 10 plus récents subscribers + sequences attachées + landings bound. À utiliser AVANT toute opération de masse (rotation de sequence, soft-delete, changement de SMTP) pour t'éviter une surprise.

Patch incrémental

{
  "name": "update_email_list",
  "arguments": {
    "id": 1,
    "default_sequence_id": 5,
    "from_name_override": "Marie de Brand FR"
  }
}

Tu envoies seulement les champs à modifier. Les autres restent intacts.

Bind une liste à une landing

{
  "name": "bind_landing_to_email_list",
  "arguments": {
    "landing_id": 42,
    "list_id": 7
  }
}

Toute soumission du formulaire de la landing enrôle désormais l'email dans la liste 7 et démarre sa default_sequence. Pour unbind, passe "list_id": null.

Détails sur les automations dans la page dédiée → Automations.

Soft-delete

Une liste se supprime de manière réversible. La table email_lists reçoit deleted_at = NOW(), mais les subscribers, sends, events, suppression sont conservés.

{
  "name": "delete_email_list",
  "arguments": { "id": 1 }
}

C'est un tool Tier-2 : la première call retourne une preview avec subscriber_count + nombre de landings bound. Tu rappelles avec confirm_token pour exécuter.

Erreurs courantes

  • "column does not exist : from_name_override" — ta DB n'a pas appliqué la migration v41. Lance le startup une fois, le migrator y va.
  • Sequence ne démarre pas — vérifie que default_sequence_id est bien set sur la liste. Sans ça, l'enrollment se fait mais aucune drip ne tourne.
  • Subscribers en pending_confirm qui ne reçoivent rien — double opt-in actif sans SMTP configuré → la confirmation attend. Branche un SMTP actif dans les 24 h qui suivent l'inscription (elle part alors au tick suivant), sinon elle échoue en smtp_indisponible. Ou désactive le double opt-in.
  • Compteur figé à 0 — tu as utilisé INSERT INTO email_subscribers direct au lieu de passer par enroll_email_subscriber (qui met à jour le compteur).

Voir aussi