HubSpot to Google Sheets and Notion Sync for Event Ops

I built a HubSpot to Google Sheets and Notion sync on Google Cloud to centralize event-specific data and cut manual work across teams.

HubSpot to Google Sheets and Notion Sync for Event Ops

Context

I completed this experiment for ProjectTogether, a nonprofit with 50+ employees and several teams collaborating across retreats, conferences, and workshops.

My goal for the project was to build a reliable full-code application via vibe coding with Generative AI (ChatGPT and Claude) while creating real business value.

Problem

Event managers needed to manage per-event, per-person details (invite status, plus-ones, who invited them, ticket status). HubSpot contained core contact data but not event-specific fields. Managing invitations directly in the CRM (customer relationship management) was not feasible given the pace and variability of events.

Managers spun up separate Google Sheets for each event to have flexibility and control of the set up. That flexibility came at a cost: fragmented data, manual copy-paste, and critical event status missing from HubSpot.

Approach & Process

I designed a lightweight, resilient data sync that let teams keep using flexible Google Sheets while keeping HubSpot contacts aligned.

  • Used Generative AI (ChatGPT and Claude) to accelerate boilerplate like auth scaffolding and API clients, and focused my time on data modeling, guardrails, and failure modes.
  • Defined a single Control sheet as the source of truth for sync jobs, one row per sync. Each row declares what to pull from HubSpot and where to write in Sheets or Notion.
  • Chose a serverless architecture on Google Cloud for low-ops reliability: Cloud Scheduler triggers a Dispatcher service, which publishes work to Google Cloud Pub/Sub. A Worker service performs the sync.
  • Implemented message ordering by sync_id so concurrent events did not step on each other. I used OpenID Connect (OIDC) push auth and a dead-letter queue (DLQ) for safety.
  • Tuned concurrency for parallel jobs and wrote detailed logs to Cloud Logging for traceability.
Control sheet I used to configure each sync.

Solution

The application synchronized HubSpot contact data to multiple Google Sheets and Notion databases.

Key components

  1. Control sheet (Google Sheets): One row per sync job with sync_id, active, sheet_id, tab_name, properties_csv, header_map_json, search_filter_json, frequency_cron, last_run_ms, due_in_ms. The Dispatcher reads it. The Worker reads it and writes timing fields.
  2. Dispatcher (Cloud Run): /dispatch endpoint. Reads due rows from the Control sheet and publishes one Pub/Sub message per sync_id.
  3. Pub/Sub: Topic hubspot-sync, push subscription with OIDC, message ordering keyed by sync_id, and a dead-letter queue hubspot-sync-dlq.
  4. Worker (Cloud Run): /pubsub endpoint. Loads the control row, queries HubSpot with a Private App token from Secret Manager, writes rows to the destination, and updates the Control sheet (last_run_ms and due_in_ms).
  5. Secrets & Auth: Secret Manager stores the HubSpot Private App token. Least-privilege service accounts handle Sheets read-write and Pub/Sub publish.
  6. Cloud Scheduler: A cron schedule (for example, every 10 minutes) calls the Dispatcher via a stable tag URL.
Flow chart of the HubSpot-to-Sheets-and-Notion sync I built.

How it works (high level)

  1. Cloud Scheduler calls the Dispatcher.
  2. The Dispatcher opens the Control sheet and publishes due sync_ids to Pub/Sub.
  3. Pub/Sub pushes each message to the Worker with OIDC.
  4. The Worker queries HubSpot, writes to the destination Google Sheet or Notion database, and updates the Control sheet.
  5. All steps emit structured logs to Cloud Logging.

Why this design

  • Kept event teams in Sheets, their preferred flexible tool, while reestablishing HubSpot as the source of truth for contact information.
  • Made configuration approachable. Non-technical users could enable new syncs by adding rows to the Control sheet.
  • Minimized operations with serverless services and clear, inspectable logs.
  • Preserved per-event autonomy with one row per sync configuration.
  • Ensured consistent data flow. Target fields are overwritten each run, which encouraged teams to update contact data directly in HubSpot.

Results

  • Centralized event participation data in managed Google Sheets and Notion databases while pulling authoritative contacts from HubSpot.
  • Reduced manual copy-paste and ad-hoc exports across teams.
  • Improved visibility. Event leads and ops worked from up-to-date sheets per event, refreshed on a schedule.

Challenges & Trade-offs

  • Modeling event specifics: HubSpot lacks a native per-event per-person object. I kept event state in Sheets with explicit headers and mapping. Trade-off: flexibility in Sheets versus centralized governance in the CRM.
  • Ordering and concurrency: Without ordering, multiple runs for the same event risked race conditions. I enforced Pub/Sub ordering by sync_id and added a guard so a new sync would not start while one was active.
  • Rate limits and backoff: HubSpot API throttling required conservative pagination and retries.
  • One-directionality: I intentionally implemented a one-way sync from HubSpot to Google Sheets and Notion. Two-way sync is technically possible but introduces reliability risks across multiple targets, especially since Sheets and Notion do not log changes at the property level.
  • Testing: I wrote extensive tests and split the code into focused modules I could test independently, which made the application reliable enough for operations.

Main Lessons

  • Vibe coding with GenAI accelerated build time and let me focus on design decisions, risk mitigation, and product thinking.
  • The Control sheet pattern scaled well. Non-technical users configured new syncs by adding rows while the code remained unchanged.
My development setup in VS Code. README.md documented goals, architecture, and how to configure the Control sheet for new syncs.

Possible Next Steps

  • Add support for more HubSpot objects such as Companies, Deals, Tickets, and Marketing Events.
  • Build an admin UI for run history, errors, and on-demand runs.
  • Add alerting, including error budget alerts and DLQ drains.
  • Harden rate-limit handling and batch sizes to support higher data volumes with efficient API usage.

Project Details

  • Timeline: August 2025
  • Role: Sole creator of the application (architecture, implementation, rollout)
  • Collaborators: Event managers for feedback
  • Tech Stack: Google Cloud (Cloud Run, Pub/Sub, Cloud Scheduler, Secret Manager, Cloud Logging), Google Sheets, Notion, HubSpot (Private App)