Repository navigation
HubSpot audiences - #45
Merged
Merged
Conversation
Add audience_from_hubspot and `adwarp audience from-hubspot`: a HubSpot list's members, or a CRM search by property filters, becomes a LinkedIn matched audience of contacts or companies, the way Salesforce audiences work. - Auth is a read-only HubSpot private app token (crm.objects.contacts.read, crm.objects.companies.read, crm.lists.read) from ADWARP_HUBSPOT_TOKEN or ~/.adwarp/hubspot.json. No OAuth. - Reads every page through HubSpot's 2026-09 date-versioned API (search, list memberships, batch read), paced under the search and private app limits, and refuses a search above HubSpot's 10,000 result cap. - Records go through the CSV cleaner and upload path (cleanAudienceRows, uploadCleanedAudience), so contacts leave only as SHA256 hashes. The result reports read, valid, uploaded and skipped with reasons. - dryRun reads and cleans, returns counts and a masked sample, and makes no LinkedIn call. - Local only: the hosted route registers tools with hubspot: false, so the tool is hosted: false in tools.json. - Company CSV uploads now also respect the 300,000 row cap. - Tests stub fetch for HubSpot and use a fake LinkedIn client. - New docs/hubspot.md; tools, cli, config, hosted, index, README, AGENTS and the adwarp-audiences skill updated; tools.json regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Merged
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What it does
Adds HubSpot audiences, built the way Salesforce audiences work: a HubSpot list's members, or a CRM search by property filters, becomes a LinkedIn matched audience of contacts or companies.
audience_from_hubspot:name, optionaltype(contactorcompany), exactly 1 source (listId, orfiltersof property, operator and value, all ANDed, up to 6), optionalaccountIdanddryRun.adwarp audience from-hubspot -n <name> (--list <id> | --filter <prop>:<op>:<value> ...) [-t contact|company] [--dry-run].0-1contacts,0-2companies), and a mismatchedtypeis refused. A search builds a contact audience unlesstypesays company.email. Companies readnameanddomain, and domains becomehttps://websites.cleanAudienceRows(split out ofcleanAudienceCsv) anduploadCleanedAudience(split out ofuploadAudienceFromFile). Contact emails are trimmed, lowercased, validated, deduped and SHA-256 hashed, so only hashes leave the machine.read,valid,uploaded,skippedandskippedReasons(no email, invalid email, duplicate email, no name or domain, not found in HubSpot).HubSpot API
Checked against HubSpot's developer docs on 2026-10-18. HubSpot moved to date-based versions in March 2026 and ends support for the v3 paths in September 2027, so this pins
2026-09(HUBSPOT_API_VERSION), whose request and response shapes match v3 for these endpoints:POST /crm/objects/2026-09/{contacts|companies}/search:filterGroups[].filters[], 200 per page, cursor inpaging.next.after, at most 10,000 results per query (a broader search is refused before any upload), 5 requests per second.GET /crm/lists/2026-09/{listId}for the list'sobjectTypeId, thenGET /crm/lists/2026-09/{listId}/membershipsfor itsrecordIds.POST /crm/objects/2026-09/{contacts|companies}/batch/read, 100 ids per call. A 207 for deleted members counts them as not found.Scopes and token
A HubSpot private app (HubSpot now files these under Development, then Legacy apps) with 3 read-only scopes:
crm.objects.contacts.readcrm.objects.companies.readcrm.lists.readThe token goes in
~/.adwarp/hubspot.jsonas{ "accessToken": "..." }(mode 0600) or inADWARP_HUBSPOT_TOKEN, and the environment variable wins. No OAuth.env()now accepts names added after the rename, which have no legacy fallback.Local only
The hosted route now registers tools with
{ google: false, hubspot: false }, so the endpoint never registersaudience_from_hubspot. A token in a server environment variable would let any bring-your-own-credentials caller read that HubSpot account.gen-tools.mjschecks the new route call,tools.jsonhas the tool ashosted: false, anddocs/hosted.mdlists it under the hosted limitations.Change journal
Audience creation runs through
createLinkedInAds(), the same client and hook as Salesforce audiences, so it is captured exactly as they are. The journal tracks campaign groups, campaigns and creatives only, so DMP segment creation is journaled for neither source today.Tests
packages/core/test/hubspot.test.mjs, with HubSpot answered by a stubbed fetch and LinkedIn by a fake client. No real HubSpot or LinkedIn calls, and no credentials.aftercursors, ANDed filters, skip reasons, masked sample, dry run uploads nothingRun locally:
pnpm -r build,pnpm -r typecheck,pnpm test(16 + 28 + 10 pass),pnpm gen:toolswith no drift, andpnpm packplusscripts/smoke.mjs(37 tools listed byadwarp-mcpandadwarp mcp).Docs
New
docs/hubspot.md(order 12, after Salesforce; the pages after it moved down 1). Updatedtools.md,cli.md,config.md,hosted.md,index.md,platforms-linkedin-ads.md,skills.md, the README (tool list, CLI, configuration, a HubSpot integration section linking https://adwarp.dev/docs/hubspot),AGENTS.md,CONTRIBUTING.mdand theadwarp-audiencesskill.tools.jsonis regenerated.Not merged here: it ships in the next release, and the website's
/integrations/hubspotpage follows once that release is out.🤖 Generated with Claude Code