J'ai créé ma newsletter cette semaine et je vous partage aujourd'hui les outils et automatisations qui me permettent de le faire sans que ça ne me coûte un euro.

Et reçois un max de valeur Gratuitement !

On utilise aujourd'hui principalement 3 outils :

Créer votre compte Resend

Commençons pas créer un compte, ça se passe ici !

Pour ma part j'utilise la connexion avec Google, c'est vraiment trop pratique...

Configurer votre nom de domaine

Pour envoyer des emails, il nous fait une nom de domaine, par exemple pour moi le nom est timotion.fr afin d'envoyer des emails avec contact@timotion.fr. J'aurais également pu choisir timtiret.com ou autres...

Si vous avez déjà un blog à priori, vous disposez d'un nom de domaine. Connectez-vous donc (dans un nouvel onglet) sur l'interface proposée par le vendeur de ce nom. Vous devriez voir un endroit vous permettant de configurer le DNS ou Zone DNS.

💡
Le DNS est un serveur qui permet d'associer un nom de domaine à une adresse IP pour faciliter la vie aux humains. Les machines, elles préfèrent les bonnes vieilles adresses IP de ce style : 184.128.11.161...

Vous avez trouvé ? C'est parfait ! Revenons dans l'onglet Resend dans la catégorie Domains puis cliquons sur + Add Domain.

Écrivez dans le champ Name le nom de domaine choisi et cliquez à nouveau sur + Add Domain.

Un nouvelle page s'affiche avec des informations qui peuvent faire peur, mais ne vous en faites pas, rien de sorcier. Vous allez devoir passer de la page de votre hébergeur (ou registraire) à Resend régulièrement, je vous invite donc à ouvrir les deux à côté.

Le tableau affiché par Resend vous invite simplement à créer un enregistrement par ligne dans votre DNS, plutôt simple non ?

Commencez par copier dans la colonne Host / Name le contenu de la première ligne, on voit que c'est un enregistrement de type MX et il y a une valeur et une priorité de 10.

Chez votre registraire, vous avez certainement la possibilité de créer un enregistrement, faites le avec donc un enregistrement de type MX, le nom que vous venez de copier, la priorité de 10 et pour la valeur...Retournez sur Resend pour copier à nouveau la valeur qu'il vous manquait, collez là chez votre hébergeur et ajoutez l'enregistrement.

C'est exactement la même chose pour les 3 enregistrements qui suivent...

Une fois ceci fait, cliquez dans Resend sur I've added the records. Vous devriez ensuite voir une page où Resend vous montre qu'il vérifie ces enregistrements, nous allons pouvoir passer à l'étape suivante.

Créer un Webhook sur N8N

Le but est de créer un lien sur lequel Ghost va pouvoir envoyer une requête à chaque modification sur un membre, par exemple quelqu'un qui s'inscrit, modifie son nom, ou se désinscrit des emails.

Le logiciel d'automatisations N8N va recevoir les modifications de la part de Ghost, puis les transmettre à Resend, et inversement lorsque quelqu'un se désinscris via un lien dans un email envoyé avec Resend, nous allons faire en sorte qu'il envoie les informations nécessaires à N8N, pour que lui-même les applique également sur Ghost.

Dans N8N créez un nouveau workflow. Pour créer votre premier bloc, cliquez sur le + et sélectionnez On webhook call. Un fenêtre s'ouvre, changez donc la HTTP Method de GET à POST, cliquez ensuite sur l'onglet Production URL, puis sur le lien qui s'affiche juste en dessous. Vous pouvez à présent fermer ce gros popup en cliquant en dehors ou bien en haut à gauche sur Back to canvas.

Vous pouvez maintenant cliquer sur le bouton orange Save en haut à droite et activer le workflow avec le switch juste à gauche où il est pour l'instant écrit Inactive.

Faire pointer l'évènement Ghost sur le Webhook N8N

Connectez-vous à présent sur votre Panel Admin Ghost (<votre-nom-de-domaine>/ghost) et dirigez vous dans les paramètres, section Advanced, menu Integrations.

En haut à droite du petit panneau, cliquez sur Add custom integration.

Donnez-lui un petit nom, par exemple N8N -> Resend, puis cliquez sur Add.

Dans le modal qui s'est ouvert, en bas apparaît un lien en vert Add webhook, cliquez dessus.

Dans le nouveau modal, vous pouvez nommer ce webhook, par exemple Member Update, sélectionner un évènement, celui qui nous intéresse est tout en bas dans la liste : Member updated. Dans Target URL, collez le lien que nous venons de créer sur N8N et vous pouvez finir par cliquer sur Add, puis Save.

Et voilà ! 🎉 Maintenant, chaque fois qu'un membre est créé ou modifié, Ghost envoie l'information à N8N. Testons cela !

Rendez-vous sur votre blog, et créez un nouveau membre avec une adresse mail qui n'existe pas encore dedans.

Sur N8N, dans l'onglet Executions vous devriez voir qu'il s'est passé quelque chose dans la colonne de gauche qui contient les exécutions. Notre node Webhook créé précédemment s'affiche maintenant en vert car il contient des données, cliquez en haut à droite sur Copy to editor.

Votre node apparaît maintenant en violet dans l'éditeur car il contient des données épinglées, celles qui nous ont été envoyées par Ghost lors de l'ajout du membre. En cliquant dessus, on peut visualiser dans la colonne de droite les données dans body>member>current, on voit que l'on a notamment l'email, le nom et le statut de souscription (subscribed). Fermons ce modal et passons à l'étape suivante.

Créer une clé API Resend

Pour avertir Resend des informations envoyées par Ghost, nous allons devoir connecter N8N et Resend. Pour cela, nous avons besoin d'une sorte de mot de passe que l'on appelle dans le jargon "Clé API" ou "API Key" en anglais.

Rendez-vous donc sur Resend, dans l'onglet API Keys. Faites ensuite Create API Key, puis nommez là, par exemple N8N -> Ghost, enfin, cliquez sur Add. Copiez maintenant la clé grâce au petit bouton tout à droite du champ qui est apparu.

Créer une requête HTTP sur N8N

Dans N8N, nous allons maintenant créer une requête et l'authentifier à l'aide de notre clé API pour communiquer les informations qui nous ont été transmises par Ghost à Resend.

Cliquez donc sur le petit + à droite de votre Webhook pour déclencher à la suite de la réception automatiquement une requête HTTP, tapez donc HTTP dans la barre qui s'est affichée sur le panneau à droite et cliquez sur l'icône Internet bleue (HTTP Request).

Vous pouvez d'ores et déjà sélectionner la méthode POST, puis dans Authentification sélectionnez Generic Credential Type, puis dans Generic Auth Type sélectionnez Header Auth. Enfin, dans Header Auth sélectionnez Create new credential.

Dans le champ Value, vous pouvez directement écrire Bearer, puis après un espace, coller votre clé API. Dans name, vous aurez besoin d'écrire Authorization et puis si vous le souhaiter vous pouvez nommer cette authentification en cliquant en haut à gauche sur le nom par défaut : Header Auth Account, pour me part je mets par exemple Resend timotion.fr.

Vous pouvez à présent cliquer sur le bouton orange Save pour enregistrer.

À présent, nous allons chercher quelle requête faire dans Resend, l'idée est de créer ou mettre à jour un contact, rendez-vous donc sur l'onglet Audiences de Resend qui est l'endroit où nous ajoutons nos contacts. Resend propose quasiment à chaque fois un petit bouton API qui permet de visualiser facilement quelles requêtes sont possibles sur une certaine entité, en l'occurence : l'audience générale.

Dans le panneau qui s'affiche maintenant à droite je vois que le premier bout de code me propose d'ajouter un contact, ça tombe bien c'est justement ce que je souhaite faire. Je clique alors sur cURL pour voir la requête en entier.

curl -X POST 'https://api.resend.com/audiences/14a40358-c464-468a-abcc-e09ae18b84f0/contacts' \
 -H 'Authorization: Bearer re_123456789' \
 -H 'Content-Type: application/json' \
 -d $'{
  "email": "steve.wozniak@gmail.com",
  "first_name": "Steve",
  "last_name": "Wozniak",
  "unsubscribed": false
}'

Je vois que c'est une requête avec la méthode POST et qu'elle utilise une authentification avec jeton "Bearer", mais ce qui m'intéresse ici c'est surtout l'URL de la requête que je n'ai pas encore configurée dans N8N :

https://api.resend.com/audiences/14a40358-c464-468a-abcc-e09ae18b84f0/contacts

Et également son contenu :

{
  "email": "steve.wozniak@gmail.com",
  "first_name": "Steve",
  "last_name": "Wozniak",
  "unsubscribed": false
}
⚠️
Ne copiez pas mon URL, la vôtre sera différente en fonction de l'ID de votre audience !

Voilà, nous pouvons dès à présent coller le lien dans le champ URL de N8N et puis pour le contenu, nous allons activer le switch Send Body, dans Specify Body je sélectionne Using JSON et puis juste en dessous je colle le contenu de ma requête.

Nous allons pour finir intégrer les variables fournies par Ghost dans le Webhook au contenu de ma requête pour que ce ne soit pas à chaque fois Steve Wozniak mais bien la personne qui a effectué une action !

Pour cela, je passe en mode expression et en bas à droite du champ éditeur de JSON, un bouton permet de passer en mode plein écran pour plus de clarté. Ensuite, je remplace les valeurs par défaut par mes variables en les glissant depuis les informations d'entrée à gauche et je retire le last_name car Ghost ne fournit qu'un nom. J'obtiens quelque chose comme cela :

{
  "email": "{{ $json.body.member.current.email }}",
  "first_name": "{{ $json.body.member.current.name }}",
  "unsubscribed": {{ !$json.body.member.current.subscribed }}
}
⚠️
Petite difficulté : le champ demandé par Resend est unsubscibed alors que celui envoyé par Ghost se nomme subscribed. Il faut donc inverser la valeur pour mettre true quand c'est false et inversement. Raison pour laquelle j'ai mis un point d'exclamation devant ma variable qui signifie non en programmation et qui permet d'inverser, si c'est non vrai, alors c'est faux. Si mon abonné n'est pas désabonné, c'est qu'il est abonné... 🤪

Transformé par N8N en cela :

{
  "email": "contact@timotion.fr",
  "first_name": "Tim",
  "unsubscribed": false
}

Par ailleurs n'oubliez pas les double guillemets (") autour de l'email et du first_name car c'est comme cela que l'on représente une chaîne de caractères (un texte) dans le format JSON.

Une fois que c'est tout bon, l'on peut fermer le mode plein écran, retourner sur notre canva et puis enregistrer.

Requête PATCH en cas de désinscription

Ce que nous venons de faire fonctionne bien pour créer le contact en cas d'inscription et modifier son nom lorsque c'est nécessaire mais - ne me demandez pas pourquoi - : si l'utilisateur existe déjà et qu'il se désinscrit, N8N envoie bien la même requête avec unsubscribe : true, mais ça ne met pas à jour le statut d'abonnement du contact dans Resend, bizarre sachant que ça fonctionne pour le nom mais c'est comme ça, nous allons devoir envoyer une deuxième requête.

Le plus simple est de dupliquer notre première requête pour ne pas devoir tout refaire de zéro. Pour cela, faisons clique droit sur la première requête, puis Duplicate et on relie la sortie du noeud précédent à l'entrée du suivant en glissant depuis la poignée à droite de la première requête vers celle d'entrée de la suivante.

Nous avons 3 petites modifications à effectuer à présent.

Déjà, la méthode n'est plus POST mais PATCH (c'est une correction / mise à jour, pas une création).

Ensuite derrière l'URL de la requête j'ajoute un slash et l'email du contact, ça donne quelque-chose comme cela :

https://api.resend.com/audiences/9ae3f7c2-6f0e-4b8f-8103-70b2a428b2a4/contacts/{{ $('Webhook').item.json.body.member.current.email }}

transformé par N8N en cela :

https://api.resend.com/audiences/9ae3f7c2-6f0e-4b8f-8103-70b2a428b2a4/contacts/contact@timotion.fr
💡
Attention ! Parfois N8N à tendance à ajouter un espace en trop ce qui provoque une erreur lors de la requête, veillez donc à ce qu'il n'y ait pas d'espace entre le / et {{.

Pour finir, dans le contenu, je ne mets que le champ unsubscribe, et je glisse à nouveau la variable qui doit être mise à jour comme nous ne somme plus dans le même noeud.

{
  "unsubscribed": {{ !$('Webhook').item.json.body.member.current.subscribed }}
}

Toujours, je n'oublie pas le point d'exclamation pour obtenir l'inverse de ce que nous propose Ghost, N8N transforme donc mon JSON en cela :

{
  "unsubscribed": false
}

Normalement c'est tout bon ! Enregistrez et testez, lorsqu'un utilisateur active ou désactive sa newsletter ou encore change son nom sur Ghost, la mise à jour est automatiquement faite sur Resend. 🎉

Synchronisation Resend vers Ghost

Lorsqu'une modification est faite sur Ghost, elle est transmise à Resend, maintenant j'aimerais que cela fonctionne aussi dans l'autre sens, c'est à dire que lorsque quelqu'un clique sur un lien de désinscription envoyé via Resend, la modification soit répercutée automatiquement dans Ghost.

Webhook de Resend vers N8N

Créez donc un nouveau workflow sur N8N comme tout à l'heure avec un Webhook, la méthode POST, puis copiez l'URL de production, enregistrez et activez le workflow.

On va maintenant retourner sur Resend pour ajouter notre Webhook dans l'onglet Webhooks.

Cliquez donc sur Add webhook, puis dans le modal qui s'ouvre, on colle l'URL fournie par N8N. L'évènement qui nous intéresse est contact.updated, on le coche donc et on finit par cliquer sur Add.

Ça y est ! 🎉 Normalement lorsque l'on modifie un contact (ou qu'il se désabonne), on reçoit le Webhook sur N8N et cela provoque une exécution.

On peut tester en se rendant dans l'onglet Audiences, puis sur la fiche d'un contact et dans les trois petits points en haut à droite à côté du bouton API, en cliquant sur Edit Contact on peut par exemple décocher le slider d'inscription (Subscribed), puis cliquer sur Save.

De retour dans N8N, l'exécution s'est effectivement produite, en regardant les informations envoyées je vois bien mon "unsubscribed" : true ainsi que l'email et le first_name, c'est parfait je vais pouvoir mettre à jour Ghost.

Éditer le membre Ghost depuis N8N

Pour modifier un contact avec l'API Admin Ghost, il faut d'abord récupérer son ID. On va donc faire une recherche avec le champ email de notre membre, puis dès que l'on a son ID, on peut mettre à jour le membre.

En pratique, on copie les données de la dernière exécution dans l'éditeur avec le bouton Copy to editor, puis, on crée un nouveau node avec une requête HTTP.

Dans le sélecteur d'authentification, on sélectionne Predefined Credential Type, puis dans le sélecteur Credential Type, on tape ghost, puis on sélectionne Ghost Admin API . Ensuite dans Ghost Admin API, on clique sur Create new credential et c'est là qu'il faut aller copier des informations dans la partie intégrations custom de Ghost, là où l'on avait créé notre premier Webhook.

Quand on retrouve et que l'on clique sur l'intégration (je l'avais personnellement appelée N8N -> Resend), on a des informations qui s'affichent et notamment notre API URL (qui est tout simplement le nom de domaine associé à notre blog) et Admin API key dont l'on va avoir besoin tout de suite.

Renseignez donc dans N8N l'API URL, et l'Admin API key donnés par Ghost, et je vous conseille de renommer la connexion en cliquant sur son nom (Ghost Admin account), pour ma part je mets Ghost Admin timtiret.com. Ça me permettra si je gère plusieurs sites de les différencier.

Ça y est, l'authentification est enfin faite ! ✅

On va maintenant construire notre première requête. Souvenez vous, on commence par rechercher notre membre et récupérer son ID, c'est donc une requête avec la méthode GET. L'URL est la suivante :

https://<votre-nom-de-domaine>/ghost/api/admin/members

Chez moi ça donne cela :

https://timtiret.com/ghost/api/admin/members

Je rajoute également un filtre sur l'email pour obtenir un truc comme cela :

https://timtiret.com/ghost/api/admin/members/?filter=email:contact@timotion.fr

Dans N8N avec une expression qui récupère les infos dans le Webhook, ça s'écrit comme cela :

https://timtiret.com/ghost/api/admin/members/?filter=email:{{ $json.body.data.email }}

Normalement c'est bon, si je clique sur Test step, j'obtiens effectivement en sortie mon membre et toutes ses informations (dont son ID).

Je passe donc à la création de ma dernière requête, pour cela, je duplique la précédente, j'utilise la méthode PUT et à la place de mon filtre d'email je mets un slash et l'ID de mon membre :

https://timtiret.com/ghost/api/admin/members/{{ $json.members[0].id }}

Il va maintenant falloir que j'ajoute un body. Je coche donc le switch, passe le sélecteur Specify Body en mode Using JSON et entre ceci dans le champ :

{
    "members": [{
      "name" : "{{ $('Webhook').item.json.body.data.first_name }}",
      "subscribed" : {{ !$('Webhook').item.json.body.data.unsubscribed }}
    }]
} 
⚠️
Attention ! Lorsque vous récupérez les variables à mettre à jour, prenez bien celles qui proviennent du Webhook et non de Ghost (en bas de la colonne input à gauche), sinon c'est inutile...

Plus qu'à tester. Lorsque je passe mon contact Resend de subscribed à unsubscibed et que je vais voir dans les préférences d'email du compte que j'ai créé, la répercussion est bien présente, et celle-ci est maintenant bidirectionnelle ! 🎉

Envoyer son premier email (avec l'éditeur Markdown)

Resend propose un éditeur d'email au format Markdown que je trouve top ! Pour y accéder, rendez-vous dans la section Broadcasts, puis on clique sur Create Broadcast. Nous y êtes ! 🤪

Dans le champ From, vous allez pouvoir configurer le nom d'expéditeur et l'email d'envoi :

Le nom que vous voulez <votre-mail@votre-domaine.com>

Ce qui donne pour ma part :

Tim Tiret <contact@timotion.fr>

Dans le champ To, vous pouvez sélectionner l'audience General, celle pour laquelle nous venons de configurer la synchronisation.

Le champ Preview comme son nom l'indique est l'aperçu que les gens voient sur certaines apps de messagerie avant d'ouvrir l'email, vous pouvez le laisser vide, c'est comme vous préférez.

Le sujet, vous connaissez et ce qui est intéressant maintenant c'est l'éditeur Markdown. Celui-ci vous permet d'écrire et de mettre en forme rapidement en utilisant des styles et éléments prédéfinis et minimalistes.

Comme sur Notion, quand vous faites un slash, vous obtenez la liste des éléments disponibles et pouvez alors en sélectionner un.

L'autre façon de créer un élément est d'utiliser le language Markdown qui est un language très simple pour vous assister dans la mise en forme.

Par exemple quand vous mettez un #, puis un espace devant un texte, il se transforme en titre de niveau 1, plus vous mettez de hashtags devant, plus il sera petit, par exemple si je fais #### Titre 4, j'aurais un titre de niveau 4. De la même manière si je mets du texte entre étoiles simples il se transforme en *italique* et entre doubles étoiles en **gras**. Pour plus de détails au sujet du Markdown, je vous conseille cette page de documentation créée par l'association de logiciels libres Framasoft.

Pour ma part j'ai l'habitude d'utiliser Markdown à la fois dans mon logiciel de prise de notes (Bear) et dans Ghost. C'est pratique parce que je peux facilement copier coller ce que je veux sans incompatibilités entre tous ces outils.

Une fois votre rédaction terminée, vous pouvez envoyer un email de test à l'aide du bouton Test email, il ne sera alors envoyé qu'aux addresses inscrites dans le popup qui s'affiche juste après. Quand tout est bon pour vous, vous pouvez cliquer sur Send, Resend vous indique alors le nombre de contacts qui recevrons l'email et vous devez glisser un curseur pour le faire partir, ce que je trouve extrêmement satisfaisant... 😏

Merci pour votre lecture !

Créer Gratuitement sa Newsletter Blog Ghost avec Resend (TUTO)