Skip to content

Bring HubSpot companies and deals into Commerce - #147

Merged
jameskurz-filecoin merged 6 commits into
mainfrom
feat/hubspot-commerce-handoff
Oct 23, 2026
Merged

jameskurz-filecoin merged 6 commits into
mainfrom
feat/hubspot-commerce-handoff

Conversation

@jameskurz-filecoin

@jameskurz-filecoin jameskurz-filecoin commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Implements the HubSpot → Commerce product spec as a read-only HubSpot integration: bring a HubSpot company or deal into Commerce as a staff partner through one reviewed handoff, keep its context and provenance, continue into Commerce forms, and browse HubSpot's pipeline inside Sales.

Runbook: docs/operations/hubspot-handoff.md.

What ships

  • Read adapter (packages/integrations/src/hubspot): typed HubSpotReader over HubSpot's public API with a server-side private-app token (HUBSPOT_ACCESS_TOKEN, read scopes only), bounded reads, one retry on short Retry-After, PDF-only downloads from HubSpot hosts via short-lived signed URLs, plus a deterministic fictional portal used by the demo and tests.
  • Contracts (packages/contracts/src/hubspot.ts): handoff input, plan, three-way drift classification and default decisions, exact per-currency pipeline totals, record-URL parsing. Partner contacts gain a stable ID, an optional chosen primary, and HubSpot source identity.
  • Storage (migration 001483): approved connection (one active), append-only typed links (unique per company/destination and per partner/portal), imports with immutable plan + selected-source snapshot keyed by operation ID, and per-document items whose resolved states are final. pgTAP and integration tests cover uniqueness, immutability, retries and concurrent handoffs.
  • Handoff (/internal/hubspot → /internal/hubspot/review): search-first picker (names or pasted record URL; foreign-portal links explained), deal → company choice with nothing preselected, one review page with company details, people, sales context, documents and in-place More control, footer summary derived from the same plan the server commits, similar-partner inspection, exact-link resolution, stale-review detection by per-item digests, one transaction for partner + link + import + audit.
  • Receipt and continuation on the partner page: record saved vs handoff complete, per-document attach/retry/use-existing/exclude-with-reason, and Continue in Commerce into the partner form, a deal registration (end customer + source as suggestions; no amount→size, no protection from close date) and an MNDA (company + chosen contact as suggestions).
  • Partner HubSpot section: link, last checked vs updated in HubSpot, changed-since-import count with Review updates, current approved details, associated deals with the attribution disclaimer, import history.
  • Pipeline (Sales → Pipeline): stage distribution, dense table and read-only board from one filtered list; totals per currency, missing amounts counted, partial coverage stated; side panel with Bring into Commerce.
  • Owner console → Integrations → HubSpot connection: a four-step setup checklist showing where the environment stands, token portal + read-only check, explicit audience acceptance, approved pipelines and properties (sensitive excluded), notes/files, default pipeline, owner mapping, disconnect. Administrators also see Set up HubSpot on Partners until it is connected.

Deliberate decisions

  • Permissions reuse the existing model (sales:read view, contract:write import, staff:manage settings) with the audience named and accepted at activation, instead of three new permissions.
  • No persistent CRM cache: live reads go through a 5-minute in-process cache keyed by connection ID+version, gated by a DB connection check on every request, so disconnect is immediate.
  • Deferred (documented): accounts as destination, presets, bulk import, reversal, saved views, HubSpot-side card.

Connecting it on commerce.fil.one

  1. HubSpot super admin: Settings → Integrations → Private apps → create "Fil One Commerce" with read scopes crm.objects.{companies,deals,contacts,owners}.read, crm.schemas.{companies,deals,contacts}.read (+ files for PDFs). Copy the token.
  2. GitHub → Settings → Environments → production (and staging): add secret HUBSPOT_ACCESS_TOKEN; then Actions → Deploy → Run workflow on main.
  3. /internal/owner/hubspot: approve pipelines, properties, notes/files, owner mapping and the audience → Activate connection.
  4. Partners → From HubSpot, or Sales → Pipeline.

Adversarial review

Two independent reviews (backend/security and spec/UX) found 28 issues; fixed here: decisions recomputed when HubSpot or the partner changes under a review (no silent overwrite of a concurrent edit), server-side refusal of undecided both-changed fields and uninspected similar partners, stable IDs for hand-typed contacts so they match instead of duplicating, pipeline allowlist enforced in pipeline/search/review, partner version conflicts refresh in place, precise DB error mapping, idempotent concurrent attach, body-read timeouts, bounded stale cache, file-name limits, registration end-customer choice without retyping, accessibility (scroll padding, focus targets, panel anchor), copy and plurals.

🤖 Generated with Claude Code

@jameskurz-filecoin
jameskurz-filecoin marked this pull request as ready for review October 22, 2026 21:36
@jameskurz-filecoin
jameskurz-filecoin merged commit 253ea5b into main Oct 23, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant