fullstackgtm 0.38.0 → 0.38.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/connectors/hubspot.js +6 -2
- package/package.json +1 -1
- package/src/connectors/hubspot.ts +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ The path to 1.0 is planned in [docs/roadmap-to-1.0.md](./docs/roadmap-to-1.0.md)
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.38.1] — 2026-06-23
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`enrich acquire` LinkedIn creates failed with HubSpot 400.** The resolve-first
|
|
15
|
+
search before a `create_record` filtered on the *canonical* match key
|
|
16
|
+
(`linkedin`) instead of the HubSpot property name (`hs_linkedin_url`); HubSpot
|
|
17
|
+
400s on a filter against a property it doesn't have, so every LinkedIn-sourced
|
|
18
|
+
lead failed at apply. The connector now translates the match key through
|
|
19
|
+
`HUBSPOT_DEFAULT_FIELD_MAPPINGS.contacts` before searching (email is unaffected
|
|
20
|
+
— its canonical name already equals the property name). Regression test added.
|
|
21
|
+
|
|
10
22
|
## [0.38.0] — 2026-06-23
|
|
11
23
|
|
|
12
24
|
### Added
|
|
@@ -493,13 +493,17 @@ export function createHubspotConnector(options) {
|
|
|
493
493
|
}
|
|
494
494
|
return { operationId: operation.id, status: "applied", detail: `Resolved/created company "${matchValue}" (${id}).`, providerData: { id } };
|
|
495
495
|
}
|
|
496
|
-
// contact — resolve-first on the dedupe key (email by default)
|
|
496
|
+
// contact — resolve-first on the dedupe key (email by default). The match
|
|
497
|
+
// key is canonical (e.g. "linkedin"); translate it to the HubSpot property
|
|
498
|
+
// name ("hs_linkedin_url") before searching — HubSpot 400s on a filter
|
|
499
|
+
// against a property it doesn't have (there is no property named "linkedin").
|
|
497
500
|
const matchKey = payload.matchKey || "email";
|
|
501
|
+
const searchProperty = HUBSPOT_DEFAULT_FIELD_MAPPINGS.contacts[matchKey] ?? matchKey;
|
|
498
502
|
const matchKeyLower = `${matchKey}:${matchValue.toLowerCase()}`;
|
|
499
503
|
if (createdContactsByMatch.has(matchKeyLower)) {
|
|
500
504
|
return { operationId: operation.id, status: "skipped", detail: `Contact ${matchKey}=${matchValue} already created earlier in this run; not duplicating.`, providerData: { id: createdContactsByMatch.get(matchKeyLower), existing: true } };
|
|
501
505
|
}
|
|
502
|
-
const existing = await searchContactsBy(
|
|
506
|
+
const existing = await searchContactsBy(searchProperty, matchValue);
|
|
503
507
|
if (existing.length > 0) {
|
|
504
508
|
createdContactsByMatch.set(matchKeyLower, existing[0]);
|
|
505
509
|
return { operationId: operation.id, status: "skipped", detail: `Contact ${matchKey}=${matchValue} already exists (${existing.join(", ")}); resolve-first declined to create.`, providerData: { id: existing[0], existing: true } };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fullstackgtm",
|
|
3
|
-
"version": "0.38.
|
|
3
|
+
"version": "0.38.1",
|
|
4
4
|
"description": "Open-source agentic GTM ops framework: canonical GTM data model, pluggable deterministic audits, reviewable dry-run patch plans, approval-gated write-back with conflict detection, and cross-system entity resolution. HubSpot, Salesforce, and Stripe connectors included.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Full Stack GTM LLC <ryan@fullstackgtm.com> (https://fullstackgtm.com)",
|
|
@@ -595,13 +595,17 @@ export function createHubspotConnector(options: HubspotConnectorOptions): Requir
|
|
|
595
595
|
return { operationId: operation.id, status: "applied", detail: `Resolved/created company "${matchValue}" (${id}).`, providerData: { id } };
|
|
596
596
|
}
|
|
597
597
|
|
|
598
|
-
// contact — resolve-first on the dedupe key (email by default)
|
|
598
|
+
// contact — resolve-first on the dedupe key (email by default). The match
|
|
599
|
+
// key is canonical (e.g. "linkedin"); translate it to the HubSpot property
|
|
600
|
+
// name ("hs_linkedin_url") before searching — HubSpot 400s on a filter
|
|
601
|
+
// against a property it doesn't have (there is no property named "linkedin").
|
|
599
602
|
const matchKey = payload.matchKey || "email";
|
|
603
|
+
const searchProperty = HUBSPOT_DEFAULT_FIELD_MAPPINGS.contacts[matchKey] ?? matchKey;
|
|
600
604
|
const matchKeyLower = `${matchKey}:${matchValue.toLowerCase()}`;
|
|
601
605
|
if (createdContactsByMatch.has(matchKeyLower)) {
|
|
602
606
|
return { operationId: operation.id, status: "skipped", detail: `Contact ${matchKey}=${matchValue} already created earlier in this run; not duplicating.`, providerData: { id: createdContactsByMatch.get(matchKeyLower), existing: true } };
|
|
603
607
|
}
|
|
604
|
-
const existing = await searchContactsBy(
|
|
608
|
+
const existing = await searchContactsBy(searchProperty, matchValue);
|
|
605
609
|
if (existing.length > 0) {
|
|
606
610
|
createdContactsByMatch.set(matchKeyLower, existing[0]);
|
|
607
611
|
return { operationId: operation.id, status: "skipped", detail: `Contact ${matchKey}=${matchValue} already exists (${existing.join(", ")}); resolve-first declined to create.`, providerData: { id: existing[0], existing: true } };
|