Skip to main content

Create a branded PDF receipt for every Stripe payment with n8n

Each successful Stripe payment becomes a receipt built from your own Google Docs template, saved to Google Drive as a Doc and a PDF, with the link emailed to the customer.

Source
Stripe
Output
Google DocsPDF (single runs only)
Delivered to
Google Drive

n8n workflow

The importable workflow file for this recipe is being tested and will be added here. The steps below show exactly what it does, so you can build it now in a few minutes with the SourceToDocs n8n node.

How the workflow runs

  1. 01

    Stripe Trigger: Checkout Session.completed

    Starts the workflow when a customer completes a Stripe Checkout session or a Payment Link. The event carries the customer's name and email, the amount and the currency.

  2. 02

    If: payment_status is equal to paid

    A session can complete before the money has arrived, for example with a bank debit. Only paid sessions continue.

  3. 03

    SourceToDocs: Document, Generate

    Rows Come From is set to the Map Columns mode. Customer, Email and Reference come from the event; Amount is divided by 100 and given its currency code; Paid on is the event's timestamp as a date. One payment is one run, so switching on Also generate PDF delivers a PDF beside the Google Doc.

  4. 04

    Gmail: Message, Send

    The node's output carries file_url, file_name and a deliveries list. The email goes to the address the customer paid with, and the PDF link comes from the Google Drive delivery.

Stripe fields and the template placeholders they fill
Stripe field Template placeholder
customer_details.name {{ customer }}
customer_details.email {{ email }}
amount_total and currency {{ amount }}
payment_intent {{ reference }}
created (the event's time) {{ paid_on }}

Or have Claude set it up

The Invoices and receipts skill works through the SourceToDocs connector at https://app.sourcetodocs.com/api/mcp. Install the skill, connect, then paste:

Use the invoices-and-receipts skill. I exported last month's successful payments from Stripe as a CSV, attached here: one row per payment, with the columns Customer, Email, Amount, Reference and Paid on. The receipts have no line items: one row is one receipt. Create a Google Docs receipt document in SourceToDocs from it, with a PDF for each receipt. I will pick the Google Docs template and the Drive folder "Receipts 2026" when you send me the links. Show me the placeholder mapping and tell me how many runs it takes before you run anything, then generate the first receipt only.

Get the skill file

To turn a Stripe payment into a PDF receipt with n8n, start the workflow with the Stripe Trigger on a completed Checkout session, let only paid sessions through, pass the customer, amount and date to the SourceToDocs node to fill your Google Docs receipt template, and email the link the node returns with Gmail. Each payment is its own run, which is what lets SourceToDocs save a PDF next to the Google Doc in your Drive folder. The build is four nodes, described below.

What you need

  • A Stripe account that takes payments through Checkout or Payment Links, and its API key for the n8n credential. Build and test with a test mode key first.
  • A receipt template in Google Docs with your logo and the placeholders {{ customer }}, {{ email }}, {{ amount }}, {{ reference }} and {{ paid_on }}. The receipt template is a fuller design with its own placeholder names: a design reference, not a drop-in file.
  • A SourceToDocs workspace and an API token for it. A workspace owner or admin creates the token on the API Tokens page.
  • On the Accounts page, a Google Docs connection for the template and a Google Drive connection for delivery. Every tile is a separate connection: Google Drive does not cover Google Docs.
  • n8n, self-hosted or Cloud, with the SourceToDocs node installed. In n8n, open Settings, then Community nodes, and install @twovmodules/n8n-nodes-sourcetodocs. On n8n Cloud, add it from the nodes panel, where it is listed as a verified node.
  • A Gmail credential in n8n.

Set up the document in SourceToDocs

SourceToDocs has no Stripe connection. The template and its settings live in SourceToDocs, and n8n sends the payment’s values.

  1. Create a Google Docs document.
  2. Upload a small CSV with the header row Customer, Email, Amount, Reference, Paid on and one sample line as the data source; it becomes a data set. The sample is enough, because the workflow sends the real rows in place of reading the source.
  3. Each column gets an alias, a snake-case identifier derived from its name. The template uses the alias: column Paid on is {{ paid_on }}.
  4. On the Document step, pick your Google Doc. Its Variables panel lists the exact placeholders for your data set; click one to copy it.
  5. Add a Google Drive distribution and choose the folder.
  6. Switch on Also generate PDF in the Output card.
  7. Leave split off. A split document produces no PDFs.

Build it in n8n

Add the nodes in this order and connect them in a line, using the true output of If. Stripe delivers one event per execution, so no loop is needed.

NodeOperationKey settings
Stripe TriggerEvents: Checkout Session.completed, the checkout.session.completed eventNo other settings. Activating the workflow registers the webhook in your Stripe account.
IfOne condition{{ $json.data.object.payment_status }} is equal to paid.
SourceToDocsResource: Document. Operation: GenerateDocument: your receipt document. Rows Come From: This Workflow — Map Columns. Data Set: the one with the five columns. Columns: see below. Wait for Completion stays on, which is the default.
GmailResource: Message. Operation: SendTo, Subject and Message: see below.

The trigger outputs the Stripe event, and the Checkout session sits under data.object. Make a test payment and confirm the paths below in the trigger’s output panel. The five column values in the SourceToDocs node:

ColumnValue
Customer{{ $json.data.object.customer_details.name }}
Email{{ $json.data.object.customer_details.email }}
Amount{{ ($json.data.object.amount_total / 100).toFixed(2) }} {{ $json.data.object.currency.toUpperCase() }}
Reference{{ $json.data.object.payment_intent }}
Paid on{{ DateTime.fromSeconds($json.created).toFormat('d LLLL yyyy') }}

The Gmail fields:

FieldValue
To{{ $('Stripe Trigger').first().json.data.object.customer_details.email }}
SubjectYour receipt
MessageThank you for your payment. Your receipt: {{ $json.deliveries[0].result.pdf_url }}

The Column names are the data set’s column names, case included, not the template placeholders, which use the aliases.

Getting the amount right

Stripe sends amount_total in the smallest unit of the currency and currency as a lowercase code. A payment of 49.00 euros arrives as 4900 and eur. The Amount expression divides by 100, keeps two decimals and appends the code in capitals, which prints 49.00 EUR.

Two cases need a change:

  • Currencies without a minor unit. The Japanese yen and a few others arrive as whole units. Leave out the division for those, or the receipt will show a hundredth of what was paid.
  • A currency symbol in place of the code. Replace the expression with {{ new Intl.NumberFormat('en-US', { style: 'currency', currency: $json.data.object.currency.toUpperCase() }).format($json.data.object.amount_total / 100) }} and set the locale to the one your customers expect.

Mapped values are sent as they resolve, so what the expression prints in n8n’s preview is what appears on the receipt. Check it against a test payment before you go live.

Other Stripe events

Checkout Session.completed is the event to use when customers pay through Checkout or Payment Links, because it carries the name and email they typed. payment_intent is filled for one-off payments; a subscription checkout carries an invoice reference instead, so map Reference to {{ $json.data.object.id }} or another field you see in the trigger’s output.

If your payments are created another way, the Stripe Trigger also offers Charge Succeeded and Payment Intent.succeeded. Their objects have other field names (a charge has amount in place of amount_total, for instance) and do not always carry the customer’s email. Run the trigger once, read the output and adjust the five expressions.

The SourceToDocs node returns file_url and file_name at the top level of its output. With Also generate PDF on and a Google Drive destination, the deliveries list holds one entry per destination, and a Drive entry’s result carries drive_url for the Google Doc and pdf_url for the PDF. The Message above uses the first entry, which is correct when the document has one destination.

The file sits in your Drive folder, so a customer can open the link only if that folder’s sharing allows it. Share the folder so that anyone with the link can view it, or attach the PDF to the email instead (see the FAQ). For customers, the attachment is usually the better choice: nothing in your Drive has to be opened up.

When Stripe’s own receipt is enough

Stripe can email its own receipt after a successful payment, with your branding on it. If that layout is all you need, switch it on and skip this workflow. Build the workflow when the receipt has to follow your own template, carry wording Stripe’s receipt does not have, or be filed as a Doc and a PDF in a Drive folder your team works from.

The Claude route

With the SourceToDocs connector added to Claude and the invoices-and-receipts skill installed, Claude can do the setup above for you: create the document, attach your data, read the template’s placeholders, show you the mapping, set the Drive delivery with a PDF and start the runs, one run per receipt, because a PDF comes with a single run only. You pick the template and the Drive folder through links Claude sends you, and you edit the template itself in Google Docs. The MCP page explains how to connect it.

The limit: Claude cannot read Stripe through SourceToDocs, a payment does not start it, and it does not email the customer. Export the payments from Stripe as a CSV, or keep them in a Google Sheet or an Airtable base, and hand that to Claude. That suits a handful of receipts for a period that has already closed; Claude tells you the number of runs before it starts. To issue a receipt the moment a payment succeeds, use the n8n workflow on this page.

Where this fits

An event, a template and a destination: the same pattern sits behind most generated documents. The document automation guide covers it beyond receipts, and the n8n node page lists what else the node can do.

Related

Common questions, answered

Is there a workflow file to import? +
Not yet. The importable file is being tested and will be added to this page. Until then, the four nodes and their settings above are everything the workflow contains, so you can build it by hand.
Why does the amount need an expression? +
Stripe sends amounts in the smallest unit of the currency, so 49.00 in euros or dollars arrives as 4900. The expression on this page divides by 100 and adds the currency code. Currencies without a minor unit, such as the Japanese yen, arrive as whole units: for those, leave out the division.
Can the email carry the PDF as an attachment instead of a link? +
Yes. The Drive delivery in the node's output also holds pdf_file_id. Pass it to n8n's Google Drive node with its download operation, then attach the downloaded file in the Gmail node. SourceToDocs itself returns links, never file contents.
Can the receipt list what was bought? +
The Checkout Session event does not include the line items. Retrieve them from Stripe's API in an extra step, for example with n8n's HTTP Request node, add a second data set for them in SourceToDocs, and send them as a JSON array under Options, Additional Data Sets. A Google Docs template can repeat a table row per item.
Does SourceToDocs connect to Stripe directly? +
No. SourceToDocs reads Airtable, Google Sheets and uploaded CSV files, or takes rows from an n8n workflow. Here n8n receives the event from Stripe and passes the values to the SourceToDocs node.