n8n-nodes-idb2b 3.2.4 → 3.2.6

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/README.md CHANGED
@@ -47,13 +47,23 @@ npm install n8n-nodes-idb2b
47
47
  | Update | Update an existing company |
48
48
  | Delete | Delete a company |
49
49
 
50
+ ### Activity
51
+
52
+ | Operation | Description |
53
+ |-----------|-------------|
54
+ | Get All | Retrieve all activities for a specific company/lead |
55
+ | Get | Fetch a single activity by ID |
56
+ | Create | Create a new activity linked to a company or contact |
57
+ | Update | Update an existing activity |
58
+ | Delete | Delete an activity |
59
+
50
60
  ## How to Use
51
61
 
52
62
  ### Basic workflow
53
63
 
54
- 1. Add the **IDB2B CRM** node to your workflow
64
+ 1. Add the **IDB2B API** node to your workflow
55
65
  2. Select your saved credential under **Credential to connect with**
56
- 3. Choose a **Resource** (Contact or Company)
66
+ 3. Choose a **Resource** (Activity, Contact, or Company)
57
67
  4. Choose an **Operation** (Get All, Get, Create, Update, Delete)
58
68
  5. Fill in the required parameters and execute
59
69
 
@@ -90,7 +100,31 @@ Required:
90
100
  - **Name**: Company name
91
101
 
92
102
  Optional (under Additional Fields):
93
- - Any additional company fields supported by the API
103
+ - **Website**, **Description**, **Industry ID**, **Size ID**, **Status ID**, **Source ID**, **Owner ID**
104
+
105
+ ### Get All Activities
106
+
107
+ Required:
108
+ - **Company ID**: The company/lead to list activities for
109
+ - **Limit** / **Page**: Pagination controls
110
+
111
+ ### Create Activity
112
+
113
+ Required:
114
+ - **Subject**: Title of the activity
115
+ - **Associate With**: Choose **Company** or **Contact**
116
+ - **Company ID** or **Contact ID** depending on the above
117
+
118
+ Optional (under Additional Fields):
119
+ - **Description**, **Date & Time**, **Icon**, **User ID**
120
+
121
+ ### Update Activity
122
+
123
+ Required:
124
+ - **Activity ID**
125
+
126
+ Optional (under Additional Fields):
127
+ - Any fields to change: **Subject**, **Description**, **Date & Time**, **Icon**, **User ID**
94
128
 
95
129
  ## Example Workflows
96
130
 
@@ -110,6 +144,17 @@ Webhook → IDB2B Create Contact
110
144
  - Additional Fields.LinkedIn URL: {{ $json.linkedin_url }}
111
145
  ```
112
146
 
147
+ ### Log a call activity after a deal closes
148
+
149
+ ```
150
+ Webhook → IDB2B Create Activity
151
+ - Subject: "Call with {{ $json.contact_name }}"
152
+ - Associate With: Company
153
+ - Company ID: {{ $json.company_id }}
154
+ - Additional Fields.Description: {{ $json.call_notes }}
155
+ - Additional Fields.Date & Time: {{ $json.call_time }}
156
+ ```
157
+
113
158
  ### Paginate through all contacts
114
159
 
115
160
  ```
@@ -136,6 +181,15 @@ npm run lint
136
181
 
137
182
  ## Version History
138
183
 
184
+ ### v3.2.5
185
+ - Added **Activity** resource with full CRUD operations (Get All, Get, Create, Update, Delete)
186
+ - Activities can be linked to a company or a contact
187
+ - Get All Activities scoped to a specific company via `GET /leads/:id/activities`
188
+
189
+ ### v3.2.4
190
+ - Added LinkedIn URL and social links support for contacts
191
+ - Included socials in response data for create/update operations
192
+
139
193
  ### v2.0.3
140
194
  - Fixed endpoint paths (`/contacts`, `/companies`)
141
195
  - Fixed access token parsing from login response
@@ -4,6 +4,7 @@ exports.IDB2B = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
5
  const contactProperties_1 = require("./descriptions/contactProperties");
6
6
  const companyProperties_1 = require("./descriptions/companyProperties");
7
+ const activityProperties_1 = require("./descriptions/activityProperties");
7
8
  // Import new utility modules
8
9
  const validators_1 = require("./utils/validators");
9
10
  const errorHandler_1 = require("./utils/errorHandler");
@@ -39,6 +40,10 @@ class IDB2B {
39
40
  type: "options",
40
41
  noDataExpression: true,
41
42
  options: [
43
+ {
44
+ name: "Activity",
45
+ value: "activity",
46
+ },
42
47
  {
43
48
  name: "Contact",
44
49
  value: "contact",
@@ -50,8 +55,10 @@ class IDB2B {
50
55
  ],
51
56
  default: "contact",
52
57
  },
58
+ activityProperties_1.activityOperations,
53
59
  contactProperties_1.contactOperations,
54
60
  companyProperties_1.companyOperations,
61
+ ...activityProperties_1.activityFields,
55
62
  ...contactProperties_1.contactFields,
56
63
  ...companyProperties_1.companyFields,
57
64
  ],
@@ -82,6 +89,7 @@ class IDB2B {
82
89
  let body = undefined;
83
90
  let qs = {};
84
91
  let initialBody = undefined;
92
+ let useFormData = false;
85
93
  if (resource === "contact") {
86
94
  if (operation === "getAll") {
87
95
  method = "GET";
@@ -144,6 +152,79 @@ class IDB2B {
144
152
  endpoint = `${constants_1.ENDPOINTS.CONTACTS}/${(0, common_1.sanitizeId)(contactId)}`;
145
153
  }
146
154
  }
155
+ else if (resource === "activity") {
156
+ if (operation === "getAll") {
157
+ method = "GET";
158
+ const getAllScope = this.getNodeParameter("getAllScope", i);
159
+ const limit = this.getNodeParameter("limit", i, constants_1.PAGINATION.DEFAULT_LIMIT);
160
+ const page = this.getNodeParameter("page", i, constants_1.PAGINATION.DEFAULT_PAGE);
161
+ if (getAllScope === "contact") {
162
+ const contactId = this.getNodeParameter("getAllContactId", i);
163
+ endpoint = `/contacts/${(0, common_1.sanitizeId)(contactId)}/activities`;
164
+ }
165
+ else {
166
+ const companyId = this.getNodeParameter("companyId", i);
167
+ endpoint = `/leads/${(0, common_1.sanitizeId)(companyId)}/activities`;
168
+ }
169
+ qs.limit = limit;
170
+ qs.page = page;
171
+ }
172
+ else if (operation === "get") {
173
+ method = "GET";
174
+ const activityId = this.getNodeParameter("activityId", i);
175
+ endpoint = `${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(activityId)}`;
176
+ }
177
+ else if (operation === "create") {
178
+ method = "POST";
179
+ endpoint = constants_1.ENDPOINTS.ACTIVITIES;
180
+ const subject = this.getNodeParameter("subject", i);
181
+ const associateWith = this.getNodeParameter("associateWith", i);
182
+ const additionalFields = this.getNodeParameter("additionalFields", i, {});
183
+ if (!subject || !subject.trim()) {
184
+ throw new Error("Subject is required to create an activity");
185
+ }
186
+ // Must send as multipart/form-data — the /activities endpoint uses
187
+ // FilesInterceptor which does not parse JSON bodies reliably
188
+ const formPayload = { subject: subject.trim() };
189
+ if (associateWith === "company") {
190
+ const companyId = this.getNodeParameter("activityCompanyId", i);
191
+ formPayload.company_id = companyId;
192
+ }
193
+ else {
194
+ const contactId = this.getNodeParameter("activityContactId", i);
195
+ formPayload.contact_id = contactId;
196
+ }
197
+ // Merge optional additional fields
198
+ Object.entries(additionalFields).forEach(([key, value]) => {
199
+ if (value !== undefined && value !== "" && key !== "subject") {
200
+ formPayload[key] = value;
201
+ }
202
+ });
203
+ body = formPayload;
204
+ initialBody = formPayload;
205
+ useFormData = true;
206
+ }
207
+ else if (operation === "update") {
208
+ method = "PATCH";
209
+ const activityId = this.getNodeParameter("activityId", i);
210
+ endpoint = `${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(activityId)}`;
211
+ const additionalFields = this.getNodeParameter("additionalFields", i, {});
212
+ const updatePayload = {};
213
+ Object.entries(additionalFields).forEach(([key, value]) => {
214
+ if (value !== undefined && value !== "") {
215
+ updatePayload[key] = value;
216
+ }
217
+ });
218
+ body = updatePayload;
219
+ initialBody = updatePayload;
220
+ useFormData = true;
221
+ }
222
+ else if (operation === "delete") {
223
+ method = "DELETE";
224
+ const activityId = this.getNodeParameter("activityId", i);
225
+ endpoint = `${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(activityId)}`;
226
+ }
227
+ }
147
228
  else if (resource === "company") {
148
229
  if (operation === "getAll") {
149
230
  method = "GET";
@@ -202,16 +283,9 @@ class IDB2B {
202
283
  endpoint = `${constants_1.ENDPOINTS.COMPANIES}/${(0, common_1.sanitizeId)(companyId)}`;
203
284
  }
204
285
  }
205
- const response = await httpClient.makeRequest({
206
- method,
207
- url: `${credentials.baseUrl}${endpoint}`,
208
- headers: {
286
+ const response = await httpClient.makeRequest(Object.assign(Object.assign({ method, url: `${credentials.baseUrl}${endpoint}`, headers: {
209
287
  Authorization: `Bearer ${accessToken}`,
210
- },
211
- body,
212
- qs,
213
- json: true,
214
- });
288
+ } }, (useFormData ? { formData: body } : { body })), { qs, json: true }));
215
289
  let processedResponse = (0, common_1.processResponse)(response, operation, initialBody);
216
290
  // Apply field filtering for getAll operations
217
291
  if (operation === "getAll") {
@@ -48,6 +48,7 @@ export declare const ENDPOINTS: {
48
48
  LOGIN: string;
49
49
  CONTACTS: string;
50
50
  COMPANIES: string;
51
+ ACTIVITIES: string;
51
52
  };
52
53
  export declare const RESPONSE: {
53
54
  SUCCESS_MESSAGE: string;
@@ -65,6 +65,7 @@ exports.ENDPOINTS = {
65
65
  LOGIN: "/login",
66
66
  CONTACTS: "/contacts",
67
67
  COMPANIES: "/companies",
68
+ ACTIVITIES: "/activities",
68
69
  };
69
70
  // Response Processing
70
71
  exports.RESPONSE = {
@@ -0,0 +1,3 @@
1
+ import { INodeProperties } from 'n8n-workflow';
2
+ export declare const activityOperations: INodeProperties;
3
+ export declare const activityFields: INodeProperties[];
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.activityFields = exports.activityOperations = void 0;
4
+ exports.activityOperations = {
5
+ displayName: 'Operation',
6
+ name: 'operation',
7
+ type: 'options',
8
+ noDataExpression: true,
9
+ displayOptions: {
10
+ show: {
11
+ resource: ['activity'],
12
+ },
13
+ },
14
+ options: [
15
+ {
16
+ name: 'Get All',
17
+ value: 'getAll',
18
+ action: 'Get all activities for a company',
19
+ description: 'Retrieve all activities for a specific company/lead',
20
+ },
21
+ {
22
+ name: 'Get',
23
+ value: 'get',
24
+ action: 'Get an activity',
25
+ description: 'Get a single activity by ID',
26
+ },
27
+ {
28
+ name: 'Create',
29
+ value: 'create',
30
+ action: 'Create an activity',
31
+ description: 'Create a new activity for a company or contact',
32
+ },
33
+ {
34
+ name: 'Update',
35
+ value: 'update',
36
+ action: 'Update an activity',
37
+ description: 'Update an existing activity',
38
+ },
39
+ {
40
+ name: 'Delete',
41
+ value: 'delete',
42
+ action: 'Delete an activity',
43
+ description: 'Delete an activity by ID',
44
+ },
45
+ ],
46
+ default: 'getAll',
47
+ };
48
+ exports.activityFields = [
49
+ // Activity ID — required for get, update, delete
50
+ {
51
+ displayName: 'Activity ID',
52
+ name: 'activityId',
53
+ type: 'string',
54
+ default: '',
55
+ required: true,
56
+ displayOptions: {
57
+ show: {
58
+ resource: ['activity'],
59
+ operation: ['get', 'update', 'delete'],
60
+ },
61
+ },
62
+ description: 'ID of the activity',
63
+ },
64
+ // Scope selector for getAll
65
+ {
66
+ displayName: 'Scope',
67
+ name: 'getAllScope',
68
+ type: 'options',
69
+ default: 'company',
70
+ required: true,
71
+ displayOptions: {
72
+ show: {
73
+ resource: ['activity'],
74
+ operation: ['getAll'],
75
+ },
76
+ },
77
+ options: [
78
+ { name: 'Company', value: 'company' },
79
+ { name: 'Contact', value: 'contact' },
80
+ ],
81
+ description: 'Whether to list activities for a company or a contact',
82
+ },
83
+ {
84
+ displayName: 'Company ID',
85
+ name: 'companyId',
86
+ type: 'string',
87
+ default: '',
88
+ required: true,
89
+ displayOptions: {
90
+ show: {
91
+ resource: ['activity'],
92
+ operation: ['getAll'],
93
+ getAllScope: ['company'],
94
+ },
95
+ },
96
+ description: 'ID of the company/lead to list activities for',
97
+ },
98
+ {
99
+ displayName: 'Contact ID',
100
+ name: 'getAllContactId',
101
+ type: 'string',
102
+ default: '',
103
+ required: true,
104
+ displayOptions: {
105
+ show: {
106
+ resource: ['activity'],
107
+ operation: ['getAll'],
108
+ getAllScope: ['contact'],
109
+ },
110
+ },
111
+ description: 'ID of the contact to list activities for',
112
+ },
113
+ // Subject — required for create
114
+ {
115
+ displayName: 'Subject',
116
+ name: 'subject',
117
+ type: 'string',
118
+ default: '',
119
+ required: true,
120
+ displayOptions: {
121
+ show: {
122
+ resource: ['activity'],
123
+ operation: ['create'],
124
+ },
125
+ },
126
+ description: 'Subject of the activity',
127
+ },
128
+ // Association — required for create (company OR contact)
129
+ {
130
+ displayName: 'Associate With',
131
+ name: 'associateWith',
132
+ type: 'options',
133
+ default: 'company',
134
+ required: true,
135
+ displayOptions: {
136
+ show: {
137
+ resource: ['activity'],
138
+ operation: ['create'],
139
+ },
140
+ },
141
+ options: [
142
+ { name: 'Company', value: 'company' },
143
+ { name: 'Contact', value: 'contact' },
144
+ ],
145
+ description: 'Whether to link this activity to a company or a contact',
146
+ },
147
+ {
148
+ displayName: 'Company ID',
149
+ name: 'activityCompanyId',
150
+ type: 'string',
151
+ default: '',
152
+ required: true,
153
+ displayOptions: {
154
+ show: {
155
+ resource: ['activity'],
156
+ operation: ['create'],
157
+ associateWith: ['company'],
158
+ },
159
+ },
160
+ description: 'ID of the company/lead to associate this activity with',
161
+ },
162
+ {
163
+ displayName: 'Contact ID',
164
+ name: 'activityContactId',
165
+ type: 'string',
166
+ default: '',
167
+ required: true,
168
+ displayOptions: {
169
+ show: {
170
+ resource: ['activity'],
171
+ operation: ['create'],
172
+ associateWith: ['contact'],
173
+ },
174
+ },
175
+ description: 'ID of the contact to associate this activity with',
176
+ },
177
+ // Additional fields for create/update
178
+ {
179
+ displayName: 'Additional Fields',
180
+ name: 'additionalFields',
181
+ type: 'collection',
182
+ placeholder: 'Add Field',
183
+ default: {},
184
+ displayOptions: {
185
+ show: {
186
+ resource: ['activity'],
187
+ operation: ['create', 'update'],
188
+ },
189
+ },
190
+ options: [
191
+ {
192
+ displayName: 'Subject',
193
+ name: 'subject',
194
+ type: 'string',
195
+ default: '',
196
+ description: 'Subject of the activity (for update)',
197
+ },
198
+ {
199
+ displayName: 'Description',
200
+ name: 'description',
201
+ type: 'string',
202
+ typeOptions: {
203
+ rows: 4,
204
+ },
205
+ default: '',
206
+ description: 'Description or notes for the activity',
207
+ },
208
+ {
209
+ displayName: 'Date & Time',
210
+ name: 'datetime',
211
+ type: 'dateTime',
212
+ default: '',
213
+ description: 'Date and time of the activity (ISO 8601)',
214
+ },
215
+ {
216
+ displayName: 'Icon',
217
+ name: 'icon',
218
+ type: 'string',
219
+ default: '',
220
+ description: 'Icon identifier for the activity',
221
+ },
222
+ {
223
+ displayName: 'User ID',
224
+ name: 'user_id',
225
+ type: 'string',
226
+ default: '',
227
+ description: 'UUID of the user to associate with the activity',
228
+ },
229
+ ],
230
+ },
231
+ // Pagination for getAll
232
+ {
233
+ displayName: 'Limit',
234
+ name: 'limit',
235
+ type: 'number',
236
+ default: 50,
237
+ description: 'Maximum number of activities to return',
238
+ displayOptions: {
239
+ show: {
240
+ resource: ['activity'],
241
+ operation: ['getAll'],
242
+ },
243
+ },
244
+ },
245
+ {
246
+ displayName: 'Page',
247
+ name: 'page',
248
+ type: 'number',
249
+ default: 1,
250
+ description: 'Page number to retrieve',
251
+ displayOptions: {
252
+ show: {
253
+ resource: ['activity'],
254
+ operation: ['getAll'],
255
+ },
256
+ },
257
+ },
258
+ ];
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Activity resource handler
3
+ * Implements CRUD operations for lead/contact activities
4
+ */
5
+ import { HttpClient } from "../utils/httpClient";
6
+ import { DataValidator } from "../utils/validators";
7
+ import { ErrorHandler } from "../utils/errorHandler";
8
+ import { IResourceHandler, GetAllParams, GetParams, CreateParams, UpdateParams, DeleteParams } from "./handlerFactory";
9
+ export interface ActivityGetAllParams extends GetAllParams {
10
+ companyId: string;
11
+ }
12
+ export interface ActivityCreateParams extends CreateParams {
13
+ }
14
+ export declare class ActivityHandler implements IResourceHandler {
15
+ private httpClient;
16
+ private validator;
17
+ private errorHandler;
18
+ constructor(httpClient: HttpClient, validator: DataValidator, errorHandler: ErrorHandler);
19
+ /**
20
+ * Get all activities for a specific company/lead
21
+ */
22
+ getAll(params: ActivityGetAllParams): Promise<any>;
23
+ /**
24
+ * Get single activity by ID
25
+ */
26
+ get(params: GetParams): Promise<any>;
27
+ /**
28
+ * Create a new activity (linked to a company or contact)
29
+ */
30
+ create(params: CreateParams): Promise<any>;
31
+ /**
32
+ * Update an existing activity
33
+ */
34
+ update(params: UpdateParams): Promise<any>;
35
+ /**
36
+ * Delete an activity
37
+ */
38
+ delete(params: DeleteParams): Promise<any>;
39
+ }
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ /**
3
+ * Activity resource handler
4
+ * Implements CRUD operations for lead/contact activities
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ActivityHandler = void 0;
8
+ const constants_1 = require("../config/constants");
9
+ const common_1 = require("../utils/common");
10
+ class ActivityHandler {
11
+ constructor(httpClient, validator, errorHandler) {
12
+ this.httpClient = httpClient;
13
+ this.validator = validator;
14
+ this.errorHandler = errorHandler;
15
+ }
16
+ /**
17
+ * Get all activities for a specific company/lead
18
+ */
19
+ async getAll(params) {
20
+ var _a, _b;
21
+ const limit = (_a = params.limit) !== null && _a !== void 0 ? _a : constants_1.PAGINATION.DEFAULT_LIMIT;
22
+ const page = (_b = params.page) !== null && _b !== void 0 ? _b : constants_1.PAGINATION.DEFAULT_PAGE;
23
+ const qs = { limit, page };
24
+ if (params.queryParameters) {
25
+ Object.assign(qs, params.queryParameters);
26
+ }
27
+ const options = {
28
+ method: "GET",
29
+ url: `${params.baseUrl}/leads/${(0, common_1.sanitizeId)(params.companyId)}/activities`,
30
+ headers: {
31
+ Authorization: `Bearer ${params.accessToken}`,
32
+ },
33
+ qs,
34
+ json: true,
35
+ };
36
+ return this.httpClient.makeRequest(options);
37
+ }
38
+ /**
39
+ * Get single activity by ID
40
+ */
41
+ async get(params) {
42
+ const options = {
43
+ method: "GET",
44
+ url: `${params.baseUrl}${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(params.id)}`,
45
+ headers: {
46
+ Authorization: `Bearer ${params.accessToken}`,
47
+ },
48
+ json: true,
49
+ };
50
+ return this.httpClient.makeRequest(options);
51
+ }
52
+ /**
53
+ * Create a new activity (linked to a company or contact)
54
+ */
55
+ async create(params) {
56
+ const { company_id, contact_id, subject, description, datetime, icon, user_id } = params.data;
57
+ if (!subject || !subject.trim()) {
58
+ throw new Error("Subject is required to create an activity");
59
+ }
60
+ if (!company_id && !contact_id) {
61
+ throw new Error("Either company_id or contact_id is required");
62
+ }
63
+ const body = {
64
+ subject: subject.trim(),
65
+ };
66
+ if (company_id)
67
+ body.company_id = company_id;
68
+ if (contact_id)
69
+ body.contact_id = contact_id;
70
+ if (description)
71
+ body.description = description;
72
+ if (datetime)
73
+ body.datetime = datetime;
74
+ if (icon)
75
+ body.icon = icon;
76
+ if (user_id)
77
+ body.user_id = user_id;
78
+ const options = {
79
+ method: "POST",
80
+ url: `${params.baseUrl}${constants_1.ENDPOINTS.ACTIVITIES}`,
81
+ headers: {
82
+ Authorization: `Bearer ${params.accessToken}`,
83
+ },
84
+ body,
85
+ json: true,
86
+ };
87
+ return this.httpClient.makeRequest(options);
88
+ }
89
+ /**
90
+ * Update an existing activity
91
+ */
92
+ async update(params) {
93
+ const { subject, description, datetime, icon, user_id } = params.data;
94
+ const body = {};
95
+ if (subject)
96
+ body.subject = subject.trim();
97
+ if (description !== undefined && description !== "")
98
+ body.description = description;
99
+ if (datetime)
100
+ body.datetime = datetime;
101
+ if (icon)
102
+ body.icon = icon;
103
+ if (user_id)
104
+ body.user_id = user_id;
105
+ const options = {
106
+ method: "PATCH",
107
+ url: `${params.baseUrl}${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(params.id)}`,
108
+ headers: {
109
+ Authorization: `Bearer ${params.accessToken}`,
110
+ },
111
+ body,
112
+ json: true,
113
+ };
114
+ return this.httpClient.makeRequest(options);
115
+ }
116
+ /**
117
+ * Delete an activity
118
+ */
119
+ async delete(params) {
120
+ const options = {
121
+ method: "DELETE",
122
+ url: `${params.baseUrl}${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(params.id)}`,
123
+ headers: {
124
+ Authorization: `Bearer ${params.accessToken}`,
125
+ },
126
+ json: true,
127
+ };
128
+ return this.httpClient.makeRequest(options);
129
+ }
130
+ }
131
+ exports.ActivityHandler = ActivityHandler;
@@ -69,6 +69,7 @@ export declare class HandlerFactory {
69
69
  private errorHandler;
70
70
  private contactHandler;
71
71
  private companyHandler;
72
+ private activityHandler;
72
73
  constructor(executeFunctions: IExecuteFunctions, httpClient: HttpClient, validator: DataValidator, errorHandler: ErrorHandler);
73
74
  /**
74
75
  * Get handler for specified resource
@@ -8,6 +8,7 @@ exports.HandlerFactory = void 0;
8
8
  exports.createHandlerFactory = createHandlerFactory;
9
9
  const ContactHandler_1 = require("./ContactHandler");
10
10
  const CompanyHandler_1 = require("./CompanyHandler");
11
+ const ActivityHandler_1 = require("./ActivityHandler");
11
12
  /**
12
13
  * Handler factory class for creating handlers
13
14
  */
@@ -19,6 +20,7 @@ class HandlerFactory {
19
20
  this.errorHandler = errorHandler;
20
21
  this.contactHandler = new ContactHandler_1.ContactHandler(httpClient, validator, errorHandler);
21
22
  this.companyHandler = new CompanyHandler_1.CompanyHandler(httpClient, validator, errorHandler);
23
+ this.activityHandler = new ActivityHandler_1.ActivityHandler(httpClient, validator, errorHandler);
22
24
  }
23
25
  /**
24
26
  * Get handler for specified resource
@@ -29,6 +31,8 @@ class HandlerFactory {
29
31
  return this.contactHandler;
30
32
  case "company":
31
33
  return this.companyHandler;
34
+ case "activity":
35
+ return this.activityHandler;
32
36
  default:
33
37
  throw new Error(`Unknown resource: ${resource}`);
34
38
  }
@@ -37,13 +41,13 @@ class HandlerFactory {
37
41
  * Check if resource is supported
38
42
  */
39
43
  isResourceSupported(resource) {
40
- return ["contact", "company"].includes(resource);
44
+ return ["contact", "company", "activity"].includes(resource);
41
45
  }
42
46
  /**
43
47
  * Get list of supported resources
44
48
  */
45
49
  getSupportedResources() {
46
- return ["contact", "company"];
50
+ return ["contact", "company", "activity"];
47
51
  }
48
52
  }
49
53
  exports.HandlerFactory = HandlerFactory;
@@ -0,0 +1,14 @@
1
+ export interface IDB2BActivity {
2
+ id: string;
3
+ company_id: string | null;
4
+ contact_id: string | null;
5
+ user_id: string | null;
6
+ organization_id: string;
7
+ icon: string | null;
8
+ subject: string;
9
+ description: string | null;
10
+ datetime: string | null;
11
+ type: string | null;
12
+ created_at: string | null;
13
+ updated_at: string | null;
14
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -8,6 +8,7 @@ export interface RequestOptions {
8
8
  url: string;
9
9
  headers?: any;
10
10
  body?: any;
11
+ formData?: any;
11
12
  qs?: any;
12
13
  json?: boolean;
13
14
  }
@@ -0,0 +1,5 @@
1
+ import { INodeType, INodeTypeDescription, INodeExecutionData, IPollFunctions } from "n8n-workflow";
2
+ export declare class IDB2BTrigger implements INodeType {
3
+ description: INodeTypeDescription;
4
+ poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null>;
5
+ }
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IDB2BTrigger = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const httpClient_1 = require("../IDB2B/utils/httpClient");
6
+ const tokenCache_1 = require("../IDB2B/utils/tokenCache");
7
+ const common_1 = require("../IDB2B/utils/common");
8
+ const constants_1 = require("../IDB2B/config/constants");
9
+ class IDB2BTrigger {
10
+ constructor() {
11
+ this.description = {
12
+ displayName: "IDB2B Trigger",
13
+ name: "idb2bTrigger",
14
+ icon: "file:../IDB2B/Icon.svg",
15
+ group: ["trigger"],
16
+ version: 1,
17
+ subtitle: '={{$parameter["event"]}}',
18
+ description: "Polls IDB2B for new or updated contacts, companies, and activities",
19
+ defaults: {
20
+ name: "IDB2B Trigger",
21
+ },
22
+ polling: true,
23
+ inputs: [],
24
+ outputs: ["main"],
25
+ credentials: [
26
+ {
27
+ name: "idb2bApi",
28
+ required: true,
29
+ },
30
+ ],
31
+ properties: [
32
+ {
33
+ displayName: "Event",
34
+ name: "event",
35
+ type: "options",
36
+ noDataExpression: true,
37
+ options: [
38
+ {
39
+ name: "Contact Created",
40
+ value: "contactCreated",
41
+ description: "Triggers when a new contact is created",
42
+ },
43
+ {
44
+ name: "Contact Updated",
45
+ value: "contactUpdated",
46
+ description: "Triggers when a contact is updated",
47
+ },
48
+ {
49
+ name: "Company Created",
50
+ value: "companyCreated",
51
+ description: "Triggers when a new company is created",
52
+ },
53
+ {
54
+ name: "Company Updated",
55
+ value: "companyUpdated",
56
+ description: "Triggers when a company is updated",
57
+ },
58
+ {
59
+ name: "Activity Created (Under Company)",
60
+ value: "activityCreated",
61
+ description: "Triggers when a new activity is created for a specific company",
62
+ },
63
+ ],
64
+ default: "contactCreated",
65
+ },
66
+ // Company ID required for activity trigger
67
+ {
68
+ displayName: "Company ID",
69
+ name: "companyId",
70
+ type: "string",
71
+ default: "",
72
+ required: true,
73
+ displayOptions: {
74
+ show: {
75
+ event: ["activityCreated"],
76
+ },
77
+ },
78
+ description: "ID of the company/lead to watch for new activities",
79
+ },
80
+ {
81
+ displayName: "Limit",
82
+ name: "limit",
83
+ type: "number",
84
+ default: constants_1.PAGINATION.DEFAULT_LIMIT,
85
+ description: "Maximum number of records to fetch per poll cycle",
86
+ },
87
+ ],
88
+ };
89
+ }
90
+ async poll() {
91
+ const webhookData = this.getWorkflowStaticData("node");
92
+ const event = this.getNodeParameter("event");
93
+ const limit = this.getNodeParameter("limit", constants_1.PAGINATION.DEFAULT_LIMIT);
94
+ const credentials = await this.getCredentials("idb2bApi");
95
+ const baseUrl = credentials.baseUrl;
96
+ tokenCache_1.secureTokenCache.cleanupIfNeeded();
97
+ const accessToken = await (0, httpClient_1.getAccessToken)(this, credentials);
98
+ const httpClient = new httpClient_1.HttpClient(this);
99
+ // Determine the timestamp field and endpoint for this event
100
+ const isUpdate = event === "contactUpdated" || event === "companyUpdated";
101
+ const timestampField = isUpdate ? "updated_at" : "created_at";
102
+ // On first run, use now minus 1 minute as the baseline
103
+ const now = new Date();
104
+ const lastCheck = webhookData.lastCheck;
105
+ const since = lastCheck !== null && lastCheck !== void 0 ? lastCheck : new Date(now.getTime() - 60000).toISOString();
106
+ let endpoint;
107
+ if (event === "contactCreated" || event === "contactUpdated") {
108
+ endpoint = constants_1.ENDPOINTS.CONTACTS;
109
+ }
110
+ else if (event === "companyCreated" || event === "companyUpdated") {
111
+ endpoint = constants_1.ENDPOINTS.COMPANIES;
112
+ }
113
+ else {
114
+ // activityCreated
115
+ const companyId = this.getNodeParameter("companyId");
116
+ if (!companyId) {
117
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), "Company ID is required for the Activity Created trigger");
118
+ }
119
+ endpoint = `/leads/${(0, common_1.sanitizeId)(companyId)}/activities`;
120
+ }
121
+ let response;
122
+ try {
123
+ response = await httpClient.makeRequest({
124
+ method: "GET",
125
+ url: `${baseUrl}${endpoint}`,
126
+ headers: {
127
+ Authorization: `Bearer ${accessToken}`,
128
+ },
129
+ qs: {
130
+ limit,
131
+ page: 1,
132
+ sort_by: timestampField,
133
+ sort_order: "desc",
134
+ },
135
+ json: true,
136
+ });
137
+ }
138
+ catch (error) {
139
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error);
140
+ }
141
+ // Update the last check timestamp
142
+ webhookData.lastCheck = now.toISOString();
143
+ const records = Array.isArray(response === null || response === void 0 ? void 0 : response.data)
144
+ ? response.data
145
+ : Array.isArray(response)
146
+ ? response
147
+ : [];
148
+ // Filter to only records newer than our last check
149
+ const newRecords = records.filter((record) => {
150
+ const ts = record[timestampField];
151
+ if (!ts)
152
+ return false;
153
+ return new Date(ts) > new Date(since);
154
+ });
155
+ if (newRecords.length === 0) {
156
+ return null;
157
+ }
158
+ return [
159
+ newRecords.map((record) => ({
160
+ json: record,
161
+ })),
162
+ ];
163
+ }
164
+ }
165
+ exports.IDB2BTrigger = IDB2BTrigger;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-idb2b",
3
- "version": "3.2.4",
3
+ "version": "3.2.6",
4
4
  "description": "n8n community node for IDB2B - WhatsApp AI Agents",
5
5
  "main": "index.js",
6
6
  "scripts": {