strapi-plugin-hubspot 0.16.8 → 0.16.9

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 CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.16.9 — 2026-10-01
6
+
7
+ ### Fixed
8
+ - **Brand-new leads silently lost their timeline note and company
9
+ association** after a Forms API conversion: the contact id was resolved
10
+ through the Search API, whose index lags by seconds to minutes on freshly
11
+ created records — so the contact the conversion itself just created was
12
+ invisible, and the note/association steps were skipped without a warning.
13
+ The lookup now uses the direct object endpoint
14
+ (`GET /crm/v3/objects/contacts/{email}?idProperty=email`), which reads the
15
+ object store (no index lag), with a short 404 retry (~1.6 s budget) since
16
+ the conversion is processed asynchronously. Existing contacts are
17
+ unaffected; any non-404 error still gives up immediately.
18
+
5
19
  ## 0.16.8 — 2026-09-24
6
20
 
7
21
  ### Added
@@ -2167,24 +2167,20 @@ async function loadFormShape(apiKey, formGuid) {
2167
2167
  return null;
2168
2168
  }
2169
2169
  }
2170
- async function findContactIdByEmail(apiKey, email) {
2171
- const res = await fetch("https://api.hubapi.com/crm/v3/objects/contacts/search", {
2172
- method: "POST",
2173
- headers: {
2174
- Authorization: `Bearer ${apiKey}`,
2175
- "Content-Type": "application/json"
2176
- },
2177
- body: JSON.stringify({
2178
- filterGroups: [
2179
- { filters: [{ propertyName: "email", operator: "EQ", value: email }] }
2180
- ],
2181
- properties: ["email"],
2182
- limit: 1
2183
- })
2184
- });
2185
- const body = await res.json().catch(() => ({}));
2186
- if (!res.ok) return void 0;
2187
- return body.results?.[0]?.id;
2170
+ async function findContactIdByEmail(apiKey, email, retryDelaysMs = [400, 1200]) {
2171
+ for (const delay of [0, ...retryDelaysMs]) {
2172
+ if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));
2173
+ const res = await fetch(
2174
+ `https://api.hubapi.com/crm/v3/objects/contacts/${encodeURIComponent(email)}?idProperty=email`,
2175
+ { headers: { Authorization: `Bearer ${apiKey}` } }
2176
+ );
2177
+ if (res.ok) {
2178
+ const body = await res.json().catch(() => ({}));
2179
+ return body.id || void 0;
2180
+ }
2181
+ if (res.status !== 404) return void 0;
2182
+ }
2183
+ return void 0;
2188
2184
  }
2189
2185
  function contextPayload(ctx) {
2190
2186
  const out = {};
@@ -2166,24 +2166,20 @@ async function loadFormShape(apiKey, formGuid) {
2166
2166
  return null;
2167
2167
  }
2168
2168
  }
2169
- async function findContactIdByEmail(apiKey, email) {
2170
- const res = await fetch("https://api.hubapi.com/crm/v3/objects/contacts/search", {
2171
- method: "POST",
2172
- headers: {
2173
- Authorization: `Bearer ${apiKey}`,
2174
- "Content-Type": "application/json"
2175
- },
2176
- body: JSON.stringify({
2177
- filterGroups: [
2178
- { filters: [{ propertyName: "email", operator: "EQ", value: email }] }
2179
- ],
2180
- properties: ["email"],
2181
- limit: 1
2182
- })
2183
- });
2184
- const body = await res.json().catch(() => ({}));
2185
- if (!res.ok) return void 0;
2186
- return body.results?.[0]?.id;
2169
+ async function findContactIdByEmail(apiKey, email, retryDelaysMs = [400, 1200]) {
2170
+ for (const delay of [0, ...retryDelaysMs]) {
2171
+ if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));
2172
+ const res = await fetch(
2173
+ `https://api.hubapi.com/crm/v3/objects/contacts/${encodeURIComponent(email)}?idProperty=email`,
2174
+ { headers: { Authorization: `Bearer ${apiKey}` } }
2175
+ );
2176
+ if (res.ok) {
2177
+ const body = await res.json().catch(() => ({}));
2178
+ return body.id || void 0;
2179
+ }
2180
+ if (res.status !== 404) return void 0;
2181
+ }
2182
+ return void 0;
2187
2183
  }
2188
2184
  function contextPayload(ctx) {
2189
2185
  const out = {};
@@ -133,7 +133,18 @@ export interface FormSubmitInput {
133
133
  }
134
134
  export declare function clearFormShapeCache(formGuid?: string): void;
135
135
  export declare function loadFormShape(apiKey: string, formGuid: string): Promise<FormShape | null>;
136
- export declare function findContactIdByEmail(apiKey: string, email: string): Promise<string | undefined>;
136
+ /**
137
+ * The contact's id right after a Forms API conversion, via the DIRECT object
138
+ * lookup (`idProperty=email`) — never the Search API: the search index lags by
139
+ * seconds to minutes on freshly created records, so a contact the conversion
140
+ * itself just created was invisible, silently costing brand-new leads their
141
+ * timeline note and company association. The object store has no such lag.
142
+ *
143
+ * The conversion is processed asynchronously on HubSpot's side, so a 404 is
144
+ * retried briefly (the delays are injectable for tests). Any other error
145
+ * (auth, 5xx) gives up immediately — retrying wouldn't change it.
146
+ */
147
+ export declare function findContactIdByEmail(apiKey: string, email: string, retryDelaysMs?: readonly number[]): Promise<string | undefined>;
137
148
  /**
138
149
  * Submit a marketing form. Extra fields HubSpot doesn't know on that form
139
150
  * 400 the whole request, so callers should filter first. A GDPR block on the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-hubspot",
3
- "version": "0.16.8",
3
+ "version": "0.16.9",
4
4
  "description": "The form builder for HubSpot in Strapi — multi-step forms with conditional fields mapped to real CRM properties, a public submit pipeline with retries, and save-time mapping validation.",
5
5
  "keywords": [
6
6
  "strapi",