@belmontdigitalmarketing/n8n-nodes-flowlu 0.2.0 → 0.2.2

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.
@@ -9,9 +9,7 @@ async function getFlowluCredentials(context) {
9
9
  const credentials = await context.getCredentials('flowluApi');
10
10
  const subdomain = credentials.subdomain;
11
11
  const apiKey = credentials.apiKey;
12
- const baseUrl = subdomain.endsWith('.flowlu.com')
13
- ? `https://${subdomain}`
14
- : `https://${subdomain}.flowlu.com`;
12
+ const baseUrl = subdomain.endsWith('.flowlu.com') ? `https://${subdomain}` : `https://${subdomain}.flowlu.com`;
15
13
  return { baseUrl, apiKey };
16
14
  }
17
15
  async function flowluApiRequest(method, baseUrl, endpoint, apiKey, body, queryParams) {
@@ -31,6 +29,55 @@ async function flowluApiRequest(method, baseUrl, endpoint, apiKey, body, queryPa
31
29
  const response = await this.helpers.request(requestOptions);
32
30
  return typeof response === 'string' ? JSON.parse(response) : response;
33
31
  }
32
+ // Detect Flowlu's rate-limit signal in either a thrown error or a returned body.
33
+ function isFlowluRateLimit(value) {
34
+ const text = value instanceof Error ? value.message : typeof value === 'string' ? value : JSON.stringify(value ?? '');
35
+ return /limit exceeded|too many requests|\b429\b/i.test(text);
36
+ }
37
+ // GET wrapper that retries through Flowlu's rate limit with linear backoff. If it
38
+ // never succeeds it throws, so callers surface the real cause instead of silently
39
+ // returning no data (which previously masqueraded as "No fields found").
40
+ async function flowluApiGetWithRetry(baseUrl, endpoint, apiKey, queryParams, maxRetries = 3) {
41
+ let lastError = new Error('Flowlu request failed');
42
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
43
+ try {
44
+ const response = await flowluApiRequest.call(this, 'GET', baseUrl, endpoint, apiKey, undefined, queryParams);
45
+ if (!isFlowluRateLimit(response?.error ?? response)) {
46
+ return response;
47
+ }
48
+ lastError = new Error(String(response?.error ?? 'Flowlu request limit exceeded'));
49
+ }
50
+ catch (error) {
51
+ if (!isFlowluRateLimit(error))
52
+ throw error;
53
+ lastError = error;
54
+ }
55
+ // linear backoff before the next attempt (no wait after the final one)
56
+ if (attempt < maxRetries) {
57
+ await new Promise((resolve) => {
58
+ setTimeout(resolve, 1500 * (attempt + 1));
59
+ });
60
+ }
61
+ }
62
+ throw lastError instanceof Error ? lastError : new Error('Flowlu request failed');
63
+ }
64
+ // Fetch every page of a Flowlu list endpoint. The API caps responses at 50 items
65
+ // per page, so a single call silently truncates larger result sets.
66
+ async function flowluListAll(baseUrl, endpoint, apiKey) {
67
+ const items = [];
68
+ const maxPages = 100; // safety bound
69
+ for (let page = 1; page <= maxPages; page++) {
70
+ const response = await flowluApiGetWithRetry.call(this, baseUrl, endpoint, apiKey, { page: String(page) });
71
+ const pageItems = response?.response?.items;
72
+ if (!Array.isArray(pageItems) || pageItems.length === 0)
73
+ break;
74
+ items.push(...pageItems);
75
+ const total = Number(response.response.total ?? items.length);
76
+ if (items.length >= total)
77
+ break;
78
+ }
79
+ return items;
80
+ }
34
81
  function appendWorkflowFooter(target, contentKey = 'description') {
35
82
  const { id } = this.getWorkflow();
36
83
  const footer = `<hr><em>Generated via n8n:</em> <a href="${this.getInstanceBaseUrl()}workflow/${id}">View Workflow</a>`;
@@ -52,49 +99,27 @@ function applyResourceMapperFields(context, body, paramName, itemIndex) {
52
99
  // No custom fields set - ignore
53
100
  }
54
101
  }
55
- // Helper to load custom fields by module/model, with fallback approach
102
+ // Helper to load an entity's custom fields as name/value options.
56
103
  async function loadCustomFieldsForEntity(context, moduleFilter, modelFilter) {
104
+ const { baseUrl, apiKey } = await getFlowluCredentials(context);
105
+ let fieldsets;
106
+ let allFields;
57
107
  try {
58
- const { baseUrl, apiKey } = await getFlowluCredentials(context);
59
- // Strategy 1: Get all fieldsets and match by module/model
60
- const fieldsetsResponse = await flowluApiRequest.call(context, 'GET', baseUrl, '/api/v1/module/customfields/fieldsets/list', apiKey);
61
- const fieldsetIds = [];
62
- if (fieldsetsResponse?.response?.items) {
63
- for (const fs of fieldsetsResponse.response.items) {
64
- if (fs.module === moduleFilter && fs.model === modelFilter) {
65
- fieldsetIds.push(fs.id.toString());
66
- }
67
- }
68
- }
69
- // Get all custom fields
70
- const fieldsResponse = await flowluApiRequest.call(context, 'GET', baseUrl, '/api/v1/module/customfields/fields/list', apiKey);
71
- if (fieldsResponse?.response?.items) {
72
- let fields = fieldsResponse.response.items;
73
- // Filter by fieldset if we found matching fieldsets
74
- if (fieldsetIds.length > 0) {
75
- fields = fields.filter((f) => fieldsetIds.includes(f.fieldset_id?.toString()));
76
- }
77
- else {
78
- // Fallback: filter by module/model directly on the field
79
- fields = fields.filter((f) => f.module === moduleFilter && f.model === modelFilter);
80
- }
81
- return fields
82
- .filter((f) => f.active !== 0)
83
- .map((f) => {
84
- const cfKey = (f.api_use_alias && f.alias)
85
- ? `cf_${f.alias}`
86
- : `cf_${f.id}`;
87
- return {
88
- name: f.name || cfKey,
89
- value: cfKey,
90
- };
91
- });
92
- }
93
- return [];
108
+ fieldsets = await flowluListAll.call(context, baseUrl, '/api/v1/module/customfields/fieldsets/list', apiKey);
109
+ allFields = await flowluListAll.call(context, baseUrl, '/api/v1/module/customfields/fields/list', apiKey);
94
110
  }
95
111
  catch (error) {
96
- return [];
112
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Could not load Flowlu custom fields: ${error.message}. If this is a rate limit, wait a moment and try again.`);
97
113
  }
114
+ // Fields don't reliably carry module/model, so match via their fieldset.
115
+ const matchingFieldsetIds = new Set(fieldsets.filter((fs) => fs.module === moduleFilter && fs.model === modelFilter).map((fs) => String(fs.id)));
116
+ return allFields
117
+ .filter((f) => f.active !== 0)
118
+ .filter((f) => matchingFieldsetIds.has(String(f.fieldset_id)) || (f.module === moduleFilter && f.model === modelFilter))
119
+ .map((f) => {
120
+ const cfKey = f.api_use_alias && f.alias ? `cf_${f.alias}` : `cf_${f.id}`;
121
+ return { name: f.name || cfKey, value: cfKey };
122
+ });
98
123
  }
99
124
  // ============================================================
100
125
  // Reusable custom field option definitions
@@ -103,27 +128,16 @@ async function loadCustomFieldsForEntity(context, moduleFilter, modelFilter) {
103
128
  async function loadEntityCustomFieldColumns(moduleFilter, modelFilter) {
104
129
  try {
105
130
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
106
- const fieldsetsResponse = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customfields/fieldsets/list', apiKey);
107
- const fieldsetIds = [];
108
- if (fieldsetsResponse?.response?.items) {
109
- for (const fs of fieldsetsResponse.response.items) {
110
- if (fs.module === moduleFilter && fs.model === modelFilter) {
111
- fieldsetIds.push(fs.id.toString());
112
- }
113
- }
114
- }
115
- const fieldsResponse = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customfields/fields/list', apiKey);
116
- if (!fieldsResponse?.response?.items)
117
- return { fields: [] };
118
- let filtered = fieldsResponse.response.items.filter((f) => f.active !== 0);
119
- if (fieldsetIds.length > 0) {
120
- filtered = filtered.filter((f) => fieldsetIds.includes(f.fieldset_id?.toString()));
121
- }
122
- else {
123
- filtered = filtered.filter((f) => f.module === moduleFilter && f.model === modelFilter);
124
- }
131
+ const fieldsets = await flowluListAll.call(this, baseUrl, '/api/v1/module/customfields/fieldsets/list', apiKey);
132
+ const allFields = await flowluListAll.call(this, baseUrl, '/api/v1/module/customfields/fields/list', apiKey);
133
+ // Fields don't reliably carry module/model, so match via their fieldset.
134
+ const matchingFieldsetIds = new Set(fieldsets
135
+ .filter((fs) => fs.module === moduleFilter && fs.model === modelFilter)
136
+ .map((fs) => String(fs.id)));
137
+ const filtered = allFields.filter((f) => f.active !== 0 &&
138
+ (matchingFieldsetIds.has(String(f.fieldset_id)) || (f.module === moduleFilter && f.model === modelFilter)));
125
139
  const fields = filtered.map((f) => {
126
- const cfKey = (f.api_use_alias && f.alias) ? `cf_${f.alias}` : `cf_${f.id}`;
140
+ const cfKey = f.api_use_alias && f.alias ? `cf_${f.alias}` : `cf_${f.id}`;
127
141
  let fieldType = 'string';
128
142
  if (f.type === 'int')
129
143
  fieldType = 'number';
@@ -145,7 +159,9 @@ async function loadEntityCustomFieldColumns(moduleFilter, modelFilter) {
145
159
  .filter((o) => o.value)
146
160
  .map((o) => ({ name: o.value, value: o.id?.toString() || o.value }));
147
161
  }
148
- catch { /* ignore */ }
162
+ catch {
163
+ /* ignore */
164
+ }
149
165
  }
150
166
  const field = {
151
167
  id: cfKey,
@@ -163,7 +179,9 @@ async function loadEntityCustomFieldColumns(moduleFilter, modelFilter) {
163
179
  return { fields };
164
180
  }
165
181
  catch (error) {
166
- return { fields: [] };
182
+ if (error instanceof n8n_workflow_1.NodeOperationError)
183
+ throw error;
184
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Could not load Flowlu custom fields: ${error.message}. If this is a rate limit, wait a moment and click Retry.`);
167
185
  }
168
186
  }
169
187
  function makeCustomFieldsOption(loadMethod) {
@@ -251,7 +269,12 @@ class Flowlu {
251
269
  displayOptions: { show: { resource: ['tag'] } },
252
270
  options: [
253
271
  { name: 'Add', value: 'add', description: 'Add a tag to an entity', action: 'Add a tag' },
254
- { name: 'Get Many', value: 'getAll', description: 'Get all tags on an entity', action: 'Get tags on entity' },
272
+ {
273
+ name: 'Get Many',
274
+ value: 'getAll',
275
+ description: 'Get many tags on an entity',
276
+ action: 'Get tags on entity',
277
+ },
255
278
  { name: 'List All', value: 'listAll', description: 'List all tags in your account', action: 'List all tags' },
256
279
  { name: 'Remove', value: 'remove', description: 'Remove a tag from an entity', action: 'Remove a tag' },
257
280
  ],
@@ -268,7 +291,12 @@ class Flowlu {
268
291
  displayOptions: { show: { resource: ['comment'] } },
269
292
  options: [
270
293
  { name: 'Create', value: 'create', description: 'Create a comment on an entity', action: 'Create a comment' },
271
- { name: 'Get Many', value: 'getAll', description: 'Get all comments on an entity', action: 'Get comments on entity' },
294
+ {
295
+ name: 'Get Many',
296
+ value: 'getAll',
297
+ description: 'Get many comments on an entity',
298
+ action: 'Get comments on entity',
299
+ },
272
300
  ],
273
301
  default: 'create',
274
302
  },
@@ -303,7 +331,12 @@ class Flowlu {
303
331
  { name: 'Create', value: 'create', description: 'Create a new opportunity', action: 'Create an opportunity' },
304
332
  { name: 'Delete', value: 'delete', description: 'Delete an opportunity', action: 'Delete an opportunity' },
305
333
  { name: 'Get', value: 'get', description: 'Get an opportunity by ID', action: 'Get an opportunity' },
306
- { name: 'Get Many', value: 'getAll', description: 'Get many opportunities', action: 'Get many opportunities' },
334
+ {
335
+ name: 'Get Many',
336
+ value: 'getAll',
337
+ description: 'Get many opportunities',
338
+ action: 'Get many opportunities',
339
+ },
307
340
  { name: 'Update', value: 'update', description: 'Update an opportunity', action: 'Update an opportunity' },
308
341
  ],
309
342
  default: 'create',
@@ -394,6 +427,7 @@ class Flowlu {
394
427
  displayName: 'Email',
395
428
  name: 'email',
396
429
  type: 'string',
430
+ placeholder: 'name@email.com',
397
431
  default: '',
398
432
  displayOptions: { show: { resource: ['contact'], operation: ['create'] } },
399
433
  },
@@ -412,33 +446,15 @@ class Flowlu {
412
446
  default: {},
413
447
  displayOptions: { show: { resource: ['contact'], operation: ['create'] } },
414
448
  options: [
415
- { displayName: 'Middle Name', name: 'middle_name', type: 'string', default: '' },
416
- { displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
417
- { displayName: 'Owner', name: 'owner_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '', description: 'Assigned user for this contact' },
418
- { displayName: 'Date of Birth', name: 'birth_date', type: 'dateTime', default: '' },
419
- { displayName: 'Website', name: 'web', type: 'string', default: '' },
420
- { displayName: 'Phone 2', name: 'phone2', type: 'string', default: '' },
421
- { displayName: 'Phone 3', name: 'phone3', type: 'string', default: '' },
422
- { displayName: 'Personal Email', name: 'email_personal', type: 'string', default: '' },
423
- { displayName: 'Telegram', name: 'social_network_link_2', type: 'string', default: '' },
424
- { displayName: 'Facebook', name: 'social_network_link_3', type: 'string', default: '' },
425
- { displayName: 'X (Twitter)', name: 'social_network_link_4', type: 'string', default: '' },
426
- { displayName: 'LinkedIn', name: 'social_network_link_5', type: 'string', default: '' },
427
- { displayName: 'Instagram', name: 'social_network_link_6', type: 'string', default: '' },
428
- { displayName: 'Skype', name: 'social_network_link_1', type: 'string', default: '' },
429
449
  { displayName: 'Address', name: 'address', type: 'string', default: '' },
430
- { displayName: 'Shipping Address Line 1', name: 'shipping_address_line_1', type: 'string', default: '' },
431
- { displayName: 'Shipping City', name: 'shipping_city', type: 'string', default: '' },
432
- { displayName: 'Shipping State', name: 'shipping_state', type: 'string', default: '' },
433
- { displayName: 'Shipping Zip', name: 'shipping_zip', type: 'string', default: '' },
434
- { displayName: 'Shipping Country', name: 'shipping_country', type: 'string', default: '' },
435
450
  { displayName: 'Billing Address Line 1', name: 'billing_address_line_1', type: 'string', default: '' },
436
451
  { displayName: 'Billing City', name: 'billing_city', type: 'string', default: '' },
452
+ { displayName: 'Billing Country', name: 'billing_country', type: 'string', default: '' },
437
453
  { displayName: 'Billing State', name: 'billing_state', type: 'string', default: '' },
438
454
  { displayName: 'Billing Zip', name: 'billing_zip', type: 'string', default: '' },
439
- { displayName: 'Billing Country', name: 'billing_country', type: 'string', default: '' },
440
- { displayName: 'VAT / Tax ID', name: 'VAT', type: 'string', default: '' },
441
- { displayName: 'Timezone', name: 'timezone', type: 'string', default: '' },
455
+ { displayName: 'Date of Birth', name: 'birth_date', type: 'dateTime', default: '' },
456
+ { displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
457
+ { displayName: 'Facebook', name: 'social_network_link_3', type: 'string', default: '' },
442
458
  {
443
459
  displayName: 'Include Link to Workflow',
444
460
  name: 'includeLinkToWorkflow',
@@ -446,6 +462,31 @@ class Flowlu {
446
462
  default: false,
447
463
  description: 'Whether to append a "Generated via n8n: View Workflow" footer to the contact description, linking back to this workflow',
448
464
  },
465
+ { displayName: 'Instagram', name: 'social_network_link_6', type: 'string', default: '' },
466
+ { displayName: 'LinkedIn', name: 'social_network_link_5', type: 'string', default: '' },
467
+ { displayName: 'Middle Name', name: 'middle_name', type: 'string', default: '' },
468
+ {
469
+ displayName: 'Owner Name or ID',
470
+ name: 'owner_id',
471
+ type: 'options',
472
+ typeOptions: { loadOptionsMethod: 'getUsers' },
473
+ default: '',
474
+ description: 'Assigned user for this contact. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
475
+ },
476
+ { displayName: 'Personal Email', name: 'email_personal', type: 'string', default: '' },
477
+ { displayName: 'Phone 2', name: 'phone2', type: 'string', default: '' },
478
+ { displayName: 'Phone 3', name: 'phone3', type: 'string', default: '' },
479
+ { displayName: 'Shipping Address Line 1', name: 'shipping_address_line_1', type: 'string', default: '' },
480
+ { displayName: 'Shipping City', name: 'shipping_city', type: 'string', default: '' },
481
+ { displayName: 'Shipping Country', name: 'shipping_country', type: 'string', default: '' },
482
+ { displayName: 'Shipping State', name: 'shipping_state', type: 'string', default: '' },
483
+ { displayName: 'Shipping Zip', name: 'shipping_zip', type: 'string', default: '' },
484
+ { displayName: 'Skype', name: 'social_network_link_1', type: 'string', default: '' },
485
+ { displayName: 'Telegram', name: 'social_network_link_2', type: 'string', default: '' },
486
+ { displayName: 'Timezone', name: 'timezone', type: 'string', default: '' },
487
+ { displayName: 'VAT / Tax ID', name: 'VAT', type: 'string', default: '' },
488
+ { displayName: 'Website', name: 'web', type: 'string', default: '' },
489
+ { displayName: 'X (Twitter)', name: 'social_network_link_4', type: 'string', default: '' },
449
490
  ],
450
491
  },
451
492
  {
@@ -473,26 +514,12 @@ class Flowlu {
473
514
  default: {},
474
515
  displayOptions: { show: { resource: ['contact'], operation: ['update'] } },
475
516
  options: [
476
- { displayName: 'First Name', name: 'first_name', type: 'string', default: '' },
477
- { displayName: 'Last Name', name: 'last_name', type: 'string', default: '' },
478
- { displayName: 'Middle Name', name: 'middle_name', type: 'string', default: '' },
479
- { displayName: 'Email', name: 'email', type: 'string', default: '' },
480
- { displayName: 'Phone', name: 'phone', type: 'string', default: '' },
481
- { displayName: 'Phone 2', name: 'phone2', type: 'string', default: '' },
482
- { displayName: 'Phone 3', name: 'phone3', type: 'string', default: '' },
483
- { displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
484
- { displayName: 'Owner', name: 'owner_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
517
+ { displayName: 'Address', name: 'address', type: 'string', default: '' },
485
518
  { displayName: 'Date of Birth', name: 'birth_date', type: 'dateTime', default: '' },
486
- { displayName: 'Website', name: 'web', type: 'string', default: '' },
487
- { displayName: 'Personal Email', name: 'email_personal', type: 'string', default: '' },
488
- { displayName: 'Telegram', name: 'social_network_link_2', type: 'string', default: '' },
519
+ { displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
520
+ { displayName: 'Email', name: 'email', type: 'string', placeholder: 'name@email.com', default: '' },
489
521
  { displayName: 'Facebook', name: 'social_network_link_3', type: 'string', default: '' },
490
- { displayName: 'X (Twitter)', name: 'social_network_link_4', type: 'string', default: '' },
491
- { displayName: 'LinkedIn', name: 'social_network_link_5', type: 'string', default: '' },
492
- { displayName: 'Instagram', name: 'social_network_link_6', type: 'string', default: '' },
493
- { displayName: 'Skype', name: 'social_network_link_1', type: 'string', default: '' },
494
- { displayName: 'Address', name: 'address', type: 'string', default: '' },
495
- { displayName: 'VAT / Tax ID', name: 'VAT', type: 'string', default: '' },
522
+ { displayName: 'First Name', name: 'first_name', type: 'string', default: '' },
496
523
  {
497
524
  displayName: 'Include Link to Workflow',
498
525
  name: 'includeLinkToWorkflow',
@@ -500,6 +527,27 @@ class Flowlu {
500
527
  default: false,
501
528
  description: 'Whether to append a "Generated via n8n: View Workflow" footer to the contact description, linking back to this workflow. Requires Description to be set.',
502
529
  },
530
+ { displayName: 'Instagram', name: 'social_network_link_6', type: 'string', default: '' },
531
+ { displayName: 'Last Name', name: 'last_name', type: 'string', default: '' },
532
+ { displayName: 'LinkedIn', name: 'social_network_link_5', type: 'string', default: '' },
533
+ { displayName: 'Middle Name', name: 'middle_name', type: 'string', default: '' },
534
+ {
535
+ displayName: 'Owner Name or ID',
536
+ name: 'owner_id',
537
+ type: 'options',
538
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
539
+ typeOptions: { loadOptionsMethod: 'getUsers' },
540
+ default: '',
541
+ },
542
+ { displayName: 'Personal Email', name: 'email_personal', type: 'string', default: '' },
543
+ { displayName: 'Phone', name: 'phone', type: 'string', default: '' },
544
+ { displayName: 'Phone 2', name: 'phone2', type: 'string', default: '' },
545
+ { displayName: 'Phone 3', name: 'phone3', type: 'string', default: '' },
546
+ { displayName: 'Skype', name: 'social_network_link_1', type: 'string', default: '' },
547
+ { displayName: 'Telegram', name: 'social_network_link_2', type: 'string', default: '' },
548
+ { displayName: 'VAT / Tax ID', name: 'VAT', type: 'string', default: '' },
549
+ { displayName: 'Website', name: 'web', type: 'string', default: '' },
550
+ { displayName: 'X (Twitter)', name: 'social_network_link_4', type: 'string', default: '' },
503
551
  ],
504
552
  },
505
553
  {
@@ -524,9 +572,11 @@ class Flowlu {
524
572
  displayName: 'Limit',
525
573
  name: 'limit',
526
574
  type: 'number',
575
+ typeOptions: {
576
+ minValue: 1,
577
+ },
527
578
  default: 50,
528
579
  description: 'Max number of results to return',
529
- typeOptions: { minValue: 1, maxValue: 500 },
530
580
  displayOptions: { show: { resource: ['contact'], operation: ['getAll'] } },
531
581
  },
532
582
  {
@@ -538,9 +588,23 @@ class Flowlu {
538
588
  displayOptions: { show: { resource: ['contact'], operation: ['getAll'] } },
539
589
  options: [
540
590
  { displayName: 'Name', name: 'name', type: 'string', default: '', description: 'Filter by contact name' },
541
- { displayName: 'Email', name: 'email', type: 'string', default: '', description: 'Filter by email address' },
591
+ {
592
+ displayName: 'Email',
593
+ name: 'email',
594
+ type: 'string',
595
+ placeholder: 'name@email.com',
596
+ default: '',
597
+ description: 'Filter by email address',
598
+ },
542
599
  { displayName: 'Phone', name: 'phone', type: 'string', default: '', description: 'Filter by phone number' },
543
- { displayName: 'Owner', name: 'owner_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '', description: 'Filter by assigned owner' },
600
+ {
601
+ displayName: 'Owner Name or ID',
602
+ name: 'owner_id',
603
+ type: 'options',
604
+ typeOptions: { loadOptionsMethod: 'getUsers' },
605
+ default: '',
606
+ description: 'Filter by assigned owner. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
607
+ },
544
608
  ],
545
609
  },
546
610
  // ========================================
@@ -565,32 +629,32 @@ class Flowlu {
565
629
  displayOptions: { show: { resource: ['opportunity'], operation: ['create'] } },
566
630
  },
567
631
  {
568
- displayName: 'Pipeline',
632
+ displayName: 'Pipeline Name or ID',
569
633
  name: 'pipeline_id',
570
634
  type: 'options',
571
635
  typeOptions: { loadOptionsMethod: 'getPipelines' },
572
636
  required: true,
573
637
  default: '',
574
- description: 'The sales pipeline for this opportunity',
638
+ description: 'The sales pipeline for this opportunity. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
575
639
  displayOptions: { show: { resource: ['opportunity'], operation: ['create'] } },
576
640
  },
577
641
  {
578
- displayName: 'Pipeline Stage',
642
+ displayName: 'Pipeline Stage Name or ID',
579
643
  name: 'pipeline_stage_id',
580
644
  type: 'options',
581
645
  typeOptions: { loadOptionsMethod: 'getPipelineStages' },
582
646
  required: true,
583
647
  default: '',
584
- description: 'The stage within the pipeline',
648
+ description: 'The stage within the pipeline. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
585
649
  displayOptions: { show: { resource: ['opportunity'], operation: ['create'] } },
586
650
  },
587
651
  {
588
- displayName: 'Assignee',
652
+ displayName: 'Assignee Name or ID',
589
653
  name: 'opportunityAssignee',
590
654
  type: 'options',
591
655
  typeOptions: { loadOptionsMethod: 'getUsers' },
592
656
  default: '',
593
- description: 'The user responsible for this opportunity',
657
+ description: 'The user responsible for this opportunity. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
594
658
  displayOptions: { show: { resource: ['opportunity'], operation: ['create'] } },
595
659
  },
596
660
  // Opportunity Create: Additional Fields
@@ -602,20 +666,27 @@ class Flowlu {
602
666
  default: {},
603
667
  displayOptions: { show: { resource: ['opportunity'], operation: ['create'] } },
604
668
  options: [
605
- { displayName: 'Budget', name: 'budget', type: 'number', default: 0, description: 'Opportunity value/amount' },
606
- { displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
607
- { displayName: 'Link to Account', name: 'link_account_id', type: 'options', typeOptions: { loadOptionsMethod: 'getAccounts' }, default: '', description: 'Link this opportunity to an existing CRM account (organization)' },
608
- { displayName: 'Link to Contact', name: 'link_contact_id', type: 'options', typeOptions: { loadOptionsMethod: 'getContacts' }, default: '', description: 'Link this opportunity to an existing CRM contact' },
609
- { displayName: 'Start Date', name: 'start_date', type: 'dateTime', default: '' },
610
- { displayName: 'Planned Close Date', name: 'deadline', type: 'dateTime', default: '' },
611
- { displayName: 'Source', name: 'source_id', type: 'options', typeOptions: { loadOptionsMethod: 'getOpportunitySources' }, default: '', description: 'Where this opportunity came from' },
612
- { displayName: 'Contact Name', name: 'contact_name', type: 'string', default: '', description: 'Quick-add contact name (if not linking to existing contact)' },
669
+ {
670
+ displayName: 'Budget',
671
+ name: 'budget',
672
+ type: 'number',
673
+ default: 0,
674
+ description: 'Opportunity value/amount',
675
+ },
676
+ { displayName: 'Contact Company', name: 'contact_company', type: 'string', default: '' },
613
677
  { displayName: 'Contact Email', name: 'contact_email', type: 'string', default: '' },
614
- { displayName: 'Contact Phone', name: 'contact_phone', type: 'string', default: '' },
615
678
  { displayName: 'Contact Mobile', name: 'contact_mobile', type: 'string', default: '' },
616
- { displayName: 'Contact Company', name: 'contact_company', type: 'string', default: '' },
679
+ {
680
+ displayName: 'Contact Name',
681
+ name: 'contact_name',
682
+ type: 'string',
683
+ default: '',
684
+ description: 'Quick-add contact name (if not linking to existing contact)',
685
+ },
686
+ { displayName: 'Contact Phone', name: 'contact_phone', type: 'string', default: '' },
617
687
  { displayName: 'Contact Position', name: 'contact_position', type: 'string', default: '' },
618
688
  { displayName: 'Contact Website', name: 'contact_web', type: 'string', default: '' },
689
+ { displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
619
690
  {
620
691
  displayName: 'Include Link to Workflow',
621
692
  name: 'includeLinkToWorkflow',
@@ -623,6 +694,32 @@ class Flowlu {
623
694
  default: false,
624
695
  description: 'Whether to append a "Generated via n8n: View Workflow" footer to the opportunity description, linking back to this workflow',
625
696
  },
697
+ {
698
+ displayName: 'Link to Account Name or ID',
699
+ name: 'link_account_id',
700
+ type: 'options',
701
+ typeOptions: { loadOptionsMethod: 'getAccounts' },
702
+ default: '',
703
+ description: 'Link this opportunity to an existing CRM account (organization). Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
704
+ },
705
+ {
706
+ displayName: 'Link to Contact Name or ID',
707
+ name: 'link_contact_id',
708
+ type: 'options',
709
+ typeOptions: { loadOptionsMethod: 'getContacts' },
710
+ default: '',
711
+ description: 'Link this opportunity to an existing CRM contact. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
712
+ },
713
+ { displayName: 'Planned Close Date', name: 'deadline', type: 'dateTime', default: '' },
714
+ {
715
+ displayName: 'Source Name or ID',
716
+ name: 'source_id',
717
+ type: 'options',
718
+ typeOptions: { loadOptionsMethod: 'getOpportunitySources' },
719
+ default: '',
720
+ description: 'Where this opportunity came from. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
721
+ },
722
+ { displayName: 'Start Date', name: 'start_date', type: 'dateTime', default: '' },
626
723
  ],
627
724
  },
628
725
  {
@@ -651,16 +748,68 @@ class Flowlu {
651
748
  default: {},
652
749
  displayOptions: { show: { resource: ['opportunity'], operation: ['update'] } },
653
750
  options: [
654
- { displayName: 'Name', name: 'name', type: 'string', default: '' },
655
- { displayName: 'Pipeline', name: 'pipeline_id', type: 'options', typeOptions: { loadOptionsMethod: 'getPipelines' }, default: '' },
656
- { displayName: 'Pipeline Stage', name: 'pipeline_stage_id', type: 'options', typeOptions: { loadOptionsMethod: 'getPipelineStages' }, default: '' },
657
- { displayName: 'Assignee', name: 'assignee_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
751
+ {
752
+ displayName: 'Assignee Name or ID',
753
+ name: 'assignee_id',
754
+ type: 'options',
755
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
756
+ typeOptions: { loadOptionsMethod: 'getUsers' },
757
+ default: '',
758
+ },
658
759
  { displayName: 'Budget', name: 'budget', type: 'number', default: 0 },
760
+ {
761
+ displayName: 'Closing Comment',
762
+ name: 'closing_comment',
763
+ type: 'string',
764
+ default: '',
765
+ description: 'Reason for win/loss',
766
+ },
767
+ { displayName: 'Contact Email', name: 'contact_email', type: 'string', default: '' },
768
+ { displayName: 'Contact Name', name: 'contact_name', type: 'string', default: '' },
769
+ { displayName: 'Contact Phone', name: 'contact_phone', type: 'string', default: '' },
659
770
  { displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
660
- { displayName: 'Link to Account', name: 'link_account_id', type: 'options', typeOptions: { loadOptionsMethod: 'getAccounts' }, default: '', description: 'Link this opportunity to a CRM account' },
661
- { displayName: 'Link to Contact', name: 'link_contact_id', type: 'options', typeOptions: { loadOptionsMethod: 'getContacts' }, default: '', description: 'Link this opportunity to a CRM contact' },
662
- { displayName: 'Start Date', name: 'start_date', type: 'dateTime', default: '' },
771
+ {
772
+ displayName: 'Include Link to Workflow',
773
+ name: 'includeLinkToWorkflow',
774
+ type: 'boolean',
775
+ default: false,
776
+ description: 'Whether to append a "Generated via n8n: View Workflow" footer to the opportunity description, linking back to this workflow. Requires Description to be set.',
777
+ },
778
+ {
779
+ displayName: 'Link to Account Name or ID',
780
+ name: 'link_account_id',
781
+ type: 'options',
782
+ typeOptions: { loadOptionsMethod: 'getAccounts' },
783
+ default: '',
784
+ description: 'Link this opportunity to a CRM account. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
785
+ },
786
+ {
787
+ displayName: 'Link to Contact Name or ID',
788
+ name: 'link_contact_id',
789
+ type: 'options',
790
+ typeOptions: { loadOptionsMethod: 'getContacts' },
791
+ default: '',
792
+ description: 'Link this opportunity to a CRM contact. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
793
+ },
794
+ { displayName: 'Name', name: 'name', type: 'string', default: '' },
795
+ {
796
+ displayName: 'Pipeline Name or ID',
797
+ name: 'pipeline_id',
798
+ type: 'options',
799
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
800
+ typeOptions: { loadOptionsMethod: 'getPipelines' },
801
+ default: '',
802
+ },
803
+ {
804
+ displayName: 'Pipeline Stage Name or ID',
805
+ name: 'pipeline_stage_id',
806
+ type: 'options',
807
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
808
+ typeOptions: { loadOptionsMethod: 'getPipelineStages' },
809
+ default: '',
810
+ },
663
811
  { displayName: 'Planned Close Date', name: 'deadline', type: 'dateTime', default: '' },
812
+ { displayName: 'Start Date', name: 'start_date', type: 'dateTime', default: '' },
664
813
  {
665
814
  displayName: 'Status',
666
815
  name: 'active',
@@ -672,17 +821,6 @@ class Flowlu {
672
821
  ],
673
822
  default: 1,
674
823
  },
675
- { displayName: 'Closing Comment', name: 'closing_comment', type: 'string', default: '', description: 'Reason for win/loss' },
676
- { displayName: 'Contact Name', name: 'contact_name', type: 'string', default: '' },
677
- { displayName: 'Contact Email', name: 'contact_email', type: 'string', default: '' },
678
- { displayName: 'Contact Phone', name: 'contact_phone', type: 'string', default: '' },
679
- {
680
- displayName: 'Include Link to Workflow',
681
- name: 'includeLinkToWorkflow',
682
- type: 'boolean',
683
- default: false,
684
- description: 'Whether to append a "Generated via n8n: View Workflow" footer to the opportunity description, linking back to this workflow. Requires Description to be set.',
685
- },
686
824
  ],
687
825
  },
688
826
  {
@@ -707,22 +845,24 @@ class Flowlu {
707
845
  displayName: 'Limit',
708
846
  name: 'limit',
709
847
  type: 'number',
848
+ typeOptions: {
849
+ minValue: 1,
850
+ },
710
851
  default: 50,
711
852
  description: 'Max number of results to return',
712
- typeOptions: { minValue: 1, maxValue: 500 },
713
853
  displayOptions: { show: { resource: ['opportunity'], operation: ['getAll'] } },
714
854
  },
715
855
  {
716
- displayName: 'Pipeline',
856
+ displayName: 'Pipeline Name or ID',
717
857
  name: 'opportunityFilterPipeline',
718
858
  type: 'options',
719
859
  typeOptions: { loadOptionsMethod: 'getPipelines' },
720
860
  default: '',
721
- description: 'Filter by pipeline (also controls which stages appear below)',
861
+ description: 'Filter by pipeline (also controls which stages appear below). Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
722
862
  displayOptions: { show: { resource: ['opportunity'], operation: ['getAll'] } },
723
863
  },
724
864
  {
725
- displayName: 'Pipeline Stage',
865
+ displayName: 'Pipeline Stage Name or ID',
726
866
  name: 'opportunityFilterStage',
727
867
  type: 'options',
728
868
  typeOptions: {
@@ -730,7 +870,7 @@ class Flowlu {
730
870
  loadOptionsDependsOn: ['opportunityFilterPipeline'],
731
871
  },
732
872
  default: '',
733
- description: 'Filter by stage (select a pipeline first to see its stages)',
873
+ description: 'Filter by stage (select a pipeline first to see its stages). Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
734
874
  displayOptions: { show: { resource: ['opportunity'], operation: ['getAll'] } },
735
875
  },
736
876
  {
@@ -742,8 +882,22 @@ class Flowlu {
742
882
  displayOptions: { show: { resource: ['opportunity'], operation: ['getAll'] } },
743
883
  options: [
744
884
  { displayName: 'Name', name: 'name', type: 'string', default: '', description: 'Filter by opportunity name' },
745
- { displayName: 'Assignee', name: 'assignee_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
746
- { displayName: 'Account / Contact', name: 'company_id', type: 'options', typeOptions: { loadOptionsMethod: 'getAllCrmAccounts' }, default: '', description: 'Filter by linked company or contact (note: Flowlu may not support this filter)' },
885
+ {
886
+ displayName: 'Assignee Name or ID',
887
+ name: 'assignee_id',
888
+ type: 'options',
889
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
890
+ typeOptions: { loadOptionsMethod: 'getUsers' },
891
+ default: '',
892
+ },
893
+ {
894
+ displayName: 'Account / Contact Name or ID',
895
+ name: 'company_id',
896
+ type: 'options',
897
+ typeOptions: { loadOptionsMethod: 'getAllCrmAccounts' },
898
+ default: '',
899
+ description: 'Filter by linked company or contact (note: Flowlu may not support this filter). Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
900
+ },
747
901
  {
748
902
  displayName: 'Status',
749
903
  name: 'active',
@@ -779,12 +933,12 @@ class Flowlu {
779
933
  displayOptions: { show: { resource: ['project'], operation: ['create'] } },
780
934
  },
781
935
  {
782
- displayName: 'Manager',
936
+ displayName: 'Manager Name or ID',
783
937
  name: 'manager_id',
784
938
  type: 'options',
785
939
  typeOptions: { loadOptionsMethod: 'getUsers' },
786
940
  default: '',
787
- description: 'The user responsible for managing this project',
941
+ description: 'The user responsible for managing this project. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
788
942
  displayOptions: { show: { resource: ['project'], operation: ['create'] } },
789
943
  },
790
944
  {
@@ -804,26 +958,6 @@ class Flowlu {
804
958
  default: {},
805
959
  displayOptions: { show: { resource: ['project'], operation: ['create'] } },
806
960
  options: [
807
- {
808
- displayName: 'Priority',
809
- name: 'priority',
810
- type: 'options',
811
- options: [
812
- { name: 'Low', value: 1 },
813
- { name: 'Medium', value: 2 },
814
- { name: 'High', value: 3 },
815
- ],
816
- default: 2,
817
- },
818
- { displayName: 'Start Date', name: 'startdate', type: 'dateTime', default: '' },
819
- { displayName: 'End Date', name: 'enddate', type: 'dateTime', default: '' },
820
- { displayName: 'Contract Sum (Revenue)', name: 'estimated_revenue', type: 'number', default: 0 },
821
- { displayName: 'Expense Sum', name: 'estimated_expenses', type: 'number', default: 0 },
822
- { displayName: 'Customer (Company ID)', name: 'customer_id', type: 'options', typeOptions: { loadOptionsMethod: 'getContacts' }, default: '', description: 'CRM company linked to this project' },
823
- { displayName: 'Customer Contact', name: 'customer_crm_contact_id', type: 'options', typeOptions: { loadOptionsMethod: 'getContacts' }, default: '', description: 'CRM contact linked to this project' },
824
- { displayName: 'Portfolio', name: 'briefcase_id', type: 'options', typeOptions: { loadOptionsMethod: 'getPortfolios' }, default: '' },
825
- { displayName: 'Project Stage', name: 'stage_id', type: 'options', typeOptions: { loadOptionsMethod: 'getProjectStages' }, default: '' },
826
- { displayName: 'Task Workflow', name: 'tasks_workflow_id', type: 'options', typeOptions: { loadOptionsMethod: 'getTaskWorkflows' }, default: '', description: 'Default task workflow for tasks in this project' },
827
961
  {
828
962
  displayName: 'Billing Type',
829
963
  name: 'billing_type',
@@ -836,7 +970,25 @@ class Flowlu {
836
970
  ],
837
971
  default: 0,
838
972
  },
839
- { displayName: 'Opportunity', name: 'crm_lead_id', type: 'number', default: 0, description: 'ID of a linked CRM opportunity' },
973
+ { displayName: 'Contract Sum (Revenue)', name: 'estimated_revenue', type: 'number', default: 0 },
974
+ {
975
+ displayName: 'Customer (Company ID) Name or ID',
976
+ name: 'customer_id',
977
+ type: 'options',
978
+ typeOptions: { loadOptionsMethod: 'getContacts' },
979
+ default: '',
980
+ description: 'CRM company linked to this project. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
981
+ },
982
+ {
983
+ displayName: 'Customer Contact Name or ID',
984
+ name: 'customer_crm_contact_id',
985
+ type: 'options',
986
+ typeOptions: { loadOptionsMethod: 'getContacts' },
987
+ default: '',
988
+ description: 'CRM contact linked to this project. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
989
+ },
990
+ { displayName: 'End Date', name: 'enddate', type: 'dateTime', default: '' },
991
+ { displayName: 'Expense Sum', name: 'estimated_expenses', type: 'number', default: 0 },
840
992
  {
841
993
  displayName: 'Include Link to Workflow',
842
994
  name: 'includeLinkToWorkflow',
@@ -844,6 +996,49 @@ class Flowlu {
844
996
  default: false,
845
997
  description: 'Whether to append a "Generated via n8n: View Workflow" footer to the project description, linking back to this workflow',
846
998
  },
999
+ {
1000
+ displayName: 'Opportunity',
1001
+ name: 'crm_lead_id',
1002
+ type: 'number',
1003
+ default: 0,
1004
+ description: 'ID of a linked CRM opportunity',
1005
+ },
1006
+ {
1007
+ displayName: 'Portfolio Name or ID',
1008
+ name: 'briefcase_id',
1009
+ type: 'options',
1010
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1011
+ typeOptions: { loadOptionsMethod: 'getPortfolios' },
1012
+ default: '',
1013
+ },
1014
+ {
1015
+ displayName: 'Priority',
1016
+ name: 'priority',
1017
+ type: 'options',
1018
+ options: [
1019
+ { name: 'Low', value: 1 },
1020
+ { name: 'Medium', value: 2 },
1021
+ { name: 'High', value: 3 },
1022
+ ],
1023
+ default: 2,
1024
+ },
1025
+ {
1026
+ displayName: 'Project Stage Name or ID',
1027
+ name: 'stage_id',
1028
+ type: 'options',
1029
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1030
+ typeOptions: { loadOptionsMethod: 'getProjectStages' },
1031
+ default: '',
1032
+ },
1033
+ { displayName: 'Start Date', name: 'startdate', type: 'dateTime', default: '' },
1034
+ {
1035
+ displayName: 'Task Workflow Name or ID',
1036
+ name: 'tasks_workflow_id',
1037
+ type: 'options',
1038
+ typeOptions: { loadOptionsMethod: 'getTaskWorkflows' },
1039
+ default: '',
1040
+ description: 'Default task workflow for tasks in this project. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
1041
+ },
847
1042
  ],
848
1043
  },
849
1044
  {
@@ -872,9 +1067,31 @@ class Flowlu {
872
1067
  default: {},
873
1068
  displayOptions: { show: { resource: ['project'], operation: ['update'] } },
874
1069
  options: [
875
- { displayName: 'Project Name', name: 'name', type: 'string', default: '' },
1070
+ {
1071
+ displayName: 'Archived',
1072
+ name: 'is_archive',
1073
+ type: 'boolean',
1074
+ default: false,
1075
+ },
1076
+ { displayName: 'Contract Sum (Revenue)', name: 'estimated_revenue', type: 'number', default: 0 },
876
1077
  { displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
877
- { displayName: 'Manager', name: 'manager_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
1078
+ { displayName: 'End Date', name: 'enddate', type: 'dateTime', default: '' },
1079
+ { displayName: 'Expense Sum', name: 'estimated_expenses', type: 'number', default: 0 },
1080
+ {
1081
+ displayName: 'Include Link to Workflow',
1082
+ name: 'includeLinkToWorkflow',
1083
+ type: 'boolean',
1084
+ default: false,
1085
+ description: 'Whether to append a "Generated via n8n: View Workflow" footer to the project description, linking back to this workflow. Requires Description to be set.',
1086
+ },
1087
+ {
1088
+ displayName: 'Manager Name or ID',
1089
+ name: 'manager_id',
1090
+ type: 'options',
1091
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1092
+ typeOptions: { loadOptionsMethod: 'getUsers' },
1093
+ default: '',
1094
+ },
878
1095
  {
879
1096
  displayName: 'Priority',
880
1097
  name: 'priority',
@@ -886,24 +1103,16 @@ class Flowlu {
886
1103
  ],
887
1104
  default: 2,
888
1105
  },
889
- { displayName: 'Start Date', name: 'startdate', type: 'dateTime', default: '' },
890
- { displayName: 'End Date', name: 'enddate', type: 'dateTime', default: '' },
891
- { displayName: 'Contract Sum (Revenue)', name: 'estimated_revenue', type: 'number', default: 0 },
892
- { displayName: 'Expense Sum', name: 'estimated_expenses', type: 'number', default: 0 },
893
- { displayName: 'Project Stage', name: 'stage_id', type: 'options', typeOptions: { loadOptionsMethod: 'getProjectStages' }, default: '' },
894
- {
895
- displayName: 'Archived',
896
- name: 'is_archive',
897
- type: 'boolean',
898
- default: false,
899
- },
1106
+ { displayName: 'Project Name', name: 'name', type: 'string', default: '' },
900
1107
  {
901
- displayName: 'Include Link to Workflow',
902
- name: 'includeLinkToWorkflow',
903
- type: 'boolean',
904
- default: false,
905
- description: 'Whether to append a "Generated via n8n: View Workflow" footer to the project description, linking back to this workflow. Requires Description to be set.',
1108
+ displayName: 'Project Stage Name or ID',
1109
+ name: 'stage_id',
1110
+ type: 'options',
1111
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1112
+ typeOptions: { loadOptionsMethod: 'getProjectStages' },
1113
+ default: '',
906
1114
  },
1115
+ { displayName: 'Start Date', name: 'startdate', type: 'dateTime', default: '' },
907
1116
  ],
908
1117
  },
909
1118
  {
@@ -928,9 +1137,11 @@ class Flowlu {
928
1137
  displayName: 'Limit',
929
1138
  name: 'limit',
930
1139
  type: 'number',
1140
+ typeOptions: {
1141
+ minValue: 1,
1142
+ },
931
1143
  default: 50,
932
1144
  description: 'Max number of results to return',
933
- typeOptions: { minValue: 1, maxValue: 500 },
934
1145
  displayOptions: { show: { resource: ['project'], operation: ['getAll'] } },
935
1146
  },
936
1147
  {
@@ -942,8 +1153,22 @@ class Flowlu {
942
1153
  displayOptions: { show: { resource: ['project'], operation: ['getAll'] } },
943
1154
  options: [
944
1155
  { displayName: 'Name', name: 'name', type: 'string', default: '', description: 'Filter by project name' },
945
- { displayName: 'Manager', name: 'manager_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
946
- { displayName: 'Customer', name: 'customer_id', type: 'options', typeOptions: { loadOptionsMethod: 'getAccounts' }, default: '', description: 'Filter by linked company' },
1156
+ {
1157
+ displayName: 'Manager Name or ID',
1158
+ name: 'manager_id',
1159
+ type: 'options',
1160
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1161
+ typeOptions: { loadOptionsMethod: 'getUsers' },
1162
+ default: '',
1163
+ },
1164
+ {
1165
+ displayName: 'Customer Name or ID',
1166
+ name: 'customer_id',
1167
+ type: 'options',
1168
+ typeOptions: { loadOptionsMethod: 'getAccounts' },
1169
+ default: '',
1170
+ description: 'Filter by linked company. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
1171
+ },
947
1172
  {
948
1173
  displayName: 'Include Archived',
949
1174
  name: 'is_archive',
@@ -956,13 +1181,13 @@ class Flowlu {
956
1181
  // RECORD LIST FIELDS
957
1182
  // ========================================
958
1183
  {
959
- displayName: 'Record List',
1184
+ displayName: 'Record List Name or ID',
960
1185
  name: 'recordListId',
961
1186
  type: 'options',
962
1187
  typeOptions: { loadOptionsMethod: 'getRecordLists' },
963
1188
  required: true,
964
1189
  default: '',
965
- description: 'Which record list to work with',
1190
+ description: 'Which record list to work with. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
966
1191
  displayOptions: { show: { resource: ['recordList'] } },
967
1192
  },
968
1193
  {
@@ -1023,9 +1248,11 @@ class Flowlu {
1023
1248
  displayName: 'Limit',
1024
1249
  name: 'limit',
1025
1250
  type: 'number',
1251
+ typeOptions: {
1252
+ minValue: 1,
1253
+ },
1026
1254
  default: 50,
1027
1255
  description: 'Max number of results to return',
1028
- typeOptions: { minValue: 1, maxValue: 500 },
1029
1256
  displayOptions: { show: { resource: ['recordList'], operation: ['getAll'] } },
1030
1257
  },
1031
1258
  {
@@ -1036,7 +1263,13 @@ class Flowlu {
1036
1263
  default: {},
1037
1264
  displayOptions: { show: { resource: ['recordList'], operation: ['getAll'] } },
1038
1265
  options: [
1039
- { displayName: 'Search', name: 'search', type: 'string', default: '', description: 'Search records by name/content' },
1266
+ {
1267
+ displayName: 'Search',
1268
+ name: 'search',
1269
+ type: 'string',
1270
+ default: '',
1271
+ description: 'Search records by name/content',
1272
+ },
1040
1273
  ],
1041
1274
  },
1042
1275
  // ========================================
@@ -1061,13 +1294,13 @@ class Flowlu {
1061
1294
  displayOptions: { show: { resource: ['task'], operation: ['create'] } },
1062
1295
  },
1063
1296
  {
1064
- displayName: 'Assignee',
1297
+ displayName: 'Assignee Name or ID',
1065
1298
  name: 'responsible_id',
1066
1299
  type: 'options',
1067
1300
  typeOptions: { loadOptionsMethod: 'getUsers' },
1068
1301
  required: true,
1069
1302
  default: '',
1070
- description: 'The user responsible for completing this task',
1303
+ description: 'The user responsible for completing this task. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
1071
1304
  displayOptions: { show: { resource: ['task'], operation: ['create'] } },
1072
1305
  },
1073
1306
  {
@@ -1087,11 +1320,40 @@ class Flowlu {
1087
1320
  default: {},
1088
1321
  displayOptions: { show: { resource: ['task'], operation: ['create'] } },
1089
1322
  options: [
1090
- { displayName: 'Owner', name: 'owner_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, description: 'The user who owns/created this task', default: '' },
1091
- { displayName: 'Contact', name: 'contact_id', type: 'options', typeOptions: { loadOptionsMethod: 'getContacts' }, default: '', description: 'The contact this task is related to' },
1092
- { displayName: 'Project', name: 'model_id', type: 'options', typeOptions: { loadOptionsMethod: 'getProjects' }, description: 'The project this task belongs to', default: '' },
1093
- { displayName: 'Workflow', name: 'workflow_id', type: 'options', typeOptions: { loadOptionsMethod: 'getTaskWorkflows' }, default: '', description: 'The task workflow to use (defaults to the first workflow if not set)' },
1094
- { displayName: 'Workflow Stage', name: 'workflow_stage_id', type: 'options', typeOptions: { loadOptionsMethod: 'getTaskWorkflowStages' }, default: '', description: 'The starting stage within the workflow' },
1323
+ { displayName: 'Allow End Date Change', name: 'deadline_allowchange', type: 'boolean', default: true },
1324
+ {
1325
+ displayName: 'Contact Name or ID',
1326
+ name: 'contact_id',
1327
+ type: 'options',
1328
+ typeOptions: { loadOptionsMethod: 'getContacts' },
1329
+ default: '',
1330
+ description: 'The contact this task is related to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
1331
+ },
1332
+ { displayName: 'End Date', name: 'deadline', type: 'dateTime', default: '' },
1333
+ {
1334
+ displayName: 'Include Link to Workflow',
1335
+ name: 'includeLinkToWorkflow',
1336
+ type: 'boolean',
1337
+ default: false,
1338
+ description: 'Whether to append a "Generated via n8n: View Workflow" footer to the task description, linking back to this workflow',
1339
+ },
1340
+ {
1341
+ displayName: 'Owner Name or ID',
1342
+ name: 'owner_id',
1343
+ type: 'options',
1344
+ typeOptions: { loadOptionsMethod: 'getUsers' },
1345
+ description: 'The user who owns/created this task. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
1346
+ default: '',
1347
+ },
1348
+ {
1349
+ displayName: 'Parent Task ID',
1350
+ name: 'parent_id',
1351
+ type: 'number',
1352
+ default: 0,
1353
+ description: 'ID of the parent task (for subtasks)',
1354
+ },
1355
+ { displayName: 'Planned Cost', name: 'cost', type: 'number', default: 0, typeOptions: { minValue: 0 } },
1356
+ { displayName: 'Planned Income', name: 'price', type: 'number', default: 0, typeOptions: { minValue: 0 } },
1095
1357
  {
1096
1358
  displayName: 'Priority',
1097
1359
  name: 'priority',
@@ -1103,6 +1365,16 @@ class Flowlu {
1103
1365
  ],
1104
1366
  default: 2,
1105
1367
  },
1368
+ {
1369
+ displayName: 'Project Name or ID',
1370
+ name: 'model_id',
1371
+ type: 'options',
1372
+ typeOptions: { loadOptionsMethod: 'getProjects' },
1373
+ description: 'The project this task belongs to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
1374
+ default: '',
1375
+ },
1376
+ { displayName: 'Reviewed by Owner', name: 'task_checkbyowner', type: 'boolean', default: false },
1377
+ { displayName: 'Start Date', name: 'plan_start_date', type: 'dateTime', default: '' },
1106
1378
  {
1107
1379
  displayName: 'Status',
1108
1380
  name: 'status',
@@ -1115,20 +1387,28 @@ class Flowlu {
1115
1387
  ],
1116
1388
  default: 1,
1117
1389
  },
1118
- { displayName: 'Start Date', name: 'plan_start_date', type: 'dateTime', default: '' },
1119
- { displayName: 'End Date', name: 'deadline', type: 'dateTime', default: '' },
1120
- { displayName: 'Time Estimate (Minutes)', name: 'time_estimate', type: 'number', default: 0, typeOptions: { minValue: 0 } },
1121
- { displayName: 'Planned Cost', name: 'cost', type: 'number', default: 0, typeOptions: { minValue: 0 } },
1122
- { displayName: 'Planned Income', name: 'price', type: 'number', default: 0, typeOptions: { minValue: 0 } },
1123
- { displayName: 'Reviewed by Owner', name: 'task_checkbyowner', type: 'boolean', default: false },
1124
- { displayName: 'Allow End Date Change', name: 'deadline_allowchange', type: 'boolean', default: true },
1125
- { displayName: 'Parent Task ID', name: 'parent_id', type: 'number', default: 0, description: 'ID of the parent task (for subtasks)' },
1126
1390
  {
1127
- displayName: 'Include Link to Workflow',
1128
- name: 'includeLinkToWorkflow',
1129
- type: 'boolean',
1130
- default: false,
1131
- description: 'Whether to append a "Generated via n8n: View Workflow" footer to the task description, linking back to this workflow',
1391
+ displayName: 'Time Estimate (Minutes)',
1392
+ name: 'time_estimate',
1393
+ type: 'number',
1394
+ default: 0,
1395
+ typeOptions: { minValue: 0 },
1396
+ },
1397
+ {
1398
+ displayName: 'Workflow Name or ID',
1399
+ name: 'workflow_id',
1400
+ type: 'options',
1401
+ typeOptions: { loadOptionsMethod: 'getTaskWorkflows' },
1402
+ default: '',
1403
+ description: 'The task workflow to use (defaults to the first workflow if not set). Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
1404
+ },
1405
+ {
1406
+ displayName: 'Workflow Stage Name or ID',
1407
+ name: 'workflow_stage_id',
1408
+ type: 'options',
1409
+ typeOptions: { loadOptionsMethod: 'getTaskWorkflowStages' },
1410
+ default: '',
1411
+ description: 'The starting stage within the workflow. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
1132
1412
  },
1133
1413
  ],
1134
1414
  },
@@ -1158,13 +1438,34 @@ class Flowlu {
1158
1438
  default: {},
1159
1439
  displayOptions: { show: { resource: ['task'], operation: ['update'] } },
1160
1440
  options: [
1161
- { displayName: 'Task Name', name: 'name', type: 'string', default: '' },
1441
+ {
1442
+ displayName: 'Assignee Name or ID',
1443
+ name: 'responsible_id',
1444
+ type: 'options',
1445
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1446
+ typeOptions: { loadOptionsMethod: 'getUsers' },
1447
+ default: '',
1448
+ },
1162
1449
  { displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
1163
- { displayName: 'Assignee', name: 'responsible_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
1164
- { displayName: 'Owner', name: 'owner_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
1165
- { displayName: 'Project', name: 'model_id', type: 'options', typeOptions: { loadOptionsMethod: 'getProjects' }, default: '' },
1166
- { displayName: 'Workflow', name: 'workflow_id', type: 'options', typeOptions: { loadOptionsMethod: 'getTaskWorkflows' }, default: '' },
1167
- { displayName: 'Workflow Stage', name: 'workflow_stage_id', type: 'options', typeOptions: { loadOptionsMethod: 'getTaskWorkflowStages' }, default: '' },
1450
+ { displayName: 'End Date', name: 'deadline', type: 'dateTime', default: '' },
1451
+ {
1452
+ displayName: 'Include Link to Workflow',
1453
+ name: 'includeLinkToWorkflow',
1454
+ type: 'boolean',
1455
+ default: false,
1456
+ description: 'Whether to append a "Generated via n8n: View Workflow" footer to the task description, linking back to this workflow. Requires Description to be set.',
1457
+ },
1458
+ {
1459
+ displayName: 'Owner Name or ID',
1460
+ name: 'owner_id',
1461
+ type: 'options',
1462
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1463
+ typeOptions: { loadOptionsMethod: 'getUsers' },
1464
+ default: '',
1465
+ },
1466
+ { displayName: 'Parent Task ID', name: 'parent_id', type: 'number', default: 0 },
1467
+ { displayName: 'Planned Cost', name: 'cost', type: 'number', default: 0, typeOptions: { minValue: 0 } },
1468
+ { displayName: 'Planned Income', name: 'price', type: 'number', default: 0, typeOptions: { minValue: 0 } },
1168
1469
  {
1169
1470
  displayName: 'Priority',
1170
1471
  name: 'priority',
@@ -1176,6 +1477,15 @@ class Flowlu {
1176
1477
  ],
1177
1478
  default: 2,
1178
1479
  },
1480
+ {
1481
+ displayName: 'Project Name or ID',
1482
+ name: 'model_id',
1483
+ type: 'options',
1484
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1485
+ typeOptions: { loadOptionsMethod: 'getProjects' },
1486
+ default: '',
1487
+ },
1488
+ { displayName: 'Start Date', name: 'plan_start_date', type: 'dateTime', default: '' },
1179
1489
  {
1180
1490
  displayName: 'Status',
1181
1491
  name: 'status',
@@ -1188,18 +1498,29 @@ class Flowlu {
1188
1498
  ],
1189
1499
  default: 1,
1190
1500
  },
1191
- { displayName: 'Start Date', name: 'plan_start_date', type: 'dateTime', default: '' },
1192
- { displayName: 'End Date', name: 'deadline', type: 'dateTime', default: '' },
1193
- { displayName: 'Time Estimate (Minutes)', name: 'time_estimate', type: 'number', default: 0, typeOptions: { minValue: 0 } },
1194
- { displayName: 'Planned Cost', name: 'cost', type: 'number', default: 0, typeOptions: { minValue: 0 } },
1195
- { displayName: 'Planned Income', name: 'price', type: 'number', default: 0, typeOptions: { minValue: 0 } },
1196
- { displayName: 'Parent Task ID', name: 'parent_id', type: 'number', default: 0 },
1501
+ { displayName: 'Task Name', name: 'name', type: 'string', default: '' },
1197
1502
  {
1198
- displayName: 'Include Link to Workflow',
1199
- name: 'includeLinkToWorkflow',
1200
- type: 'boolean',
1201
- default: false,
1202
- description: 'Whether to append a "Generated via n8n: View Workflow" footer to the task description, linking back to this workflow. Requires Description to be set.',
1503
+ displayName: 'Time Estimate (Minutes)',
1504
+ name: 'time_estimate',
1505
+ type: 'number',
1506
+ default: 0,
1507
+ typeOptions: { minValue: 0 },
1508
+ },
1509
+ {
1510
+ displayName: 'Workflow Name or ID',
1511
+ name: 'workflow_id',
1512
+ type: 'options',
1513
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1514
+ typeOptions: { loadOptionsMethod: 'getTaskWorkflows' },
1515
+ default: '',
1516
+ },
1517
+ {
1518
+ displayName: 'Workflow Stage Name or ID',
1519
+ name: 'workflow_stage_id',
1520
+ type: 'options',
1521
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1522
+ typeOptions: { loadOptionsMethod: 'getTaskWorkflowStages' },
1523
+ default: '',
1203
1524
  },
1204
1525
  ],
1205
1526
  },
@@ -1225,9 +1546,11 @@ class Flowlu {
1225
1546
  displayName: 'Limit',
1226
1547
  name: 'limit',
1227
1548
  type: 'number',
1549
+ typeOptions: {
1550
+ minValue: 1,
1551
+ },
1228
1552
  default: 50,
1229
1553
  description: 'Max number of results to return',
1230
- typeOptions: { minValue: 1, maxValue: 500 },
1231
1554
  displayOptions: { show: { resource: ['task'], operation: ['getAll'] } },
1232
1555
  },
1233
1556
  {
@@ -1238,9 +1561,15 @@ class Flowlu {
1238
1561
  default: {},
1239
1562
  displayOptions: { show: { resource: ['task'], operation: ['getAll'] } },
1240
1563
  options: [
1564
+ {
1565
+ displayName: 'Assignee Name or ID',
1566
+ name: 'responsible_id',
1567
+ type: 'options',
1568
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1569
+ typeOptions: { loadOptionsMethod: 'getUsers' },
1570
+ default: '',
1571
+ },
1241
1572
  { displayName: 'Name', name: 'name', type: 'string', default: '', description: 'Filter by task name' },
1242
- { displayName: 'Assignee', name: 'responsible_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
1243
- { displayName: 'Project', name: 'model_id', type: 'options', typeOptions: { loadOptionsMethod: 'getProjects' }, default: '' },
1244
1573
  {
1245
1574
  displayName: 'Priority',
1246
1575
  name: 'priority',
@@ -1252,6 +1581,14 @@ class Flowlu {
1252
1581
  ],
1253
1582
  default: 2,
1254
1583
  },
1584
+ {
1585
+ displayName: 'Project Name or ID',
1586
+ name: 'model_id',
1587
+ type: 'options',
1588
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1589
+ typeOptions: { loadOptionsMethod: 'getProjects' },
1590
+ default: '',
1591
+ },
1255
1592
  {
1256
1593
  displayName: 'Status',
1257
1594
  name: 'status',
@@ -1360,9 +1697,11 @@ class Flowlu {
1360
1697
  displayName: 'Limit',
1361
1698
  name: 'limit',
1362
1699
  type: 'number',
1700
+ typeOptions: {
1701
+ minValue: 1,
1702
+ },
1363
1703
  default: 50,
1364
1704
  description: 'Max number of results to return',
1365
- typeOptions: { minValue: 1, maxValue: 500 },
1366
1705
  displayOptions: { show: { resource: ['comment'], operation: ['getAll'] } },
1367
1706
  },
1368
1707
  ],
@@ -1374,7 +1713,10 @@ class Flowlu {
1374
1713
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
1375
1714
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/core/user/list', apiKey, undefined, { 'filter[role_login]': '1' });
1376
1715
  if (response?.response?.items) {
1377
- return response.response.items.map((user) => ({ name: user.name || user.email || `User ${user.id}`, value: user.id.toString() }));
1716
+ return response.response.items.map((user) => ({
1717
+ name: user.name || user.email || `User ${user.id}`,
1718
+ value: user.id.toString(),
1719
+ }));
1378
1720
  }
1379
1721
  return [];
1380
1722
  }
@@ -1387,7 +1729,10 @@ class Flowlu {
1387
1729
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
1388
1730
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/account/list', apiKey, undefined, { 'filter[type]': '1' });
1389
1731
  if (response?.response?.items) {
1390
- return response.response.items.map((a) => ({ name: a.name || `Account ${a.id}`, value: a.id.toString() }));
1732
+ return response.response.items.map((a) => ({
1733
+ name: a.name || `Account ${a.id}`,
1734
+ value: a.id.toString(),
1735
+ }));
1391
1736
  }
1392
1737
  return [];
1393
1738
  }
@@ -1400,7 +1745,10 @@ class Flowlu {
1400
1745
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
1401
1746
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/account/list', apiKey, undefined, { 'filter[type]': '2' });
1402
1747
  if (response?.response?.items) {
1403
- return response.response.items.map((c) => ({ name: c.name || `Contact ${c.id}`, value: c.id.toString() }));
1748
+ return response.response.items.map((c) => ({
1749
+ name: c.name || `Contact ${c.id}`,
1750
+ value: c.id.toString(),
1751
+ }));
1404
1752
  }
1405
1753
  return [];
1406
1754
  }
@@ -1413,7 +1761,10 @@ class Flowlu {
1413
1761
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
1414
1762
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/st/projects/list', apiKey, undefined, { 'filter[is_archive]': '0' });
1415
1763
  if (response?.response?.items) {
1416
- return response.response.items.map((p) => ({ name: p.name || `Project ${p.id}`, value: p.id.toString() }));
1764
+ return response.response.items.map((p) => ({
1765
+ name: p.name || `Project ${p.id}`,
1766
+ value: p.id.toString(),
1767
+ }));
1417
1768
  }
1418
1769
  return [];
1419
1770
  }
@@ -1426,7 +1777,10 @@ class Flowlu {
1426
1777
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
1427
1778
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/task/workflows/list', apiKey);
1428
1779
  if (response?.response?.items) {
1429
- return response.response.items.map((wf) => ({ name: wf.name || `Workflow ${wf.id}`, value: wf.id.toString() }));
1780
+ return response.response.items.map((wf) => ({
1781
+ name: wf.name || `Workflow ${wf.id}`,
1782
+ value: wf.id.toString(),
1783
+ }));
1430
1784
  }
1431
1785
  return [];
1432
1786
  }
@@ -1439,7 +1793,11 @@ class Flowlu {
1439
1793
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
1440
1794
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/task/stages/list', apiKey);
1441
1795
  if (response?.response?.items) {
1442
- return response.response.items.map((s) => ({ name: s.name || `Stage ${s.id}`, value: s.id.toString(), description: `Workflow ID: ${s.workflow_id}` }));
1796
+ return response.response.items.map((s) => ({
1797
+ name: s.name || `Stage ${s.id}`,
1798
+ value: s.id.toString(),
1799
+ description: `Workflow ID: ${s.workflow_id}`,
1800
+ }));
1443
1801
  }
1444
1802
  return [];
1445
1803
  }
@@ -1452,7 +1810,10 @@ class Flowlu {
1452
1810
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
1453
1811
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/pipeline/list', apiKey);
1454
1812
  if (response?.response?.items) {
1455
- return response.response.items.map((p) => ({ name: p.name || `Pipeline ${p.id}`, value: p.id.toString() }));
1813
+ return response.response.items.map((p) => ({
1814
+ name: p.name || `Pipeline ${p.id}`,
1815
+ value: p.id.toString(),
1816
+ }));
1456
1817
  }
1457
1818
  return [];
1458
1819
  }
@@ -1465,7 +1826,11 @@ class Flowlu {
1465
1826
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
1466
1827
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/pipeline_stage/list', apiKey);
1467
1828
  if (response?.response?.items) {
1468
- return response.response.items.map((s) => ({ name: s.name || `Stage ${s.id}`, value: s.id.toString(), description: `Pipeline ID: ${s.pipeline_id}` }));
1829
+ return response.response.items.map((s) => ({
1830
+ name: s.name || `Stage ${s.id}`,
1831
+ value: s.id.toString(),
1832
+ description: `Pipeline ID: ${s.pipeline_id}`,
1833
+ }));
1469
1834
  }
1470
1835
  return [];
1471
1836
  }
@@ -1480,7 +1845,9 @@ class Flowlu {
1480
1845
  try {
1481
1846
  pipelineId = this.getNodeParameter('opportunityFilterPipeline');
1482
1847
  }
1483
- catch { /* not set yet */ }
1848
+ catch {
1849
+ /* not set yet */
1850
+ }
1484
1851
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/pipeline_stage/list', apiKey);
1485
1852
  if (response?.response?.items) {
1486
1853
  let stages = response.response.items;
@@ -1516,7 +1883,10 @@ class Flowlu {
1516
1883
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
1517
1884
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/source/list', apiKey);
1518
1885
  if (response?.response?.items) {
1519
- return response.response.items.map((s) => ({ name: s.name || `Source ${s.id}`, value: s.id.toString() }));
1886
+ return response.response.items.map((s) => ({
1887
+ name: s.name || `Source ${s.id}`,
1888
+ value: s.id.toString(),
1889
+ }));
1520
1890
  }
1521
1891
  return [];
1522
1892
  }
@@ -1529,7 +1899,10 @@ class Flowlu {
1529
1899
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
1530
1900
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/st/portfolio/list', apiKey);
1531
1901
  if (response?.response?.items) {
1532
- return response.response.items.map((p) => ({ name: p.name || `Portfolio ${p.id}`, value: p.id.toString() }));
1902
+ return response.response.items.map((p) => ({
1903
+ name: p.name || `Portfolio ${p.id}`,
1904
+ value: p.id.toString(),
1905
+ }));
1533
1906
  }
1534
1907
  return [];
1535
1908
  }
@@ -1542,7 +1915,10 @@ class Flowlu {
1542
1915
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
1543
1916
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/st/stages/list', apiKey);
1544
1917
  if (response?.response?.items) {
1545
- return response.response.items.map((s) => ({ name: s.name || `Stage ${s.id}`, value: s.id.toString() }));
1918
+ return response.response.items.map((s) => ({
1919
+ name: s.name || `Stage ${s.id}`,
1920
+ value: s.id.toString(),
1921
+ }));
1546
1922
  }
1547
1923
  return [];
1548
1924
  }
@@ -1555,7 +1931,10 @@ class Flowlu {
1555
1931
  const { baseUrl, apiKey } = await getFlowluCredentials(this);
1556
1932
  const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customlists/lists/list', apiKey);
1557
1933
  if (response?.response?.items) {
1558
- return response.response.items.map((l) => ({ name: l.name || `List ${l.id}`, value: l.id.toString() }));
1934
+ return response.response.items.map((l) => ({
1935
+ name: l.name || `List ${l.id}`,
1936
+ value: l.id.toString(),
1937
+ }));
1559
1938
  }
1560
1939
  return [];
1561
1940
  }
@@ -1597,7 +1976,7 @@ class Flowlu {
1597
1976
  .filter((f) => fieldsetIds.includes(f.fieldset_id?.toString()))
1598
1977
  .filter((f) => f.active !== 0)
1599
1978
  .map((f) => {
1600
- const cfKey = (f.api_use_alias && f.alias) ? `cf_${f.alias}` : `cf_${f.id}`;
1979
+ const cfKey = f.api_use_alias && f.alias ? `cf_${f.alias}` : `cf_${f.id}`;
1601
1980
  let label = f.name || cfKey;
1602
1981
  let desc = '';
1603
1982
  // Detect link fields (model.any) and show what they link to
@@ -1617,7 +1996,9 @@ class Flowlu {
1617
1996
  }
1618
1997
  else if (f.module === 'customlists' && f.model === 'items' && f.group_id) {
1619
1998
  // Link to another custom list - find its name
1620
- const targetList = allFieldsets.find((fs) => fs.module === 'customlists' && fs.model === 'items' && fs.group_id?.toString() === f.group_id.toString());
1999
+ const targetList = allFieldsets.find((fs) => fs.module === 'customlists' &&
2000
+ fs.model === 'items' &&
2001
+ fs.group_id?.toString() === f.group_id.toString());
1621
2002
  const listName = targetList?.name || `List #${f.group_id}`;
1622
2003
  label = `${f.name} → ${listName}`;
1623
2004
  desc = `Enter the Record ID from "${listName}"`;
@@ -1749,7 +2130,7 @@ class Flowlu {
1749
2130
  // Find the label field for this list
1750
2131
  const labelFieldId = f.group_label_field_id;
1751
2132
  linkOptionsCache[clKey] = (resp?.response?.items || []).map((item) => {
1752
- const label = labelFieldId ? (item[`cf_${labelFieldId}`] || `#${item.id}`) : `#${item.id}`;
2133
+ const label = labelFieldId ? item[`cf_${labelFieldId}`] || `#${item.id}` : `#${item.id}`;
1753
2134
  return { name: label, value: item.id };
1754
2135
  });
1755
2136
  }
@@ -1761,7 +2142,7 @@ class Flowlu {
1761
2142
  }
1762
2143
  }
1763
2144
  const fields = filteredFields.map((f) => {
1764
- const cfKey = (f.api_use_alias && f.alias) ? `cf_${f.alias}` : `cf_${f.id}`;
2145
+ const cfKey = f.api_use_alias && f.alias ? `cf_${f.alias}` : `cf_${f.id}`;
1765
2146
  let displayName = f.name || cfKey;
1766
2147
  let fieldType = 'string';
1767
2148
  let fieldOptions;
@@ -1783,7 +2164,9 @@ class Flowlu {
1783
2164
  .filter((o) => o.value)
1784
2165
  .map((o) => ({ name: o.value, value: o.id?.toString() || o.value }));
1785
2166
  }
1786
- catch { /* ignore parse errors */ }
2167
+ catch {
2168
+ /* ignore parse errors */
2169
+ }
1787
2170
  }
1788
2171
  // Link fields - use dropdown with fetched options
1789
2172
  if (f.type === 'model.any' && f.module && f.model) {
@@ -1792,12 +2175,12 @@ class Flowlu {
1792
2175
  displayName = `${f.name} → ${linkLabels[key]}`;
1793
2176
  }
1794
2177
  else if (f.module === 'customlists' && f.model === 'items' && f.group_id) {
1795
- const targetList = allFieldsets.find((fs) => fs.module === 'customlists' && fs.model === 'items' && fs.group_id?.toString() === f.group_id.toString());
2178
+ const targetList = allFieldsets.find((fs) => fs.module === 'customlists' &&
2179
+ fs.model === 'items' &&
2180
+ fs.group_id?.toString() === f.group_id.toString());
1796
2181
  displayName = `${f.name} → ${targetList?.name || `List #${f.group_id}`}`;
1797
2182
  }
1798
- const cacheKey = (f.module === 'customlists' && f.model === 'items' && f.group_id)
1799
- ? `customlists/${f.group_id}`
1800
- : key;
2183
+ const cacheKey = f.module === 'customlists' && f.model === 'items' && f.group_id ? `customlists/${f.group_id}` : key;
1801
2184
  if (linkOptionsCache[cacheKey]?.length) {
1802
2185
  fieldType = 'options';
1803
2186
  fieldOptions = linkOptionsCache[cacheKey];
@@ -1855,15 +2238,32 @@ class Flowlu {
1855
2238
  body.phone = phone;
1856
2239
  const additional = this.getNodeParameter('contactAdditionalFields', i);
1857
2240
  const contactSimpleFields = [
1858
- 'middle_name', 'description', 'owner_id', 'web',
1859
- 'phone2', 'phone3', 'email_personal',
1860
- 'social_network_link_1', 'social_network_link_2',
1861
- 'social_network_link_3', 'social_network_link_4',
1862
- 'social_network_link_5', 'social_network_link_6',
1863
- 'address', 'shipping_address_line_1', 'shipping_city',
1864
- 'shipping_state', 'shipping_zip', 'shipping_country',
1865
- 'billing_address_line_1', 'billing_city', 'billing_state',
1866
- 'billing_zip', 'billing_country', 'VAT', 'timezone',
2241
+ 'middle_name',
2242
+ 'description',
2243
+ 'owner_id',
2244
+ 'web',
2245
+ 'phone2',
2246
+ 'phone3',
2247
+ 'email_personal',
2248
+ 'social_network_link_1',
2249
+ 'social_network_link_2',
2250
+ 'social_network_link_3',
2251
+ 'social_network_link_4',
2252
+ 'social_network_link_5',
2253
+ 'social_network_link_6',
2254
+ 'address',
2255
+ 'shipping_address_line_1',
2256
+ 'shipping_city',
2257
+ 'shipping_state',
2258
+ 'shipping_zip',
2259
+ 'shipping_country',
2260
+ 'billing_address_line_1',
2261
+ 'billing_city',
2262
+ 'billing_state',
2263
+ 'billing_zip',
2264
+ 'billing_country',
2265
+ 'VAT',
2266
+ 'timezone',
1867
2267
  ];
1868
2268
  for (const field of contactSimpleFields) {
1869
2269
  if (additional[field] !== undefined && additional[field] !== '') {
@@ -1896,7 +2296,7 @@ class Flowlu {
1896
2296
  if (filters.owner_id)
1897
2297
  qs['filter[owner_id]'] = filters.owner_id;
1898
2298
  responseData = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/account/list', apiKey, undefined, qs);
1899
- for (const item of (responseData?.response?.items || [])) {
2299
+ for (const item of responseData?.response?.items || []) {
1900
2300
  returnData.push({ json: item });
1901
2301
  }
1902
2302
  continue;
@@ -1906,13 +2306,25 @@ class Flowlu {
1906
2306
  const updateFields = this.getNodeParameter('contactUpdateFields', i);
1907
2307
  const body = { id };
1908
2308
  const contactUpdateSimpleFields = [
1909
- 'first_name', 'last_name', 'middle_name', 'email', 'phone',
1910
- 'phone2', 'phone3', 'description', 'owner_id', 'web',
2309
+ 'first_name',
2310
+ 'last_name',
2311
+ 'middle_name',
2312
+ 'email',
2313
+ 'phone',
2314
+ 'phone2',
2315
+ 'phone3',
2316
+ 'description',
2317
+ 'owner_id',
2318
+ 'web',
1911
2319
  'email_personal',
1912
- 'social_network_link_1', 'social_network_link_2',
1913
- 'social_network_link_3', 'social_network_link_4',
1914
- 'social_network_link_5', 'social_network_link_6',
1915
- 'address', 'VAT',
2320
+ 'social_network_link_1',
2321
+ 'social_network_link_2',
2322
+ 'social_network_link_3',
2323
+ 'social_network_link_4',
2324
+ 'social_network_link_5',
2325
+ 'social_network_link_6',
2326
+ 'address',
2327
+ 'VAT',
1916
2328
  ];
1917
2329
  for (const field of contactUpdateSimpleFields) {
1918
2330
  if (updateFields[field] !== undefined && updateFields[field] !== '') {
@@ -1948,9 +2360,16 @@ class Flowlu {
1948
2360
  body.assignee_id = assignee;
1949
2361
  const additional = this.getNodeParameter('opportunityAdditionalFields', i);
1950
2362
  const oppSimpleFields = [
1951
- 'budget', 'description', 'contact_name', 'contact_email',
1952
- 'contact_phone', 'contact_mobile', 'contact_company',
1953
- 'contact_position', 'contact_web', 'source_id',
2363
+ 'budget',
2364
+ 'description',
2365
+ 'contact_name',
2366
+ 'contact_email',
2367
+ 'contact_phone',
2368
+ 'contact_mobile',
2369
+ 'contact_company',
2370
+ 'contact_position',
2371
+ 'contact_web',
2372
+ 'source_id',
1954
2373
  ];
1955
2374
  for (const field of oppSimpleFields) {
1956
2375
  if (additional[field] !== undefined && additional[field] !== '') {
@@ -2008,7 +2427,7 @@ class Flowlu {
2008
2427
  if (filters.active !== undefined)
2009
2428
  qs['filter[active]'] = filters.active.toString();
2010
2429
  responseData = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/lead/list', apiKey, undefined, qs);
2011
- for (const item of (responseData?.response?.items || [])) {
2430
+ for (const item of responseData?.response?.items || []) {
2012
2431
  returnData.push({ json: item });
2013
2432
  }
2014
2433
  continue;
@@ -2018,9 +2437,17 @@ class Flowlu {
2018
2437
  const updateFields = this.getNodeParameter('opportunityUpdateFields', i);
2019
2438
  const body = { id };
2020
2439
  const oppUpdateFields = [
2021
- 'name', 'pipeline_id', 'pipeline_stage_id', 'assignee_id',
2022
- 'budget', 'description', 'active', 'closing_comment',
2023
- 'contact_name', 'contact_email', 'contact_phone',
2440
+ 'name',
2441
+ 'pipeline_id',
2442
+ 'pipeline_stage_id',
2443
+ 'assignee_id',
2444
+ 'budget',
2445
+ 'description',
2446
+ 'active',
2447
+ 'closing_comment',
2448
+ 'contact_name',
2449
+ 'contact_email',
2450
+ 'contact_phone',
2024
2451
  ];
2025
2452
  for (const field of oppUpdateFields) {
2026
2453
  if (updateFields[field] !== undefined && updateFields[field] !== '') {
@@ -2063,9 +2490,16 @@ class Flowlu {
2063
2490
  body.description = desc;
2064
2491
  const additional = this.getNodeParameter('projectAdditionalFields', i);
2065
2492
  const projSimpleFields = [
2066
- 'priority', 'estimated_revenue', 'estimated_expenses',
2067
- 'customer_id', 'customer_crm_contact_id', 'briefcase_id',
2068
- 'stage_id', 'tasks_workflow_id', 'billing_type', 'crm_lead_id',
2493
+ 'priority',
2494
+ 'estimated_revenue',
2495
+ 'estimated_expenses',
2496
+ 'customer_id',
2497
+ 'customer_crm_contact_id',
2498
+ 'briefcase_id',
2499
+ 'stage_id',
2500
+ 'tasks_workflow_id',
2501
+ 'billing_type',
2502
+ 'crm_lead_id',
2069
2503
  ];
2070
2504
  for (const field of projSimpleFields) {
2071
2505
  if (additional[field] !== undefined && additional[field] !== '' && additional[field] !== 0) {
@@ -2101,7 +2535,7 @@ class Flowlu {
2101
2535
  if (!filters.is_archive)
2102
2536
  qs['filter[is_archive]'] = '0';
2103
2537
  responseData = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/st/projects/list', apiKey, undefined, qs);
2104
- for (const item of (responseData?.response?.items || [])) {
2538
+ for (const item of responseData?.response?.items || []) {
2105
2539
  returnData.push({ json: item });
2106
2540
  }
2107
2541
  continue;
@@ -2111,8 +2545,13 @@ class Flowlu {
2111
2545
  const updateFields = this.getNodeParameter('projectUpdateFields', i);
2112
2546
  const body = { id };
2113
2547
  const projUpdateFields = [
2114
- 'name', 'description', 'manager_id', 'priority',
2115
- 'estimated_revenue', 'estimated_expenses', 'stage_id',
2548
+ 'name',
2549
+ 'description',
2550
+ 'manager_id',
2551
+ 'priority',
2552
+ 'estimated_revenue',
2553
+ 'estimated_expenses',
2554
+ 'stage_id',
2116
2555
  ];
2117
2556
  for (const field of projUpdateFields) {
2118
2557
  if (updateFields[field] !== undefined && updateFields[field] !== '') {
@@ -2165,7 +2604,7 @@ class Flowlu {
2165
2604
  if (filters.search)
2166
2605
  qs['search'] = filters.search;
2167
2606
  responseData = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customlists/items/list', apiKey, undefined, qs);
2168
- for (const item of (responseData?.response?.items || [])) {
2607
+ for (const item of responseData?.response?.items || []) {
2169
2608
  returnData.push({ json: item });
2170
2609
  }
2171
2610
  continue;
@@ -2264,7 +2703,7 @@ class Flowlu {
2264
2703
  if (filters.status !== undefined)
2265
2704
  qs['filter[status]'] = filters.status.toString();
2266
2705
  responseData = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/task/tasks/list', apiKey, undefined, qs);
2267
- for (const item of (responseData?.response?.items || [])) {
2706
+ for (const item of responseData?.response?.items || []) {
2268
2707
  returnData.push({ json: item });
2269
2708
  }
2270
2709
  continue;
@@ -2335,14 +2774,14 @@ class Flowlu {
2335
2774
  const entityType = this.getNodeParameter('tagEntityType', i);
2336
2775
  const entityId = this.getNodeParameter('tagEntityId', i);
2337
2776
  responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/${entityType}/${entityId}/global_tags/list`, apiKey);
2338
- for (const item of (responseData?.response?.items || [])) {
2777
+ for (const item of responseData?.response?.items || []) {
2339
2778
  returnData.push({ json: item });
2340
2779
  }
2341
2780
  continue;
2342
2781
  }
2343
2782
  else if (operation === 'listAll') {
2344
2783
  responseData = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/core/tag/list', apiKey);
2345
- for (const item of (responseData?.response?.items || [])) {
2784
+ for (const item of responseData?.response?.items || []) {
2346
2785
  returnData.push({ json: item });
2347
2786
  }
2348
2787
  continue;
@@ -2373,7 +2812,7 @@ class Flowlu {
2373
2812
  const entityId = this.getNodeParameter('commentEntityId', i);
2374
2813
  const limit = this.getNodeParameter('limit', i);
2375
2814
  responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/${entityType}/${entityId}/comments/list`, apiKey, undefined, { limit: limit.toString() });
2376
- for (const item of (responseData?.response?.items || [])) {
2815
+ for (const item of responseData?.response?.items || []) {
2377
2816
  returnData.push({ json: item });
2378
2817
  }
2379
2818
  continue;