Recipe: Bridging HubSpot Tickets and GitHub Issues

Recipe: Bridging HubSpot Tickets and GitHub Issues

A bidirectional integration that connects your support team's HubSpot tickets to your engineering team's GitHub workflow — in real time, without leaving either tool.


About This Recipe

The problem with most HubSpot–GitHub workflows is that they're not really workflows at all — they're manual copy-paste routines. Someone creates a GitHub issue, someone else pastes the link into a HubSpot note, and then both sides pray the other one stays updated.

This recipe builds a proper bridge: a React UI card embedded in HubSpot ticket records, backed by a GCP Cloud Functions API, with GitHub webhooks pushing activity back in real time. Support reps get GitHub visibility without leaving HubSpot. Engineers get ticket context baked into their issues automatically.

It's more involved than a simple API wrapper — three separate systems to wire together, token encryption, webhook verification, bidirectional sync. But the result is a genuinely useful internal tool, not a demo.


Difficulty Advanced
Prep Time ~1 hour (three separate service setups)
Cook Time ~2 hours (backend deploy + HubSpot deploy)
Serves Support teams, success teams, and the engineers they hand off to
Allergens Requires a GCP billing account (even on free tier), GitHub App creation rights, and HubSpot Enterprise tier

Ingredients

Accounts & Access

  • 1× GitHub account with permission to create GitHub Apps
  • 1× Google Cloud Platform account with billing enabled (Cloud Functions, Firestore, Secret Manager, and Cloud KMS)
  • 1× HubSpot account — Enterprise tier required for UI extensions on ticket records
  • Node.js 20+
  • Google Cloud CLI (gcloud)
  • HubSpot CLI (npm install -g @hubspot/cli)

From the pantry (provided in this repo)

  • React 18 UI extension — the ticket record card
  • GCP Cloud Functions backend — all API logic, webhook handling, OAuth flow
  • Firestore data layer — ticket-issue links, settings, issue templates, OAuth tokens
  • KMS encryption service — AES-256-GCM token encryption with automatic key rotation
  • Secret Manager integration — all credentials stored securely, never in env vars

Method

Mise en Place — Three Services to Prep

This recipe has more prep than most. Get all three services set up before you touch the stove.

1. Create the GitHub App

In GitHub → Settings → Developer Settings → GitHub Apps, create a new app named GitSpot. You'll need:

  • A webhook URL pointing to your Cloud Functions endpoint (you'll get this after deploying the backend — you can update it after)
  • Repository permissions: Issues (Read/Write), Pull Requests (Read), Contents (Read), Metadata (Read), Projects (Read/Write)
  • Subscriptions: Issues, Issue comment, Pull request, Push, Installation
  • Download the private key (.pem) and note your App ID

Also create a GitHub OAuth App (separate from the GitHub App) to enable per-user connections — users authorise their own GitHub accounts to access personal repositories and projects.

2. Set Up GCP

gcloud projects create gitspot-prod --name="GitSpot" gcloud config set project gitspot-prod gcloud services enable cloudfunctions.googleapis.com firestore.googleapis.com \   secretmanager.googleapis.com cloudkms.googleapis.com cloudbuild.googleapis.com gcloud firestore databases create --location=us-central1 

Then create your six secrets in Secret Manager:

gcloud secrets create hubspot-github-app-id --replication-policy="automatic" gcloud secrets create hubspot-github-private-key --replication-policy="automatic" gcloud secrets create hubspot-github-webhook-secret --replication-policy="automatic" gcloud secrets create hubspot-github-hubspot-token --replication-policy="automatic" gcloud secrets create hubspot-github-oauth-client-id --replication-policy="automatic" gcloud secrets create hubspot-github-oauth-client-secret --replication-policy="automatic" 

Add your values to each secret — the GitHub App ID, private key (the .pem contents), webhook secret, HubSpot private app token, and OAuth client credentials.

3. Set your Cloud Functions URL in the source

Before deploying the HubSpot extension, open github-card.jsx and TemplateSettings.jsx and replace the API_BASE_URL placeholder with your actual Cloud Functions URL:

https://us-central1-YOUR_GCP_PROJECT_ID.cloudfunctions.net/git-spot-api 

Also update app-hsmeta.json with the same base URL in the permittedUrls.fetch array.


Part One: The Backend

Deploy the Cloud Functions API that sits between HubSpot and GitHub.

cd cloud npm install npm run deploy          # Deploys git-spot-api (main API) npm run deploy:webhooks # Deploys gitspot-webhooks (GitHub webhook handler) 

When the deploy completes, note your Cloud Functions URL. Go back to your GitHub App settings and set the Webhook URL:

https://us-central1-YOUR_GCP_PROJECT_ID.cloudfunctions.net/git-spot-api/api/webhooks/github 

Chef's note: KMS encryption is initialised automatically on first token save — no manual setup required. The key ring (gitspot-keys) and encryption key are created on demand.


Part Two: The HubSpot Extension

Deploy the React UI card to your HubSpot account.

cd hubspot hs auth hs project upload 

After upload, go to your HubSpot account and initialise the custom behavioral events:

GitSpot Settings page → "Initialize Custom Events"

This creates HubSpot custom event definitions for all GitHub activity types — issue created, issue linked, comment synced, etc. — making them available in HubSpot workflows and reports.

Add the card to ticket records:

Settings → Objects → Tickets → Record Customization → Default view → + → Card library → GitSpot


Part Three: Install the GitHub App

In your GitHub App settings, click Install App and select the organisation or repositories you want GitSpot to have access to. Team members can additionally connect their personal GitHub accounts from within the GitSpot settings page to access personal projects.


Plating Notes

  • Bidirectional comment sync uses note body markers to prevent echo loops — GitSpot-generated notes are tagged internally so they're never re-synced back to GitHub
  • Commits referencing a ticket ID in the format TICKET-{id} (configurable) appear automatically in the ticket's card via webhook — no action required from the engineer
  • GitHub OAuth tokens are encrypted at rest with AES-256-GCM and automatically re-keyed every 90 days via Cloud KMS
  • All credentials live in GCP Secret Manager, cached in memory for 5 minutes to reduce API calls

Serving Suggestion

The most useful automation this unlocks in HubSpot:

New support ticket created → GitSpot workflow action checks if a linked issue exists → If yes, branch on issue state → Closed issues trigger ticket resolution → Open issues trigger escalation or SLA timer

This turns GitHub issue state into a live signal inside your HubSpot automation — without anyone manually updating anything.


Shelf Life

This project connects systems that all version independently — HubSpot UI Extensions, the GitHub API, and GCP Cloud Functions runtimes each have their own release cadences. If something stops working after an update to any of these, check the respective changelogs before assuming the integration is broken. The most likely friction points are HubSpot platform version changes (see the repo README for current status) and GitHub App permission requirements, which GitHub occasionally tightens.


Full Recipe

Complete source code, setup guides, and architecture notes are in the GitHub repository:

github.com/Suixcity/gitspot


Part of a professional portfolio — view the project brief

People are talking about this blog post