Snapshot your HubSpot pipeline weekly in Google Sheets using a Claude Code skill
Install this skill
Download the skill archive and extract it into your .claude/skills/ directory.
pipeline-snapshot.skill.zipThis skill works with any agent that supports the Claude Code skills standard, including Claude Code, Claude Cowork, OpenAI Codex, and Google Antigravity.
- One of the agents listed above
- HubSpot private app with
crm.objects.deals.readandcrm.schemas.deals.readscopes - Google Cloud service account with Sheets API enabled, shared as Editor on the target sheet
Why a Claude Code skill?
The other approaches in this guide run on a fixed weekly schedule. An Claude Code skill is different. You tell Claude what you want in plain language, and the skill gives it enough context to do it reliably.
That means you can say:
- "Snapshot the pipeline to Google Sheets"
- "Snapshot just the Enterprise pipeline deals over $100K"
- "Show me pipeline metrics for the last 4 weeks from the snapshot sheet"
The skill contains workflow guidelines, API reference materials, and a sheet format guide that the agent reads on demand. When you invoke the skill, Claude reads these files, writes a script on the fly, runs it, and reports results. If you ask for something different next time — a different pipeline, additional metrics, or a filtered snapshot — the agent adapts without you touching any code.
How it works
The skill directory has three parts:
SKILL.md— workflow guidelines telling the agent what steps to follow, which env vars to use, and what pitfalls to avoidreferences/— HubSpot API patterns (deals search, pipelines endpoint) and Google Sheets API patterns (append row, service account auth) so the agent calls the right APIstemplates/— a sheet format guide describing the expected column layout so snapshots are consistent across runs
When invoked, the agent reads SKILL.md, consults the reference and template files as needed, writes a Python script, executes it, and reports what it appended. The reference files act as guardrails — the agent knows exactly which endpoints to hit and what the responses look like, so it doesn't have to guess.
What is a Claude Code skill?
An Claude Code skill is a reusable command you add to your project that Claude Code can run on demand. Skills live in a .claude/skills/ directory and are defined by a SKILL.md file that tells the agent what the skill does, when to run it, and what tools it's allowed to use.
In this skill, the agent doesn't run a pre-written script. Instead, SKILL.md provides workflow guidelines and points to reference files — API documentation, sheet format guides — that the agent reads to generate and execute code itself. This is the key difference from a traditional script: the agent can adapt its approach based on what you ask for while still using the right APIs and output formats.
Once installed, you can invoke a skill as a slash command (e.g., /pipeline-snapshot), or the agent will use it automatically when you give it a task where the skill is relevant. Skills are portable — anyone who clones your repo gets the same commands.
Step 1: Create the skill directory
mkdir -p .claude/skills/pipeline-snapshot/{templates,references}This creates the layout:
.claude/skills/pipeline-snapshot/
├── SKILL.md # workflow guidelines + config
├── templates/
│ └── snapshot-sheet-format.md # expected column layout
└── references/
└── hubspot-sheets-api.md # HubSpot + Google Sheets API patternsStep 2: Write the SKILL.md
Create .claude/skills/pipeline-snapshot/SKILL.md:
---
name: pipeline-snapshot
description: Capture a snapshot of the HubSpot pipeline and append a summary row to Google Sheets with date, total value, deal count, and per-stage breakdowns
disable-model-invocation: true
allowed-tools: Bash, Read
---
## Goal
Fetch all active deals from HubSpot, calculate pipeline metrics (total value, deal count, per-stage breakdowns), and append a snapshot row to Google Sheets.
## Configuration
Read these environment variables:
- `HUBSPOT_ACCESS_TOKEN` — HubSpot private app token (required)
- `GOOGLE_SERVICE_ACCOUNT_KEY` — path to Google Cloud service account JSON key file (required)
- `GOOGLE_SPREADSHEET_ID` — Google Spreadsheet ID (required)
Default pipeline: `default`. The user may request a specific pipeline ID.
## Workflow
1. Validate that all required env vars are set. If any are missing, print which ones and exit.
2. Fetch pipeline stage definitions from HubSpot to build a stage ID → label map and an ordered list of stages. See `references/hubspot-sheets-api.md` for the endpoint.
3. Search for all active deals in the pipeline. Paginate if needed (max 100 per request). See `references/hubspot-sheets-api.md` for the search endpoint.
4. Calculate metrics: total pipeline value, deal count, and per-stage count + value.
5. Build a snapshot row following the format in `templates/snapshot-sheet-format.md`.
6. Append the row to Google Sheets using the Sheets API v4 append method. See `references/hubspot-sheets-api.md` for the append endpoint.
7. Print a summary of the snapshot: date, total value, deal count, and per-stage breakdowns.
## Important notes
- Deal amounts may be null or empty. Treat missing amounts as $0.
- The Deals Search API returns max 100 results per request. Use the `after` cursor from `paging.next.after` to paginate.
- Stage IDs in deal properties are internal identifiers — map them to human-readable labels using the pipelines endpoint.
- The Google service account must be shared as an Editor on the target spreadsheet.
- Use `valueInputOption: USER_ENTERED` when appending so numbers are treated as numbers, not text.
- The default pipeline ID is `default` in most HubSpot portals. If the user has renamed it, they may need to provide the pipeline ID.
- Use the `requests` library for HubSpot calls and `google-api-python-client` + `google-auth` for Sheets. Install with pip if needed.Understanding the SKILL.md
Unlike a script-based skill, this SKILL.md doesn't contain a Run: command pointing to a script. Instead, it provides:
| Section | Purpose |
|---|---|
| Goal | Tells the agent what outcome to produce |
| Configuration | Which env vars to read and what defaults to use |
| Workflow | Numbered steps with pointers to reference files |
| Important notes | Non-obvious context that prevents common mistakes |
The allowed-tools: Bash, Read setting lets the agent both read reference files and execute code. The agent writes its own script based on the workflow steps and reference materials.
Step 3: Add reference files
templates/snapshot-sheet-format.md
Create .claude/skills/pipeline-snapshot/templates/snapshot-sheet-format.md:
# Snapshot Sheet Format
Each snapshot row follows this column layout. The agent should build the row array in this exact order.
## Column layout
```
Date | Total Value | Deal Count | Stage 1 (Count) | Stage 1 (Value) | Stage 2 (Count) | Stage 2 (Value) | ...
```
- **Date**: ISO 8601 format (YYYY-MM-DD)
- **Total Value**: Sum of all deal amounts (numeric, no formatting)
- **Deal Count**: Total number of active deals (integer)
- **Stage columns**: For each pipeline stage in display order, two columns: count (integer) and value (numeric)
## Example row
```
2026-03-03 | 2400000 | 47 | 12 | 680000 | 8 | 420000 | 15 | 890000 | 7 | 310000 | 5 | 100000
```
## Notes
- Stages should follow the display order from the HubSpot pipelines API.
- Use `valueInputOption: USER_ENTERED` so Google Sheets parses numbers correctly.
- If no deals exist in a stage, use 0 for both count and value.
- The sheet headers should already exist. The agent appends data below them.
- Format dollar amounts as raw numbers (not strings with $ or commas) — let Google Sheets handle formatting.references/hubspot-sheets-api.md
Create .claude/skills/pipeline-snapshot/references/hubspot-sheets-api.md:
# HubSpot + Google Sheets API Reference
## Get pipeline stages (HubSpot)
Build a stage ID → label map and determine stage order.
**Request:**
```
GET https://api.hubapi.com/crm/v3/pipelines/deals
Authorization: Bearer <HUBSPOT_ACCESS_TOKEN>
```
**Response shape:**
```json
{
"results": [
{
"id": "default",
"label": "Sales Pipeline",
"stages": [
{ "id": "appointmentscheduled", "label": "Appointment Scheduled" },
{ "id": "qualifiedtobuy", "label": "Qualified to Buy" },
{ "id": "closedwon", "label": "Closed Won" },
{ "id": "closedlost", "label": "Closed Lost" }
]
}
]
}
```
## Search for deals (HubSpot)
**Request:**
```
POST https://api.hubapi.com/crm/v3/objects/deals/search
Authorization: Bearer <HUBSPOT_ACCESS_TOKEN>
Content-Type: application/json
```
**Body:**
```json
{
"filterGroups": [
{
"filters": [
{
"propertyName": "pipeline",
"operator": "EQ",
"value": "default"
}
]
}
],
"properties": ["dealname", "amount", "dealstage", "closedate", "createdate"],
"sorts": [{ "propertyName": "amount", "direction": "DESCENDING" }],
"limit": 100
}
```
- `limit` max is 100. Use the `after` cursor from `paging.next.after` to paginate.
- `amount` may be null or empty for deals without a dollar value set.
- `dealstage` is an internal stage ID — map it using the pipelines endpoint above.
**Response shape:**
```json
{
"total": 47,
"results": [
{
"id": "12345",
"properties": {
"dealname": "Acme Corp — Enterprise",
"amount": "150000",
"dealstage": "qualifiedtobuy",
"closedate": "2026-04-15T00:00:00.000Z",
"createdate": "2026-01-10T08:00:00.000Z"
}
}
],
"paging": {
"next": { "after": "100" }
}
}
```
## Append row to Google Sheets
Use the Sheets API v4 append method to add a snapshot row.
**Authentication:**
```python
from google.oauth2 import service_account
from googleapiclient.discovery import build
creds = service_account.Credentials.from_service_account_file(
"<path_to_key_file>",
scopes=["https://www.googleapis.com/auth/spreadsheets"]
)
service = build("sheets", "v4", credentials=creds)
```
**Append request:**
```python
result = service.spreadsheets().values().append(
spreadsheetId="<GOOGLE_SPREADSHEET_ID>",
range="Sheet1!A1",
valueInputOption="USER_ENTERED",
insertDataOption="INSERT_ROWS",
body={"values": [row]},
).execute()
```
- `valueInputOption: USER_ENTERED` means Google Sheets parses values as if typed — numbers stay as numbers.
- `insertDataOption: INSERT_ROWS` appends after the last row with data.
- The `range` parameter (`Sheet1!A1`) tells Sheets where to start looking for data. It appends after the last row, not at A1.
- The service account must be shared as an Editor on the spreadsheet.Step 4: Test the skill
Invoke the skill conversationally:
/pipeline-snapshotClaude will read the SKILL.md, check the reference files, write a script, install any missing dependencies, run it, and report the results. A typical run looks like:
Fetching pipeline stages... found 6 stages
Fetching deals... found 47 active deals
Calculating metrics...
Total pipeline: $2,400,000
Active deals: 47
Appointment Scheduled: 12 deals ($680,000)
Qualified to Buy: 8 deals ($420,000)
Proposal Sent: 15 deals ($890,000)
Appending row to Google Sheets...
Done. Row appended for 2026-03-03.What the Google Sheet looks like
| A | B | C | D | E | F | |
|---|---|---|---|---|---|---|
| 1 | Date | Total Value | Deal Count | Appt Sched | Qualified | Proposal |
| 2 | Mar 3, 2026 | $2.4M | 47 | 12 ($680K) | 8 ($420K) | 15 ($890K) |
| 3 | Feb 24, 2026 | $2.1M | 43 | 10 ($580K) | 7 ($380K) | 14 ($820K) |
| 4 | Feb 17, 2026 | $1.9M | 41 | 9 ($520K) | 6 ($340K) | 13 ($780K) |
Because the agent generates code on the fly, you can also make ad hoc requests:
- "Snapshot just the Enterprise pipeline" — the agent changes the pipeline filter
- "Add weighted pipeline to the snapshot" — the agent multiplies stage values by close rates
- "Show me the last 4 weeks of snapshots from the sheet" — the agent reads the sheet instead of appending
Make sure your Google Sheet has headers in the first row matching your pipeline stages. If no deals exist, the skill correctly reports zero values — that's not an error.
Step 5: Schedule it (optional)
Option A: Cron + Claude CLI
# Run every Monday at 8 AM
0 8 * * 1 cd /path/to/your/project && claude -p "Run /pipeline-snapshot" --allowedTools 'Bash(*)' 'Read(*)'Option B: GitHub Actions + Claude
name: Weekly Pipeline Snapshot
on:
schedule:
- cron: '0 13 * * 1' # 8 AM ET = 1 PM UTC, Mondays only
workflow_dispatch: {}
jobs:
snapshot:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: anthropics/claude-code-action@v1
with:
prompt: "Run /pipeline-snapshot"
allowed_tools: "Bash(*),Read(*)"
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
HUBSPOT_ACCESS_TOKEN: ${{ secrets.HUBSPOT_ACCESS_TOKEN }}
GOOGLE_SERVICE_ACCOUNT_KEY: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }}
GOOGLE_SPREADSHEET_ID: ${{ secrets.GOOGLE_SPREADSHEET_ID }}Option C: Cowork Scheduled Tasks
Claude Desktop's Cowork supports built-in scheduled tasks. Open a Cowork session, type /schedule, and configure the cadence — hourly, daily, weekly, or weekdays only. Each scheduled run has full access to your connected tools, plugins, and MCP servers.
Scheduled tasks only run while your computer is awake and Claude Desktop is open. If a run is missed, Cowork executes it automatically when the app reopens. For always-on scheduling, use GitHub Actions (Option B) instead. Available on all paid plans (Pro, Max, Team, Enterprise).
0 13 * * 1 runs at 1 PM UTC (8 AM ET) on Mondays. GitHub Actions cron may also have up to 15 minutes of delay.
Troubleshooting
When to use this approach
- You want on-demand snapshots — before a board meeting, after a big push, or at the end of a quarter
- You want conversational flexibility — different pipelines, additional metrics, or filtered snapshots
- You're iterating on what to track — the agent adapts without you editing code
- You're already using Claude Code and want pipeline snapshots as a quick command
- You want to run tasks in the background via Claude Cowork while focusing on other work
When to switch approaches
- You need reliable weekly scheduling with zero manual intervention → use n8n or the code approach
- You want a no-code setup with a visual builder → use Make
- You need snapshots running 24/7 with zero cost and no LLM usage → use the Code + Cron approach
Common questions
Why not just use a script?
A script runs the same way every time. The Claude Code skill adapts to what you ask — different pipelines, additional metrics like weighted pipeline, filtered snapshots for specific stages or deal sizes. The reference files ensure it calls the right APIs even when improvising, so you get flexibility without sacrificing reliability.
Does this use Claude API credits?
Yes. The agent reads skill files and generates code each time. Typical cost is $0.01-0.05 per invocation depending on the number of deals and how much the agent needs to read. The HubSpot and Google Sheets APIs themselves are free.
Can I snapshot multiple pipelines in one run?
Yes. Ask the agent to remove the pipeline filter or specify multiple pipeline IDs. The reference file documents the pipeline filter, so the agent knows how to adjust the query. You can also ask for separate rows per pipeline.
Do I need to set up the Google Sheet headers first?
Yes. Create headers in the first row matching your pipeline stages (see templates/snapshot-sheet-format.md). The agent appends data below the headers. If you ask the agent to "set up the snapshot sheet," it can create the headers for you too.
Cost
- Claude API — $0.01-0.05 per invocation (the agent reads files and generates code)
- HubSpot API — included in all plans, no per-call cost
- Google Sheets API — free within standard quotas (500 requests per 100 seconds)
- GitHub Actions (if scheduled) — free tier includes 2,000 minutes/month
Looking to scale your AI operations?
We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.