What chrome_bridge + in-browser CSRF can DO in HubSpot
This file is the persistent inventory. Most of the underlying operations are already exercised by sot_reconciler.py, bv_hs_sync.py, engagement_reconciler.py, etc. — this doc surfaces them as a capability menu.
Auth path
No PAT needed. The in-browser fetch() from a logged-in HS tab uses session cookies + CSRF token (document.cookie.match(/csrf\.app=([^;]+)/)). All /api/crm/v3/* endpoints work.
Verified read operations (CRM v3)
js
// Standard pattern, proven across multiple skills:
const csrf = (document.cookie.match(/csrf\.app=([^;]+)/)||[])[1];
fetch("/api/crm/v3/objects/contacts/search?portalId=23273108", {
method: "POST",
headers: {"Content-Type":"application/json", "X-HubSpot-CSRF-hubspotapi": csrf},
body: JSON.stringify({...})
})
Object types proven readable: contacts, emails, notes, calls, meetings, tasks, deals.
Filter properties proven working: email, lead_source_notes (CONTAINS_TOKEN), hubspot_owner_id, hs_email_from_email, hs_email_to_email, hs_timestamp, hs_email_bcc_email, associations.contact.
Verified write operations
- PATCH contact properties:
sot_reconciler.pydoes this for email/phone/lead_source_notes/hs_additional_emails sync - Create contact:
create_missing_hs_contacts.pycreates Joseph-owned contacts from SoT - Both must reference
hubspot_owner_id=88361194per R-01 (M-01 anti-bypass enforced by PROOF 16)
Verified URL routes
/contacts/{portal}/record/0-1/{contact_id}— contact detail page/settings/{portal}/user-preferences/email— connected inbox + alias config (5 aliases visible)/settings/{portal}/objects/activities/email-logging— HS BCC dropbox + forwarding addresses live here:
23273108@bcc.hubspot.com
- Forwarding: 23273108@forward.hubspot.com
- Read inputs: FormControl49 and FormControl53 (IDs are stable per-portal)
Verified DOM operations
- Apply contact timeline filter: click element with text == "Emails" or "Notes" → timeline reflows
- Read engagement counters:
num_contacted_notes(any outbound activity),notes_last_contacted(timestamp), NOTnum_notes(counts ALL activity types — misleading name) — Joseph's HS dashboards usinghs_email_delivered_countwill under-report because that's HS-Marketing-Hub-only
Engagement-association truth
- Forward search via
associations.contact EQ {cid}filter on/objects/emails/searchreturns associated emails ✓ - Reverse query
/api/crm/v3/objects/emails/{id}/associations/contactsreturns 0 even when associations exist (confusing API quirk) - Authoritative method:
/api/crm/v3/objects/emails/{id}?associations=contacts&portalId={portal}returns the full associations block in the object response
Anti-pattern catalogued
"HS API doesn't expose X" — when uncertain, hit the endpoint with the in-browser CSRF and read the actual response. Most v3 endpoints are richer than the public docs suggest. Don't infer impossibility from docs; verify by execution.
Related
- R-01: Joseph-owned only (88361194) — every HS write MUST include this filter
- R-17: no fabricated capability blockers
~/Library/Application Support/SkyRun/sot_reconciler.py— canonical reference for HS read+write patterns~/Library/Application Support/SkyRun/engagement_reconciler.py— canonical reference for engagement-counter queries