AutomationFlowsEmail & Gmail › Generate Course Certificates From Google Sheets with Stencil PDF and Outlook

Generate Course Certificates From Google Sheets with Stencil PDF and Outlook

ByLeon Kirschner @leonkirschner on n8n.io

This workflow creates PDF certificates using Stencil, stores them in Google Drive, and emails them to participants. A new row is added to the Google Sheets document (via form, webhook, or manual entry) The workflow generates a PDF certificate using the Stencil API The PDF is…

Event trigger★★★★☆ complexity17 nodesHTTP RequestGoogle DriveGoogle SheetsMicrosoft OutlookGoogle Sheets Trigger
Email & Gmail Trigger: Event Nodes: 17 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #11533 — we link there as the canonical source.

This workflow follows the Google Drive → Google Sheets recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "5Viam0BvDlFeWyTF2LEbV",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Generate course certificates using Google Sheets, Stencil PDF, and Outlook",
  "tags": [],
  "nodes": [
    {
      "id": "3343a90f-b31a-46f7-9712-9d9e68f09a00",
      "name": "Generate report",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        176,
        -64
      ],
      "parameters": {
        "url": "https://app.stencilpdf.com/api/reports/report-177+1234567890-k7c0zf7/generate-pdf",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "variables.participant",
              "value": "={{ $('Input').item.json.Participant }}"
            },
            {
              "name": "variables.date",
              "value": "={{ $('Input').item.json.Date }}"
            },
            {
              "name": "variables.topic",
              "value": "={{ $('Input').item.json.Exam }}"
            },
            {
              "name": "variables.course",
              "value": "={{ $('Input').item.json.Course }}"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        },
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "51fe6b7a-cedc-46c4-9473-843b56c3201e",
      "name": "Upload file",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        400,
        16
      ],
      "parameters": {
        "name": "=Course_Certificate_{{ $json.Participant }}.pdf",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "1rAaDUp6mlGgERfjEiJjY7irrG8eXG2nd",
          "cachedResultUrl": "https://drive.google.com/drive/folders/1rAaDUp6mlGgERfjEiJjY7irrG8eXG2nd",
          "cachedResultName": "Course Certificates"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "6c67c9cd-4ac9-4ab7-bce0-695e7335ec02",
      "name": "Update row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        624,
        16
      ],
      "parameters": {
        "columns": {
          "value": {
            "File": "={{ $json.webViewLink }}",
            "Participant": "={{ $('Input').item.json.Participant }}"
          },
          "schema": [
            {
              "id": "Mail",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Mail",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Participant",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Participant",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Course",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Course",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Exam",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Exam",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "File",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "File",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Sent",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Sent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Participant"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/191MQ3HBJW5-WdhPgh1ZMDKl_FRVtcG5XT_4kOnlodq8/edit#gid=0",
          "cachedResultName": "Courses"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "191MQ3HBJW5-WdhPgh1ZMDKl_FRVtcG5XT_4kOnlodq8",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/191MQ3HBJW5-WdhPgh1ZMDKl_FRVtcG5XT_4kOnlodq8/edit?usp=drivesdk",
          "cachedResultName": "n8n demos"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7,
      "alwaysOutputData": true
    },
    {
      "id": "7781839c-a6a3-4d1d-9915-d397b51b505c",
      "name": "Send a message",
      "type": "n8n-nodes-base.microsoftOutlook",
      "position": [
        1296,
        -64
      ],
      "parameters": {
        "subject": "Your certificate is ready",
        "bodyContent": "=Hi {{$('Input').item.json.Participant}},\n\nCongratulations on completing the course {{$('Input').item.json.Course}}!\n\nYour certificate is attached. Thanks for learning with us.\n\nBest,\nYour learning company",
        "toRecipients": "={{ $('Input').item.json.Mail }}",
        "additionalFields": {
          "attachments": {
            "attachments": [
              {
                "binaryPropertyName": "data"
              }
            ]
          }
        }
      },
      "credentials": {
        "microsoftOutlookOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "aa0b5983-d019-40c0-a8fc-28d311b2749f",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        848,
        -64
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2
    },
    {
      "id": "d3373c11-524b-4448-8936-2eb248a743c5",
      "name": "New participant",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        -720,
        32
      ],
      "parameters": {
        "event": "rowAdded",
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyHour"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/191MQ3HBJW5-WdhPgh1ZMDKl_FRVtcG5XT_4kOnlodq8/edit#gid=0",
          "cachedResultName": "Courses"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "191MQ3HBJW5-WdhPgh1ZMDKl_FRVtcG5XT_4kOnlodq8",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/191MQ3HBJW5-WdhPgh1ZMDKl_FRVtcG5XT_4kOnlodq8/edit?usp=drivesdk",
          "cachedResultName": "n8n demos"
        }
      },
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "d49e1bf3-59ed-4aef-9b2b-f31c71ca71a3",
      "name": "Input",
      "type": "n8n-nodes-base.set",
      "position": [
        -272,
        -64
      ],
      "parameters": {
        "options": {},
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "665178b1-8847-40a5-9463-8f6325659b3f",
      "name": "Test Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -496,
        -160
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "6007d824-f7a6-4a0d-aa0a-abd8aa98b7a6",
              "name": "Mail",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "76bfbd95-8ff3-4d4f-8b6a-672403f91105",
              "name": "Participant",
              "type": "string",
              "value": "John Doe"
            },
            {
              "id": "45a33f1d-3413-42a0-846c-9c3003ac8c6b",
              "name": "Course",
              "type": "string",
              "value": "Fantasy Course"
            },
            {
              "id": "0bcd9027-5ca3-4418-8cda-9b1c5b49e8e6",
              "name": "Exam",
              "type": "string",
              "value": "Fantasy Exam"
            },
            {
              "id": "76ac3c3c-3ecc-4fd0-8c08-804bb942b84f",
              "name": "Date",
              "type": "string",
              "value": "={{$now.format('yyyy-MM-dd')}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8a52bef6-6279-48f9-b71c-1cce0f63a455",
      "name": "Update row in sheet1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1520,
        -64
      ],
      "parameters": {
        "columns": {
          "value": {
            "Sent": "={{ $now }}",
            "Participant": "={{ $('Input').item.json.Participant }}"
          },
          "schema": [
            {
              "id": "Mail",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Mail",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Participant",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Participant",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Course",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Course",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Exam",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Exam",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "File",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "File",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Sent",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Sent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Participant"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/191MQ3HBJW5-WdhPgh1ZMDKl_FRVtcG5XT_4kOnlodq8/edit#gid=0",
          "cachedResultName": "Courses"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "191MQ3HBJW5-WdhPgh1ZMDKl_FRVtcG5XT_4kOnlodq8",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/191MQ3HBJW5-WdhPgh1ZMDKl_FRVtcG5XT_4kOnlodq8/edit?usp=drivesdk",
          "cachedResultName": "n8n demos"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "4883c4ff-a5f7-446d-9fae-9a1e8bf1dc5e",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1216,
        -304
      ],
      "parameters": {
        "width": 400,
        "height": 608,
        "content": "## How it works\nThis workflow automates course certificate generation and distribution. When a new participant is added to Google Sheets, the workflow generates a personalized PDF certificate using Stencil API, uploads it to Google Drive, sends it via Outlook email, and updates the sheet with tracking information.\n\nThe workflow runs hourly to check for new entries without a sent timestamp. The schedule can be adapted to your needs. It can also be triggered manually for testing purposes with sample data.\n\n## Setup steps\n1. Create a Stencil account and obtain your API bearer token for authentication [here](https://app.stencilpdf.com/profile)\n2. Configure the report template ID in the Generate report node\n3. Connect your Google Sheets account (for trigger and updates)\n4. Connect your Google Drive account and set the target folder ID\n5. Connect your Outlook account for email sending\n6. Update the Google Sheet columns to match: Mail, Participant, Course, Exam, Date, File, Sent\n7. Test the workflow using the Manual Trigger before activating"
      },
      "typeVersion": 1
    },
    {
      "id": "840336e5-0794-425b-ad51-3226dbe3cac5",
      "name": "Generate and save report",
      "type": "n8n-nodes-base.noOp",
      "position": [
        -48,
        -64
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "a0908fbf-6d2f-4b6b-a3c9-082accc0583f",
      "name": "Send to participant",
      "type": "n8n-nodes-base.noOp",
      "position": [
        1072,
        -64
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "b75e7c32-253f-4a56-b7c9-4b9be56af6aa",
      "name": "E-Mail not sent",
      "type": "n8n-nodes-base.filter",
      "position": [
        -496,
        32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "e53d2ed5-8f38-4aa0-a87b-ef101afb946e",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.Sent }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "fc021d18-71cf-41f2-8177-3b7b4f1931cc",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -720,
        -160
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "1ff0471e-da8f-41d8-91be-2ca2eb5103bc",
      "name": "Data Processing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -528,
        -304
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 496,
        "content": "Filters unsent entries and consolidates data from different trigger sources"
      },
      "typeVersion": 1
    },
    {
      "id": "b128ce20-f85f-44c1-92c5-ab9880f5fb91",
      "name": "PDF Generation & Storage",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        96,
        -304
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 496,
        "content": "Creates personalized PDF certificate via Stencil API and stores it in Google Drive. Also it is linked to the participant int he google sheets document."
      },
      "typeVersion": 1
    },
    {
      "id": "eda2c05d-c731-495f-a96d-506e256e3285",
      "name": "Email Delivery & Tracking",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1248,
        -304
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 496,
        "content": "Finished certificate is sent via mail to the participant. "
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "107a1d5a-b105-4715-956d-23c327c77931",
  "connections": {
    "Input": {
      "main": [
        [
          {
            "node": "Generate and save report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Send to participant",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Test Data": {
      "main": [
        [
          {
            "node": "Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload file": {
      "main": [
        [
          {
            "node": "Update row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Test Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        [
          {
            "node": "Update row in sheet1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "E-Mail not sent": {
      "main": [
        [
          {
            "node": "Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate report": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          },
          {
            "node": "Upload file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "New participant": {
      "main": [
        [
          {
            "node": "E-Mail not sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send to participant": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update row in sheet": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Generate and save report": {
      "main": [
        [
          {
            "node": "Generate report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow creates PDF certificates using Stencil, stores them in Google Drive, and emails them to participants. A new row is added to the Google Sheets document (via form, webhook, or manual entry) The workflow generates a PDF certificate using the Stencil API The PDF is…

Source: https://n8n.io/workflows/11533/ — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Email & Gmail

Sell on n8n using Stripe (Fully Automated Delivery)

Stripe Trigger, HTTP Request, Stripe +3
Email & Gmail

Hiring teams often struggle with document follow-ups, offer letter generation, and stakeholder communication. Manual checks, email back-and-forth, and missing files slow down hiring and create chaos d

Google Sheets Trigger, HTTP Request, Slack +3
Email & Gmail

This workflow turns a Google Sheet into an automated certificate-issuing pipeline. The moment a new completion row is added — whether by your training team, a Zap, or a quiz platform — a branded PDF/A

Google Sheets Trigger, N8N Nodes Templatefox, HTTP Request +3
Email & Gmail

This workflow automates the end-to-end process of generating and sending client payment links using Google Sheets and Stripe.

Google Sheets Trigger, Gmail, HTTP Request +2
Email & Gmail

This template is ideal for HR teams, startup founders, operations leads, remote-first companies, and freelancers managing onboarding manually or across multiple tools.

Google Sheets Trigger, Jira, HubSpot Trigger +7