@particle-academy/hubspot-js 0.3.3 → 0.4.0

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
@@ -7,6 +7,42 @@ The four packages share one version, because they are generated from one
7
7
  `provider/` definition and a version that meant something different in each
8
8
  would be a version nobody could reason about.
9
9
 
10
+ ## [0.4.0] — 2026-11-01
11
+
12
+ ### Added
13
+
14
+ - **The CRM core, read and write: 17 new actions.** For deals, contacts and
15
+ companies:
16
+ - `*_search`: property `filterGroups` and `sorts` (nested JSON), a text
17
+ `query`, the properties to return, and HubSpot's `after` cursor;
18
+ - `*_list`: page through every record, with `after` in and
19
+ `paging.next.after` out;
20
+ - `*_get`: one record, with `associations` when asked;
21
+ - `deal_create` and `company_create`;
22
+ - `*_update`: a PATCH of the given properties, declared `idempotent`.
23
+
24
+ `contact_create` is unchanged, because saved workflows depend on its
25
+ fields. Alongside them: `owner_list` (owner ids are NOT user ids; both are
26
+ published), `pipeline_list` (every deal pipeline with its stages in order)
27
+ and `property_list` (the property definitions of deals, contacts or
28
+ companies). Record ids are numeric STRINGS in every runtime.
29
+
30
+ ### Changed
31
+
32
+ - **Scopes:** the CRM core's read and write scopes for the three objects,
33
+ plus `crm.objects.owners.read` and `crm.schemas.*.read`. An installation made
34
+ under 0.3.x granted only `crm.objects.contacts.write`, so it **must be
35
+ re-authorised** before any new action works.
36
+ - **Drift now checks every action against HubSpot's own v3 documents:**
37
+ Contacts, Companies, Deals, Owners, Pipelines and Properties. That needs
38
+ the six documents, because HubSpot publishes the CRM one area per document.
39
+
40
+ ## [0.3.4] — 2026-09-12
41
+
42
+ ### Changed
43
+
44
+ - **Requires `fancy-connector-core` ≥ 0.4.0** — `particle-academy/fancy-connector-core` for php, `@particle-academy/fancy-connector-core` for js. The flow executors now pass a provider's declared `idempotencyMaxLength` through to the core's key derivation, and a named argument an older core does not accept is a fatal rather than a no-op, so the floor moves with it. This connector declares no limit and passes nothing, so nothing else changes for it; the bump is the floor alone.
45
+
10
46
  ## [0.3.3] — 2026-09-11
11
47
 
12
48
  ### Added
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # HubSpot
2
2
 
3
+ [![Fancified](art/fancified.svg)](https://particle.academy)
4
+
3
5
  HubSpot for [fancy-flow][flow] — as **four imported, versioned packages**, one
4
6
  per runtime. Not vendored source: a copy cannot be upgraded, and third-party APIs
5
7
  change.
@@ -50,7 +52,7 @@ HubSpot uses OAuth2 (authorization_code). The package DECLARES the exchange; the
50
52
 
51
53
  - **Authorize URL** — https://app.hubspot.com/oauth/authorize
52
54
  - **Token URL** — https://api.hubapi.com/oauth/v1/token
53
- - **Scopes** — `crm.objects.contacts.write`
55
+ - **Scopes** — `crm.objects.contacts.read`, `crm.objects.contacts.write`, `crm.objects.companies.read`, `crm.objects.companies.write`, `crm.objects.deals.read`, `crm.objects.deals.write`, `crm.objects.owners.read`, `crm.schemas.contacts.read`, `crm.schemas.companies.read`, `crm.schemas.deals.read`
54
56
  - **Access token lifetime** — 1800 seconds (30 minutes). A host that never refreshes works all afternoon and is broken by morning.
55
57
 
56
58
  The refresh tokens do **not** rotate: the same one is reusable, so a refresh may safely be retried and may run concurrently. Stated rather than assumed, because the opposite — a provider that spends the token and revokes the grant on a replay — looks identical until it happens.
@@ -65,6 +67,67 @@ HubSpot has a test estate on the same host, reached with credentials from a SEPA
65
67
 
66
68
  ### Actions
67
69
 
70
+ #### `company_create` — Create company
71
+
72
+ Create a company in HubSpot.
73
+
74
+ `POST /crm/v3/objects/companies` · **unsafe to replay** — a retried durable run does it TWICE
75
+
76
+ | Input | Required | What it is |
77
+ |---|---|---|
78
+ | `properties` | yes | HubSpot property name → value for the new company. Values are sent as strings, which is how HubSpot stores every property. |
79
+
80
+ #### `company_get` — Get company
81
+
82
+ Read one company, with its associations when asked.
83
+
84
+ `GET /crm/v3/objects/companies/{companyId}` · reads only — safe to replay
85
+
86
+ | Input | Required | What it is |
87
+ |---|---|---|
88
+ | `companyId` | yes | The company's HubSpot id (a numeric string). |
89
+ | `properties` | no | Comma-separated property names to return. HubSpot returns a small default set otherwise. |
90
+ | `associations` | no | Comma-separated object types whose associated ids to return. |
91
+
92
+ #### `company_list` — List companies
93
+
94
+ Page through every company in the portal.
95
+
96
+ `GET /crm/v3/objects/companies` · reads only — safe to replay
97
+
98
+ | Input | Required | What it is |
99
+ |---|---|---|
100
+ | `limit` | no | How many records per page, 1 to 100. HubSpot's own default is 10. |
101
+ | `after` | no | The cursor from the previous page's paging.next.after. Leave empty for the first page. |
102
+ | `properties` | no | Comma-separated property names to return. HubSpot returns a small default set otherwise. |
103
+ | `associations` | no | Comma-separated object types whose associated ids to return. |
104
+
105
+ #### `company_search` — Search companies
106
+
107
+ Find companies by property filters, sorts or a text query.
108
+
109
+ `POST /crm/v3/objects/companies/search` · reads only — safe to replay
110
+
111
+ | Input | Required | What it is |
112
+ |---|---|---|
113
+ | `filterGroups` | no | HubSpot's filterGroups: groups are ORed, filters inside a group are ANDed. Example: [{"filters":[{"propertyName":"amount","operator":"GT","value":"1000"}]}]. |
114
+ | `sorts` | no | Example: [{"propertyName":"createdate","direction":"DESCENDING"}]. |
115
+ | `query` | no | Searches the object's default text properties. |
116
+ | `properties` | no | Comma-separated property names to return. |
117
+ | `limit` | no | 1 to 200. |
118
+ | `after` | no | The cursor from the previous page's paging.next.after. |
119
+
120
+ #### `company_update` — Update company
121
+
122
+ Change properties on an existing company.
123
+
124
+ `PATCH /crm/v3/objects/companies/{companyId}` · idempotent — safe to replay
125
+
126
+ | Input | Required | What it is |
127
+ |---|---|---|
128
+ | `companyId` | yes | The company's HubSpot id (a numeric string). |
129
+ | `properties` | yes | HubSpot property name → value for the company. Values are sent as strings, which is how HubSpot stores every property. |
130
+
68
131
  #### `contact_create` — Create contact
69
132
 
70
133
  Create a contact in HubSpot.
@@ -80,6 +143,148 @@ Create a contact in HubSpot.
80
143
  | `phone` | no | Phone |
81
144
  | `lifecycleStage` | no | HubSpot's own stage names. A value it does not recognise is refused at write time, not at setup. |
82
145
 
146
+ #### `contact_get` — Get contact
147
+
148
+ Read one contact, with its associations when asked.
149
+
150
+ `GET /crm/v3/objects/contacts/{contactId}` · reads only — safe to replay
151
+
152
+ | Input | Required | What it is |
153
+ |---|---|---|
154
+ | `contactId` | yes | The contact's HubSpot id (a numeric string). |
155
+ | `properties` | no | Comma-separated property names to return. HubSpot returns a small default set otherwise. |
156
+ | `associations` | no | Comma-separated object types whose associated ids to return. |
157
+
158
+ #### `contact_list` — List contacts
159
+
160
+ Page through every contact in the portal.
161
+
162
+ `GET /crm/v3/objects/contacts` · reads only — safe to replay
163
+
164
+ | Input | Required | What it is |
165
+ |---|---|---|
166
+ | `limit` | no | How many records per page, 1 to 100. HubSpot's own default is 10. |
167
+ | `after` | no | The cursor from the previous page's paging.next.after. Leave empty for the first page. |
168
+ | `properties` | no | Comma-separated property names to return. HubSpot returns a small default set otherwise. |
169
+ | `associations` | no | Comma-separated object types whose associated ids to return. |
170
+
171
+ #### `contact_search` — Search contacts
172
+
173
+ Find contacts by property filters, sorts or a text query.
174
+
175
+ `POST /crm/v3/objects/contacts/search` · reads only — safe to replay
176
+
177
+ | Input | Required | What it is |
178
+ |---|---|---|
179
+ | `filterGroups` | no | HubSpot's filterGroups: groups are ORed, filters inside a group are ANDed. Example: [{"filters":[{"propertyName":"amount","operator":"GT","value":"1000"}]}]. |
180
+ | `sorts` | no | Example: [{"propertyName":"createdate","direction":"DESCENDING"}]. |
181
+ | `query` | no | Searches the object's default text properties. |
182
+ | `properties` | no | Comma-separated property names to return. |
183
+ | `limit` | no | 1 to 200. |
184
+ | `after` | no | The cursor from the previous page's paging.next.after. |
185
+
186
+ #### `contact_update` — Update contact
187
+
188
+ Change properties on an existing contact.
189
+
190
+ `PATCH /crm/v3/objects/contacts/{contactId}` · idempotent — safe to replay
191
+
192
+ | Input | Required | What it is |
193
+ |---|---|---|
194
+ | `contactId` | yes | The contact's HubSpot id (a numeric string). |
195
+ | `properties` | yes | HubSpot property name → value for the contact. Values are sent as strings, which is how HubSpot stores every property. |
196
+
197
+ #### `deal_create` — Create deal
198
+
199
+ Create a deal in HubSpot.
200
+
201
+ `POST /crm/v3/objects/deals` · **unsafe to replay** — a retried durable run does it TWICE
202
+
203
+ | Input | Required | What it is |
204
+ |---|---|---|
205
+ | `properties` | yes | HubSpot property name → value for the new deal. Values are sent as strings, which is how HubSpot stores every property. |
206
+
207
+ #### `deal_get` — Get deal
208
+
209
+ Read one deal, with its associations when asked.
210
+
211
+ `GET /crm/v3/objects/deals/{dealId}` · reads only — safe to replay
212
+
213
+ | Input | Required | What it is |
214
+ |---|---|---|
215
+ | `dealId` | yes | The deal's HubSpot id (a numeric string). |
216
+ | `properties` | no | Comma-separated property names to return. HubSpot returns a small default set otherwise. |
217
+ | `associations` | no | Comma-separated object types whose associated ids to return. |
218
+
219
+ #### `deal_list` — List deals
220
+
221
+ Page through every deal in the portal.
222
+
223
+ `GET /crm/v3/objects/deals` · reads only — safe to replay
224
+
225
+ | Input | Required | What it is |
226
+ |---|---|---|
227
+ | `limit` | no | How many records per page, 1 to 100. HubSpot's own default is 10. |
228
+ | `after` | no | The cursor from the previous page's paging.next.after. Leave empty for the first page. |
229
+ | `properties` | no | Comma-separated property names to return. HubSpot returns a small default set otherwise. |
230
+ | `associations` | no | Comma-separated object types whose associated ids to return. |
231
+
232
+ #### `deal_search` — Search deals
233
+
234
+ Find deals by property filters, sorts or a text query.
235
+
236
+ `POST /crm/v3/objects/deals/search` · reads only — safe to replay
237
+
238
+ | Input | Required | What it is |
239
+ |---|---|---|
240
+ | `filterGroups` | no | HubSpot's filterGroups: groups are ORed, filters inside a group are ANDed. Example: [{"filters":[{"propertyName":"amount","operator":"GT","value":"1000"}]}]. |
241
+ | `sorts` | no | Example: [{"propertyName":"createdate","direction":"DESCENDING"}]. |
242
+ | `query` | no | Searches the object's default text properties. |
243
+ | `properties` | no | Comma-separated property names to return. |
244
+ | `limit` | no | 1 to 200. |
245
+ | `after` | no | The cursor from the previous page's paging.next.after. |
246
+
247
+ #### `deal_update` — Update deal
248
+
249
+ Change properties on an existing deal.
250
+
251
+ `PATCH /crm/v3/objects/deals/{dealId}` · idempotent — safe to replay
252
+
253
+ | Input | Required | What it is |
254
+ |---|---|---|
255
+ | `dealId` | yes | The deal's HubSpot id (a numeric string). |
256
+ | `properties` | yes | HubSpot property name → value for the deal. Values are sent as strings, which is how HubSpot stores every property. |
257
+
258
+ #### `owner_list` — List owners
259
+
260
+ Page through the portal's owners: the users records can be assigned to.
261
+
262
+ `GET /crm/v3/owners` · reads only — safe to replay
263
+
264
+ | Input | Required | What it is |
265
+ |---|---|---|
266
+ | `email` | no | Only the owner with this email. |
267
+ | `limit` | no | How many records per page, 1 to 100. HubSpot's own default is 10. |
268
+ | `after` | no | The cursor from the previous page's paging.next.after. Leave empty for the first page. |
269
+
270
+ #### `pipeline_list` — List deal pipelines
271
+
272
+ Every deal pipeline in the portal, with its stages in order.
273
+
274
+ `GET /crm/v3/pipelines/deals` · reads only — safe to replay
275
+
276
+ Takes no input.
277
+
278
+ #### `property_list` — List property definitions
279
+
280
+ Every property defined on deals, contacts or companies: name, label, type and the options of an enumeration.
281
+
282
+ `GET /crm/v3/properties/{objectType}` · reads only — safe to replay
283
+
284
+ | Input | Required | What it is |
285
+ |---|---|---|
286
+ | `objectType` | yes | Object |
287
+
83
288
  ## Run it before you have credentials
84
289
 
85
290
  Every operation ships a **faker**, whether or not HubSpot has a sandbox. Set a
@@ -0,0 +1,26 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="128" height="28" role="img" aria-label="Fancified — built with Fancy UI">
2
+ <title>Fancified · built with Fancy UI</title>
3
+ <defs>
4
+ <linearGradient id="fcdBg" x1="0" y1="0" x2="1" y2="1">
5
+ <stop offset="0" stop-color="#6d28d9"/>
6
+ <stop offset=".5" stop-color="#9333ea"/>
7
+ <stop offset="1" stop-color="#d946ef"/>
8
+ </linearGradient>
9
+ <linearGradient id="fcdGloss" x1="0" y1="0" x2="0" y2="1">
10
+ <stop offset="0" stop-color="#ffffff" stop-opacity=".30"/>
11
+ <stop offset=".55" stop-color="#ffffff" stop-opacity="0"/>
12
+ </linearGradient>
13
+ <clipPath id="fcdClip"><rect width="128" height="28" rx="7"/></clipPath>
14
+ </defs>
15
+ <g clip-path="url(#fcdClip)">
16
+ <rect width="128" height="28" fill="url(#fcdBg)"/>
17
+ <rect width="34" height="28" fill="#3b0764" fill-opacity=".5"/>
18
+ <rect width="128" height="13" fill="url(#fcdGloss)"/>
19
+ <g fill="#fff">
20
+ <path transform="translate(17 14)" d="M0 -7 C.6 -2.2 2.2 -.6 7 0 C2.2 .6 .6 2.2 0 7 C-.6 2.2 -2.2 .6 -7 0 C-2.2 -.6 -.6 -2.2 0 -7 Z"/>
21
+ <path transform="translate(25.5 7.5) scale(.34)" fill-opacity=".85" d="M0 -7 C.6 -2.2 2.2 -.6 7 0 C2.2 .6 .6 2.2 0 7 C-.6 2.2 -2.2 .6 -7 0 C-2.2 -.6 -.6 -2.2 0 -7 Z"/>
22
+ </g>
23
+ <text x="81" y="18.5" fill="#ffffff" text-anchor="middle" font-family="'Segoe UI',Verdana,'DejaVu Sans',sans-serif" font-size="12" font-weight="700" letter-spacing=".3">Fancified</text>
24
+ </g>
25
+ <rect x=".5" y=".5" width="127" height="27" rx="6.5" fill="none" stroke="#ffffff" stroke-opacity=".22"/>
26
+ </svg>