Skip to content

Commit 8a769ed

Browse files
committed
feat: add hubspot action (#41)
1 parent bc4b27f commit 8a769ed

31 files changed

Lines changed: 1053 additions & 0 deletions

.gitlab-ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ include:
2525
- woocommerce-action
2626
- shopware-action
2727
- twilio-action
28+
- hubspot-action
2829

2930
test-node:
3031
image: node:24.10.0

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ The "central" place for all actions — a monorepo containing integrations provi
77
| Action | Description |
88
|--------|-------------|
99
| [GLS](docs/Actions/GLS/overview.md) | GLS ShipIT integration for creating and managing shipments |
10+
| HubSpot | HubSpot CRM integration: webhooks for contacts/deals/companies plus create, update, search, associate, and note functions |
1011

1112
## ENV
1213

actions/hubspot-action/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.env
2+
node_modules/
3+
dist/

actions/hubspot-action/Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM node:24.10.0-alpine
2+
3+
WORKDIR /app
4+
5+
COPY package.json package-lock.json* ./
6+
7+
RUN npm install
8+
9+
COPY . .
10+
11+
RUN npm run build
12+
13+
CMD ["npm", "run", "start"]

actions/hubspot-action/README.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# HubSpot Action
2+
3+
HubSpot CRM integration for the **Hercules** automation platform. Lets flows
4+
react to HubSpot webhook events and create, update, search, associate, and
5+
annotate CRM records through the official
6+
[`@hubspot/api-client`](https://www.npmjs.com/package/@hubspot/api-client) SDK
7+
(HubSpot CRM v3 API).
8+
9+
## Configuration
10+
11+
Configured via the action's `ConfigurationDefinition`s:
12+
13+
| Identifier | Type | Required | Description |
14+
|------------|------|----------|-------------|
15+
| `access_token` | TEXT | yes | Private-app access token (Bearer) for the HubSpot CRM API. |
16+
| `app_id` | TEXT | no | HubSpot app id, used when managing webhook subscriptions. |
17+
| `client_secret` | TEXT | no | HubSpot app client secret, used to validate the `X-HubSpot-Signature` on inbound webhooks. |
18+
19+
Beyond the shared Hercules variables (`HERCULES_AUTH_TOKEN`,
20+
`HERCULES_AQUILA_URL`, `HERCULES_ACTION_ID`, `HERCULES_SDK_VERSION`), the only
21+
provider-specific credential required is the HubSpot private-app access token.
22+
23+
### Creating a private app
24+
25+
1. In HubSpot, go to **Settings → Integrations → Private Apps**.
26+
2. Create a private app and grant the CRM scopes you need
27+
(`crm.objects.contacts.*`, `crm.objects.deals.*`, `crm.objects.companies.*`,
28+
`crm.schemas.*`, and note/engagement scopes for `hubspotAddNote`).
29+
3. Copy the access token into the action's `access_token` config.
30+
31+
### Webhooks (triggers)
32+
33+
The triggers use the shared `Rest` (webhook) event pattern. Point a HubSpot app
34+
**webhook subscription** at the URL generated for the trigger. HubSpot delivers
35+
a JSON array of change events; each event is exposed to the flow as a
36+
`HUBSPOT_WEBHOOK_EVENT`. To verify signatures, configure `client_secret`.
37+
38+
## Triggers
39+
40+
| Identifier | Fires on |
41+
|------------|----------|
42+
| `HubSpotContactCreatedWebhook` | `contact.creation` |
43+
| `HubSpotDealCreatedWebhook` | `deal.creation` |
44+
| `HubSpotDealStageChangedWebhook` | `deal.propertyChange` on `dealstage` |
45+
| `HubSpotFormSubmittedWebhook` | form submission (lead capture) |
46+
47+
## Functions
48+
49+
| Identifier | Signature | Description |
50+
|------------|-----------|-------------|
51+
| `hubspotCreateContact` | `(Email, PropertiesJson?): HUBSPOT_CONTACT` | Create a contact. |
52+
| `hubspotUpdateContact` | `(ContactId, PropertiesJson): HUBSPOT_CONTACT` | Update a contact. |
53+
| `hubspotGetContactByEmail` | `(Email): HUBSPOT_CONTACT` | Look up a contact by email. |
54+
| `hubspotCreateDeal` | `(Name, Pipeline, Stage, Amount?, PropertiesJson?): HUBSPOT_DEAL` | Create a deal. |
55+
| `hubspotUpdateDeal` | `(DealId, PropertiesJson): HUBSPOT_DEAL` | Update a deal. |
56+
| `hubspotCreateCompany` | `(Name, PropertiesJson?): HUBSPOT_COMPANY` | Create a company. |
57+
| `hubspotAddNote` | `(Body, PropertiesJson?): HUBSPOT_NOTE` | Create a note engagement. |
58+
| `hubspotAssociate` | `(FromObjectType, FromId, ToObjectType, ToId): BOOLEAN` | Create the default association between two objects. |
59+
| `hubspotSearchObjects` | `(ObjectType, Query): HUBSPOT_SEARCH_RESULT` | Full-text search a CRM object type. |
60+
61+
### Property maps
62+
63+
HubSpot CRM properties are a dynamic key/value bag, so functions accept extra
64+
properties as a **JSON object string** (e.g.
65+
`{"firstname":"Ada","lifecyclestage":"lead"}`) rather than a fixed set of typed
66+
parameters. Values are coerced to strings, matching the HubSpot API.
67+
68+
## Data types
69+
70+
`HUBSPOT_CONTACT`, `HUBSPOT_DEAL`, `HUBSPOT_COMPANY`, `HUBSPOT_NOTE` (all share
71+
the SDK's `SimplePublicObject` envelope: `id`, `properties`, timestamps,
72+
`archived`), plus `HUBSPOT_SEARCH_RESULT` and `HUBSPOT_WEBHOOK_EVENT`.
73+
74+
## Development
75+
76+
```bash
77+
npm install
78+
npm run typecheck
79+
npm run build
80+
npm run test
81+
```
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "@code0-tech/hubspot-action",
3+
"version": "1.0.0",
4+
"private": true,
5+
"type": "module",
6+
"main": "dist/index.js",
7+
"scripts": {
8+
"dev": "tsx watch src/index.ts",
9+
"typecheck": "tsc --noEmit",
10+
"build": "vite build",
11+
"test": "vitest run",
12+
"start": "node dist/index.js"
13+
},
14+
"dependencies": {
15+
"@code0-tech/hercules": "^1.1.1",
16+
"@hubspot/api-client": "^14.0.0"
17+
},
18+
"devDependencies": {
19+
"@types/node": "^22.0.0",
20+
"tsx": "^4.0.0",
21+
"typescript": "^5.9.3",
22+
"vite": "^8.0.3",
23+
"vitest": "^4.1.10"
24+
}
25+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules";
2+
import { z } from "zod";
3+
import { hubSpotObjectSchema } from "./hubspotObjectBase.js";
4+
5+
export const HubSpotCompanySchema = hubSpotObjectSchema();
6+
export type HubSpotCompany = z.infer<typeof HubSpotCompanySchema>;
7+
8+
@Identifier("HUBSPOT_COMPANY")
9+
@Name({ code: "en-US", content: "HubSpot company" })
10+
@DisplayMessage({ code: "en-US", content: "HubSpot company" })
11+
@Schema(HubSpotCompanySchema)
12+
export class HubSpotCompanyDataType {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules";
2+
import { z } from "zod";
3+
import { hubSpotObjectSchema } from "./hubspotObjectBase.js";
4+
5+
export const HubSpotContactSchema = hubSpotObjectSchema();
6+
export type HubSpotContact = z.infer<typeof HubSpotContactSchema>;
7+
8+
@Identifier("HUBSPOT_CONTACT")
9+
@Name({ code: "en-US", content: "HubSpot contact" })
10+
@DisplayMessage({ code: "en-US", content: "HubSpot contact" })
11+
@Schema(HubSpotContactSchema)
12+
export class HubSpotContactDataType {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules";
2+
import { z } from "zod";
3+
import { hubSpotObjectSchema } from "./hubspotObjectBase.js";
4+
5+
export const HubSpotDealSchema = hubSpotObjectSchema();
6+
export type HubSpotDeal = z.infer<typeof HubSpotDealSchema>;
7+
8+
@Identifier("HUBSPOT_DEAL")
9+
@Name({ code: "en-US", content: "HubSpot deal" })
10+
@DisplayMessage({ code: "en-US", content: "HubSpot deal" })
11+
@Schema(HubSpotDealSchema)
12+
export class HubSpotDealDataType {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules";
2+
import { z } from "zod";
3+
import { hubSpotObjectSchema } from "./hubspotObjectBase.js";
4+
5+
export const HubSpotNoteSchema = hubSpotObjectSchema();
6+
export type HubSpotNote = z.infer<typeof HubSpotNoteSchema>;
7+
8+
@Identifier("HUBSPOT_NOTE")
9+
@Name({ code: "en-US", content: "HubSpot note" })
10+
@DisplayMessage({ code: "en-US", content: "HubSpot note" })
11+
@Schema(HubSpotNoteSchema)
12+
export class HubSpotNoteDataType {}

0 commit comments

Comments
 (0)