How to generate and send by email a sum-up PDF after the completion of a form (with n8n and Gotenborg)

First, excuse my English for I’m a French Grist enthusiast. I searched for weeks a way to send a PDF file once a form has been submitted in order the user gets a ready-to-print summary of what he’d just filled.

Thanks to a lot of French top-gristers (@aude @Vincent_Viers…), I manage to get a really DIY (and surely really dirty) way using n8n and Gotenberg.

Here is my perfectible but fully functional attempt. Any advice or improvement is sincerely welcome.

1 & 2. To configure the Webhook and the Grist node, I recommend this post (in French, but I’m sure you’ll easily find one in proper English) by @aude : Workflows basiques avec n8n - Ressources et astuces - le forum Grist

3. A first Code node returns the last record from the Grist table.
Two notes :

  • Normally, a good Grist configuration in the previous node would be suffisaient but I didn’t manage to do it in my n8n/Grist instance.

  • This part of the code could be part of the following node but I preferred to separate tome if I one day manage to resolve the issue above.

  • A third one, to be honest : I use DeepSeek to create the javascript parts as I have very, very few notions.

const allRecords = $input.all();
console.log(`📊 Reçu ${allRecords.length} enregistrements`);

if (allRecords.length === 0) return [];

// Affiche les timestamps pour vérifier
allRecords.forEach((record, index) => {
  console.log(`Record ${index}: ${record.json.rec_time}`);
});

const lastRecord = allRecords.reduce((newest, current) => {
  return new Date(current.json.rec_time) > new Date(newest.json.rec_time) 
    ? current 
    : newest;
});

console.log(`✅ Dernier enregistrement: ${lastRecord.json.rec_time}`);
return [lastRecord];

4. The second node generates a pdf file :

  • gets the useful values from the Grist node.

  • converts the UNIX/Timestamp dates in dd/mm/yyyyy (:france::artist::baguette_bread:)

  • generates an html template and fills it with our values.

  • save the filled template above to an html file

//gets the useful values from the Grist node.
const { 
    eleve_nom, 
    eleve_prenom,
    eleve_ddn,
    resp1_qualite,
    resp1_nom,
    resp1_prenom,
    resp1_adresse,
    resp1_cp,
    resp1_commune,
  
} = items[0].json;

// date UNIX/Timestamp => humanly readable

const date = new Date(eleve_ddn * 1000);
const jour = String(date.getDate()).padStart(2, '0');
const mois = String(date.getMonth() + 1).padStart(2, '0');
const annee = String(date.getFullYear());
const dateFormatee = `${jour}/${mois}/${annee}`;

// generates an html template and fills it with our values

const htmlContent = `
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>My HTML document</title>
  <style>
    </style>
</head>
<body>
 
${eleve_prenom} ${eleve_nom} est un sacr&eacute; zozo !

</body>
</html>
`;

//save the filled template above to an html file
return [{
  json: {},
  binary: {
    data: {
      data: Buffer.from(htmlContent).toString('base64'),
      mimeType: 'text/html',
      fileName: 'index.html'
    }
  }
}];

5. The HTTP Request / Gotenborg node transforms the html file into pdf with the following configuration (sorry for the screenshot, I’ll write it down as soon as I got time) :

6. The Send mail node.
No tricky part, you only have to write the same name for the attachment pdf as in the Gotenberg node (here « example.pdf »).

Interesting to note that you can get values from Grist to compose your email (recipient, sender, subject, body…), with the following syntax :

{{ $('Grist').item.json.recipient_email_adress }}

Make it yours.:dove:

​​

5 Likes