Turn a HubSpot deal into a quote PDF with n8n
When a deal reaches your quote stage in HubSpot, the workflow builds the quote from your Google Docs template, saves the Doc and a PDF to Google Drive, and writes the link back to the deal.
- Source
- HubSpot
- 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
- 01
HubSpot Trigger: Deal Property Changed
The event is Deal Property Changed and the property is Deal Stage, so the workflow starts whenever a deal moves to another stage.
- 02
If: the new stage is your quote stage
Compares the new stage value in the event with the internal ID of the stage that should produce a quote. Every other stage change stops here.
- 03
Loop Over Items: Batch Size 1
The SourceToDocs node starts one run for all the items it receives. The loop hands on one deal at a time, so two deals moved together still get one quote each.
- 04
HubSpot: Deal, Get
The event carries the deal's ID and the changed property, not the deal. This node reads the deal with the properties the quote needs: name, amount and close date.
- 05
HubSpot: Company, Get
Reads the company associated with the deal, so the quote is addressed to the customer's name and not to the deal title.
- 06
SourceToDocs: Document, Generate
Rows Come From is set to the Map Columns mode. Customer, Quote number, Scope and Total come from HubSpot; Valid until is thirty days from the run. Because this is a single run, switching on Also generate PDF delivers a PDF beside the Google Doc.
- 07
HubSpot: Deal, Update
Writes file_url into a custom deal property, Quote link, so the quote is one click away from the deal record.
| HubSpot field | Template placeholder |
|---|---|
| Company name | {{ customer }} |
| Deal ID | {{ quote_number }} |
| Deal name | {{ scope }} |
| Amount | {{ total }} |
| (run date + 30 days) | {{ valid_until }} |
Or have Claude set it up
The Proposals, quotes and contracts skill
works through the SourceToDocs connector at https://app.sourcetodocs.com/api/mcp. Install the skill, connect, then paste:
Use the sales-documents skill. I saved my open HubSpot deals as "hubspot-deals.csv", one row per deal, with the columns Customer, Quote number, Scope, Total and Valid until. Quote number is unique per deal. Set up a Google Docs quote document from that file with a PDF next to the Doc. I will pick the quote template and the Drive folder "Quotes" when you send me the links. Show me the placeholder mapping before you run anything, then generate the quote for the first deal only.
To turn a HubSpot deal into a quote PDF with n8n, trigger on the deal’s stage change, let only your quote stage through, read the deal and its company, pass those values to the SourceToDocs node to fill your Google Docs quote template, and write the returned link back to the deal. One deal is one run, so SourceToDocs can deliver a PDF next to the Google Doc. The build is seven nodes, described below.
What you need
- A HubSpot account with a deal pipeline, and a custom deal property named
Quote link(single-line text) to hold the result. - A simple quote template in Google Docs with just the placeholders
{{ customer }},{{ quote_number }},{{ scope }},{{ total }}and{{ valid_until }}. The quote 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 Slides or 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. - Two HubSpot credentials in n8n. The HubSpot Trigger node uses a HubSpot Developer API credential, which comes from an app you create in a HubSpot developer account. The HubSpot node that reads and updates the deal uses its own credential.
Set up the document in SourceToDocs
SourceToDocs has no HubSpot connection. The template and its settings live in SourceToDocs, and n8n sends the deal’s values.
- Create a Google Docs document.
- Upload a small CSV with the header row
Customer,Quote number,Scope,Total,Valid untiland one sample line as the data source; it becomes a data set. The sample is enough, because the workflow sends the real row in place of reading the source. - Each column gets an alias, a snake-case identifier derived from its name. The template uses the alias: column
Quote numberis{{ quote_number }}. - On the Document step, pick your Google Doc. Its Variables panel lists the exact placeholders for your data set; click one to copy it.
- Add a Google Drive distribution and choose the folder.
- Switch on Also generate PDF in the Output card.
- Leave split off. A split document produces no PDFs.
Build it in n8n
Add the nodes in this order. Connect the true output of If to Loop Over Items, the loop output to the first HubSpot node, then on through the second HubSpot node and SourceToDocs to the last HubSpot node, and that node back into Loop Over Items. Rename the two reading nodes to Get deal and Get company; the expressions below refer to them by those names.
| Node | Operation | Key settings |
|---|---|---|
| HubSpot Trigger | Event: Deal Property Changed | Property: Deal Stage. |
| If | One condition | The new stage value from the event, for example {{ $json.propertyValue }}, is equal to the internal ID of your quote stage. |
| Loop Over Items | Batch Size: 1 | No other settings. |
HubSpot (Get deal) | Resource: Deal. Operation: Get | Deal to Get, by ID: the deal’s ID from the event, for example {{ $json.objectId }}. Among the node’s optional fields, add Deal Properties to Include and pick Deal Name, Amount and Close Date. |
HubSpot (Get company) | Resource: Company. Operation: Get | Company to Get, by ID: the associated company ID from the Get deal output. |
| SourceToDocs | Resource: Document. Operation: Generate | Document: your quote 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. |
| HubSpot | Resource: Deal. Operation: Update | Deal to Update, by ID: the same deal ID, read from the loop, for example {{ $('Loop Over Items').item.json.objectId }}. Under the fields to update, add Custom Properties, pick Quote link and set it to {{ $json.file_url }}. |
The stage value in the event is the stage’s internal ID, not its label. Move a test deal into the stage and read the value in the trigger’s output panel; HubSpot’s pipeline settings show the same ID. objectId and propertyValue are the field names of HubSpot’s property change event: confirm both in that output, and drag the values from there if your trigger shows other names.
The five column values in the SourceToDocs node:
| Column | Value |
|---|---|
| Customer | The company name from Get company, for example {{ $json.properties.name.value }} |
| Quote number | Q- followed by the deal’s ID, for example Q-{{ $('Loop Over Items').item.json.objectId }} |
| Scope | The deal name from Get deal, for example {{ $('Get deal').item.json.properties.dealname.value }} |
| Total | The amount from Get deal, for example {{ $('Get deal').item.json.properties.amount.value }} |
| Valid until | {{ $now.plus({ days: 30 }).toFormat('d LLLL yyyy') }} |
The paths into the HubSpot output depend on the node version and on the properties you include. Run the two reading nodes once on a test deal and drag each value from the output panel into the column, which writes the right expression for you. The same goes for the associated company ID in Get company.
The Column names are the data set’s column names, case included, not the template placeholders, which use the aliases. Mapped values are sent as they resolve, so format Total in the expression the way it should read in the quote, currency included.
Which link ends up on the deal
The SourceToDocs node returns file_url and file_name at the top level of its output, and that link to the Google Doc is what the last node writes. With Also generate PDF on, the deliveries list in the same output holds one entry per destination, and a Google Drive entry’s result carries drive_url for the Doc and pdf_url for the PDF. To keep the PDF link on the deal too, add a second custom property, Quote PDF, and set it to {{ $json.deliveries[0].result.pdf_url }}.
If your team reads the timeline more than the properties, replace the last node with the HubSpot node’s Engagement resource, operation Create: an engagement, for example a task, that carries the link and is associated with the deal shows up in the deal’s activity.
Set it up with Claude instead
The sales-documents skill lets Claude do the SourceToDocs part for you through the MCP connector: create the document, attach the data, read the template’s placeholders, set the Google Drive delivery with a PDF, and start a run. You pick the template file and the Drive folder yourself, through links Claude hands you, and Claude cannot edit the template.
The limit is HubSpot. Claude cannot read HubSpot through SourceToDocs and does not write the link back to the deal, so this route starts from an export: save the deals as a CSV, or keep them in Airtable or a Google Sheet, with the columns the template’s placeholders are named after (Customer, Quote number, Scope, Total, Valid until). Claude prints what the file holds, so the validity date has to be a column; working it out from the run date is something the n8n build does. A quote with a PDF is one run for one deal: Claude sends that deal’s row with the run and gives you the link to the Doc and to the PDF. It suits quotes you produce on request. The n8n workflow is the route that reacts to the stage change on its own, and it can drive the same document Claude set up.
When HubSpot’s own quotes are enough
HubSpot has a quotes tool of its own. This workflow is for the case where the quote has to come from your own Google Docs template, sit in Google Drive beside the rest of the client’s files, and stay editable as a Doc before the PDF goes out. The n8n node page lists what the SourceToDocs node does in a workflow.
Where this fits
A quote is the first document in a longer chain. When the same deal later needs a proposal, approval before sending, or pricing rules, the proposal automation guide covers the wider design, and the automations library has the neighbouring recipes.
Related
- proposal automation: the full guide this recipe belongs to.
- Free quote template for Google Docs: the template this workflow fills.
- The SourceToDocs n8n node: every action and setting.
- The SourceToDocs MCP server: what Claude can do through the connector.