Une banque de détail envoie chaque trimestre un email de prise de rendez-vous. Le nom du conseiller et son lien d'agenda sont lus dans une table de référence, à partir du code agence du client. En recette, toutes les agences de test ont un conseiller, et l'email passe la validation sans remarque.
La veille de l'envoi, un développeur ajoute une sécurité. Si le conseiller est introuvable, le code appelle RaiseError("Conseiller introuvable"). L'intention est bonne, personne ne veut d'un email qui salue un conseiller sans nom.
Le lendemain matin, l'envoi de 400 000 emails s'arrête au bout de quelques minutes, en erreur. Trois agences ouvertes le mois précédent n'avaient pas encore de conseiller dans la table de référence. Et personne ne sait combien de clients sont concernés, ni lesquels.
La fonction accepte un paramètre obligatoire et quatre facultatifs. Le deuxième est celui qui manquait dans l'incident.
RaiseError(errorMessage, boolSkipCurrentOnly, apiErrorCode, apiErrorNumber, boolPreserveDataExt)
| Paramètre | Type | Rôle |
|---|---|---|
errorMessage | Chaîne, obligatoire | Message associé à l'erreur |
boolSkipCurrentOnly | Booléen, false par défaut | true ignore le destinataire courant et laisse l'envoi continuer, false arrête tout le job |
apiErrorCode | Chaîne | Code d'erreur libre, utile pour les appels par API |
apiErrorNumber | Nombre | Numéro d'erreur libre |
boolPreserveDataExt | Booléen | true conserve les écritures en data extension faites avant l'erreur, false les annule |
Un appel à un seul paramètre est donc un arrêt d'urgence, pas un filtre. La confusion est facile à faire parce que le code se lit comme une condition de sécurité alors qu'il se comporte comme une alarme incendie.
⚠️ Le piège de la recette : un garde-fou qui ne se déclenche jamais en recette n'a pas été testé. Vos jeux de données de test contiennent des clients propres, et c'est exactement ce qui empêche de voir la différence entre les deux modes.
Avant d'écrire la moindre ligne, classez chaque donnée de l'email selon ce qui doit se passer si elle manque. Le classement se fait une fois, et il tient pour tous les emails de la campagne.
| Situation | Exemple | Réponse |
|---|---|---|
| Donnée d'agrément | Prénom absent | Contenu de repli avec If Empty(...) |
| Donnée indispensable pour ce destinataire | Conseiller introuvable pour son agence | Ignorer et tracer, RaiseError(msg, true, ...) |
| Anomalie qui touche tout l'envoi | Campagne désactivée, table de référence vide | Arrêter l'envoi, RaiseError(msg, false) |
| Population connue à l'avance | Clients sans agence de rattachement | Exclure en amont par une requête SQL |
La dernière ligne compte autant que les autres. Salesforce recommande de réserver la fonction au traitement des erreurs et non à la segmentation. Si vous savez avant l'envoi qu'une population est à écarter, la retirer par une requête coûte moins cher que de l'évaluer destinataire par destinataire pendant le rendu, et le volume ciblé affiché avant l'envoi redevient juste.
L'ordre des blocs traduit l'ordre des décisions. Le premier bloc de l'email contrôle ce qui touche tout le monde, paramètres de campagne et tables de référence. Le deuxième écarte les destinataires incomplets. Le troisième seulement adapte le texte.
%%[
Var @actif
Set @actif = Lookup("Parametres_Campagne", "Actif", "CodeCampagne", "RDV_T3")
If Empty(@actif) Then
RaiseError("Parametres_Campagne : ligne RDV_T3 absente", false)
EndIf
]%%
Ce contrôle s'exécute pour chaque destinataire, mais c'est le premier qui déclenche l'arrêt. Quand la condition porte sur une date, appelez Now(true) plutôt que Now() : la fonction renvoie l'heure de début du job, la même pour tous, et le résultat ne change pas en cours d'envoi. Écrivez les messages sans accent, ils sont destinés au support et aux journaux techniques.
Ignorer un destinataire sans savoir lequel ne règle qu'une moitié du problème. La solution tient en une ligne écrite dans une data extension juste avant de lever l'erreur.
%%[
Var @cle, @job, @agence, @conseiller
Set @cle = _subscriberkey
Set @job = jobid
Set @agence = Trim(AttributeValue("CodeAgence"))
If not Empty(@agence) Then
Set @conseiller = Lookup("Conseillers", "NomConseiller", "CodeAgence", @agence)
EndIf
If Empty(@conseiller) Then
UpsertDE("Journal_Abonnes_Ignores", 3,
"SubscriberKey", @cle,
"JobID", @job,
"Motif", "CONSEILLER_ABSENT",
"Detail", Concat("Agence : ", @agence))
RaiseError("Conseiller introuvable", true, "CONSEILLER_ABSENT", 1001, true)
EndIf
]%%
Trois détails font la différence entre un journal rempli et un journal vide. L'écriture précède l'appel, parce que le code s'arrête à l'erreur et n'exécute jamais la ligne suivante. Le cinquième paramètre vaut true, sans quoi l'écriture est annulée avec le reste. Et c'est UpsertDE qui est utilisé, pas InsertDE : l'AMPscript d'un envoi est traité par lot en dernière étape, un même destinataire peut être rendu plusieurs fois, et l'upsert évite l'échec sur une ligne déjà présente.
Le deuxième argument de UpsertDE annonce le nombre de couples de recherche qui suivent, ici trois. Si ce nombre ne correspond pas aux couples fournis, la fonction lève une exception. Relisez-le à chaque modification du bloc, c'est l'erreur la plus fréquente sur cette fonction.
Au lendemain de l'envoi, une requête donne le décompte par agence, à transmettre à l'équipe qui gère la table des conseillers.
SELECT
j.Detail,
COUNT(*) AS NbClients
FROM Journal_Abonnes_Ignores AS j
WHERE j.Motif = 'CONSEILLER_ABSENT'
AND j.DateTrace >= DATEADD(DAY, -1, GETDATE())
GROUP BY j.Detail
Posez une rétention sur ce journal dès sa création. Il ne contient que des identifiants techniques et un motif, mais il n'a pas vocation à grossir indéfiniment, et quelques mois suffisent largement à traiter les écarts constatés après un envoi.
💡 Send log ou data extension dédiée : le send log est une data extension de compte alimentée automatiquement à chaque envoi. Salesforce conseille de s'y limiter à une dizaine de champs personnalisés et à une rétention courte, de l'ordre de dix jours. Pour un journal ciblé des destinataires écartés, une table dédiée reste plus simple à interroger.
Un destinataire écarté ne disparaît pas du système, il change simplement de statut à plusieurs endroits. La documentation précise trois effets qu'on découvre souvent trop tard.
Ce dernier point mérite qu'on s'y arrête quand un parcours enchaîne plusieurs messages sur la même donnée. Écarter un client du premier email ne le protège pas du deuxième. Soit vous répétez le contrôle dans chaque envoi, soit vous posez une condition de sortie dans le parcours lui-même.
Sur la visibilité du message d'erreur, restez prudent. Il servirait surtout aux journaux techniques et au support, et se retrouve difficilement dans l'interface. Écrivez-le sans accent et gardez le même code dans le journal et dans le troisième paramètre, le rapprochement se fera par ce code, pas par la phrase.
Dans Adobe Campaign, les exclusions se font dans le workflow ou par des règles de typologie appliquées à l'analyse de la diffusion. Le résultat est un rapport d'exclusions lisible, où chaque destinataire écarté apparaît avec son motif. Vous ouvrez l'analyse, vous voyez les volumes, vous décidez de diffuser ou non.
Marketing Cloud ne fournit pas cette vue. Le journal construit plus haut joue le rôle du rapport d'exclusions, et c'est à vous de le construire avant le premier envoi, pas après le premier incident. Quant à l'arrêt global, il s'apparente à une règle de contrôle qui bloque la diffusion, à ceci près qu'il se déclenche pendant l'envoi et non avant. Un arrêt à six heures du matin sans notification ne sera découvert qu'à l'arrivée de l'équipe, prévoyez les alertes d'Automation Studio en conséquence.
Avant de publier un email qui contient un appel à cette fonction, ouvrez le code et comptez les paramètres. Un seul paramètre veut dire que le premier destinataire incomplet arrête la totalité de l'envoi. Créez ensuite une data extension de test avec une ligne par branche du code, cas nominal, donnée d'agrément absente, donnée indispensable absente, et vérifiez chaque branche en aperçu avant de programmer quoi que ce soit.
Parce que le deuxieme parametre, boolSkipCurrentOnly, vaut false par defaut. Un appel a un seul parametre est donc un arret d'urgence et non un filtre : le premier destinataire incomplet arrete tout le job. Avec true, la fonction ignore le destinataire courant et laisse l'envoi continuer.
Il faut ecrire une ligne dans une data extension juste avant de lever l'erreur, car le code s'arrete a l'erreur et n'execute jamais la ligne suivante. On utilise UpsertDE plutot que InsertDE, et le cinquieme parametre de RaiseError doit valoir true, sans quoi l'ecriture est annulee avec le reste. Le lendemain de l'envoi, une requete sur ce journal donne le decompte par agence.
Non, ils ne sont pas comptes dans la consommation facturee. Ils apparaissent en revanche dans les indicateurs de suivi et de reporting, ce qui explique l'ecart constate entre le volume cible et le volume envoye.
Non. Dans un parcours, le contact est retire d'un seul envoi : il poursuit le parcours et recevra les emails suivants, qui doivent donc avoir leurs propres garde-fous. Soit vous repetez le controle dans chaque envoi, soit vous posez une condition de sortie dans le parcours lui-meme.
Non. Salesforce recommande de reserver la fonction au traitement des erreurs et non a la segmentation. Si vous savez avant l'envoi qu'une population est a ecarter, la retirer par une requete coute moins cher que de l'evaluer destinataire par destinataire pendant le rendu, et le volume cible affiche avant l'envoi redevient juste.
Je peux relire vos blocs de personnalisation, vos exclusions et vos journaux d'envoi, et vous rendre un diagnostic écrit avec les correctifs à appliquer.
Me décrire votre contexte →