n8n-nodes-idb2b 3.2.5 → 3.2.7

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
@@ -89,6 +89,7 @@ class IDB2B {
89
89
  let body = undefined;
90
90
  let qs = {};
91
91
  let initialBody = undefined;
92
+ let useFormData = false;
92
93
  if (resource === "contact") {
93
94
  if (operation === "getAll") {
94
95
  method = "GET";
@@ -154,10 +155,17 @@ class IDB2B {
154
155
  else if (resource === "activity") {
155
156
  if (operation === "getAll") {
156
157
  method = "GET";
157
- const companyId = this.getNodeParameter("companyId", i);
158
+ const getAllScope = this.getNodeParameter("getAllScope", i);
158
159
  const limit = this.getNodeParameter("limit", i, constants_1.PAGINATION.DEFAULT_LIMIT);
159
160
  const page = this.getNodeParameter("page", i, constants_1.PAGINATION.DEFAULT_PAGE);
160
- endpoint = `/leads/${(0, common_1.sanitizeId)(companyId)}/activities`;
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
+ }
161
169
  qs.limit = limit;
162
170
  qs.page = page;
163
171
  }
@@ -175,29 +183,41 @@ class IDB2B {
175
183
  if (!subject || !subject.trim()) {
176
184
  throw new Error("Subject is required to create an activity");
177
185
  }
178
- body = Object.assign({ subject: subject.trim() }, additionalFields);
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() };
179
189
  if (associateWith === "company") {
180
190
  const companyId = this.getNodeParameter("activityCompanyId", i);
181
- body.company_id = companyId;
191
+ formPayload.company_id = companyId;
182
192
  }
183
193
  else {
184
194
  const contactId = this.getNodeParameter("activityContactId", i);
185
- body.contact_id = contactId;
195
+ formPayload.contact_id = contactId;
186
196
  }
187
- initialBody = body;
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;
188
206
  }
189
207
  else if (operation === "update") {
190
208
  method = "PATCH";
191
209
  const activityId = this.getNodeParameter("activityId", i);
192
210
  endpoint = `${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(activityId)}`;
193
211
  const additionalFields = this.getNodeParameter("additionalFields", i, {});
194
- body = {};
212
+ const updatePayload = {};
195
213
  Object.entries(additionalFields).forEach(([key, value]) => {
196
214
  if (value !== undefined && value !== "") {
197
- body[key] = value;
215
+ updatePayload[key] = value;
198
216
  }
199
217
  });
200
- initialBody = body;
218
+ body = updatePayload;
219
+ initialBody = updatePayload;
220
+ useFormData = true;
201
221
  }
202
222
  else if (operation === "delete") {
203
223
  method = "DELETE";
@@ -263,16 +283,9 @@ class IDB2B {
263
283
  endpoint = `${constants_1.ENDPOINTS.COMPANIES}/${(0, common_1.sanitizeId)(companyId)}`;
264
284
  }
265
285
  }
266
- const response = await httpClient.makeRequest({
267
- method,
268
- url: `${credentials.baseUrl}${endpoint}`,
269
- headers: {
286
+ const response = await httpClient.makeRequest(Object.assign(Object.assign({ method, url: `${credentials.baseUrl}${endpoint}`, headers: {
270
287
  Authorization: `Bearer ${accessToken}`,
271
- },
272
- body,
273
- qs,
274
- json: true,
275
- });
288
+ } }, (useFormData ? { formData: body } : { body })), { qs, json: true }));
276
289
  let processedResponse = (0, common_1.processResponse)(response, operation, initialBody);
277
290
  // Apply field filtering for getAll operations
278
291
  if (operation === "getAll") {
@@ -61,7 +61,25 @@ exports.activityFields = [
61
61
  },
62
62
  description: 'ID of the activity',
63
63
  },
64
- // Company ID — required for getAll (scoped to company), optional for create
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
+ },
65
83
  {
66
84
  displayName: 'Company ID',
67
85
  name: 'companyId',
@@ -72,10 +90,26 @@ exports.activityFields = [
72
90
  show: {
73
91
  resource: ['activity'],
74
92
  operation: ['getAll'],
93
+ getAllScope: ['company'],
75
94
  },
76
95
  },
77
96
  description: 'ID of the company/lead to list activities for',
78
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
+ },
79
113
  // Subject — required for create
80
114
  {
81
115
  displayName: 'Subject',
@@ -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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-idb2b",
3
- "version": "3.2.5",
3
+ "version": "3.2.7",
4
4
  "description": "n8n community node for IDB2B - WhatsApp AI Agents",
5
5
  "main": "index.js",
6
6
  "scripts": {