Closed-won HubSpot deal to a spreadsheet
Every deal that changes stage is read back and appended to a Google Sheet for finance.
Drag to pan, pinch to zoom. This is the real graph the template installs.
Purpose
Every deal that changes stage is read back and appended to a Google Sheet for finance.
How it works
What it does
- Subscribes to HubSpot deal property changes, narrowed to
dealstage. - Reads the deal back for its name, amount, stage and close date.
- Appends one row per change to a Google Sheet.
Validation
This template is contract-validated: the unit suite checks every node type, every config key and the whole graph against the same rules a saved workflow passes. It has not been run against the live provider — the ids, names and columns below are placeholders to replace with your own.
Nodes 3
- 01Deal stage changedtrigger.hubspot_eventTrigger
- 02Read the dealhubspot.get_dealApp
- 03Append to the sheetgoogle_sheets.appendApp
View template JSON
{
"format": "mantle-workflow/v1",
"id": "hubspot-deal-won-to-sheet",
"name": "Closed-won HubSpot deal to a spreadsheet",
"graph": {
"nodes": [
{
"id": "event",
"type": "trigger.hubspot_event",
"label": "Deal stage changed",
"config": {
"app_id": "0000000",
"subscription_types": [
"deal.propertyChange"
],
"property_name": "dealstage"
},
"worker_type": "generic-worker",
"position": {
"x": 60,
"y": 220
}
},
{
"id": "deal",
"type": "hubspot.get_deal",
"label": "Read the deal",
"config": {
"deal_id": "{{ $json.objectId }}",
"properties": "dealname,amount,dealstage,closedate"
},
"worker_type": "generic-worker",
"position": {
"x": 380,
"y": 220
}
},
{
"id": "append",
"type": "google_sheets.append",
"label": "Append to the sheet",
"config": {
"spreadsheet_id": "1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789",
"range": "Deals!A:E",
"values": [
[
"{{ $now }}",
"{{ steps.deal.id }}",
"{{ steps.deal.properties.dealname }}",
"{{ steps.deal.properties.amount }}",
"{{ steps.deal.properties.dealstage }}"
]
]
},
"worker_type": "generic-worker",
"position": {
"x": 700,
"y": 220
}
}
],
"edges": [
{
"from": "event",
"to": "deal"
},
{
"from": "deal",
"to": "append"
}
]
}
}Requirements
The connections and permissions each step needs, from the node contracts in the catalog.
- HubSpotBearer token
Used by Deal stage changed, Read the deal.
Credential: {"token", "developer_api_key", "client_secret"} · the HubSpot private-app token, or {"token": "…"}
- The webhooks API authenticates with the developer API key of the account that owns the app, and signatures are verified with the app's client secret. The private-app token alone cannot do either.
- HubSpot's webhook target URL is a single app-level setting, so one HubSpot app backs one published workflow. Publishing a second workflow against the same app_id fails rather than stealing the first one's events.
- One app serves every HubSpot account that installed it: branch on portalId when more than one portal is in play.
- Filtering is done by HubSpot — subscription_types and property_name decide what is ever sent, so nothing arrives here to be discarded.
- Runs are deduplicated per portal, subscription, object, occurredAt and eventId together — HubSpot does not guarantee eventId is unique — so a delivery retried up to ten times over twenty-four hours starts one run.
- Deliveries must be answered within five seconds, which is why the run is accepted and executed asynchronously.
- Google SheetsOAuth 2.0
Used by Append to the sheet.
Credential: {"client_id", "client_secret", "refresh_token"}
- The refresh token must have the spreadsheets scope.
Setup
app_idis your HubSpot developer app id;property_namenarrows the subscription to one property.- To record only closed won, add a Filter node between the two steps on
properties.dealstage. - Set
spreadsheet_idandrange, then pick HubSpot and Google Sheets connections.
Sample input and output
Synthetic examples: node-contract samples or placeholders, never customer data.
Input
What the Deal stage changed trigger receives, as its node contract samples it.
{
"appId": "",
"attemptNumber": 0,
"changeSource": "",
"eventAction": "propertyChange",
"eventId": "1",
"objectId": "12345",
"objectType": "contact",
"occurredAt": 0,
"occurredAtIso": "",
"portalId": "62515",
"propertyName": "email",
"propertyValue": "ada@example.com",
"subscriptionId": "",
"subscriptionType": "contact.propertyChange"
}Output
Append to the sheet google_sheets.append
{
"updated_cells": 0,
"updated_range": "",
"updated_rows": 1
}Failure handling
What the graph does when a step fails, read from its retry settings, error branches and error policies.
- A step that fails, after any retries, fails the run. The failed step is kept with its input and error.
See it run, then make it yours.
Templates install as drafts. Edit any step, and Workflow Health re-checks the graph on every save.