n8n-nodes-confirm8 0.44.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,9 @@
1
- import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription, IDataObject } from 'n8n-workflow';
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
2
  export declare class Confirm8AgentTool implements INodeType {
3
3
  description: INodeTypeDescription;
4
4
  /**
5
- * Tool description for AI Agent
6
- * This helps the AI understand when and how to use this tool
5
+ * Parse natural date queries into filter JSON
7
6
  */
8
- getToolDescription(): IDataObject;
7
+ private parseDateQuery;
9
8
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
10
9
  }
@@ -10,11 +10,10 @@ class Confirm8AgentTool {
10
10
  icon: 'file:tool.svg',
11
11
  group: ['transform'],
12
12
  version: 1,
13
- description: 'AI Agent tool for Confirm8 API - Use to fetch, create, update data from Confirm8',
13
+ description: 'AI tool for Confirm8. IMPORTANT: For "OS/ordem de serviço" use resource="order". For dates use filters with start_date/end_date. Example: {"start_date":{"gte":"2025-11-23"},"end_date":{"lte":"2025-11-25"}}',
14
14
  defaults: {
15
15
  name: 'Confirm8 AI Tool',
16
16
  },
17
- // IMPORTANT: Makes this node usable as an AI Agent Tool
18
17
  usableAsTool: true,
19
18
  inputs: ['main'],
20
19
  outputs: ['main'],
@@ -27,7 +26,6 @@ class Confirm8AgentTool {
27
26
  default: '',
28
27
  placeholder: 'https://api.confirm8.com',
29
28
  required: true,
30
- description: 'Base URL of Confirm8 API',
31
29
  },
32
30
  {
33
31
  displayName: 'Bearer Token',
@@ -36,7 +34,6 @@ class Confirm8AgentTool {
36
34
  typeOptions: { password: true },
37
35
  default: '',
38
36
  required: true,
39
- description: 'Authentication token',
40
37
  },
41
38
  {
42
39
  displayName: 'X-API-DOMAIN',
@@ -53,247 +50,379 @@ class Confirm8AgentTool {
53
50
  default: '',
54
51
  required: true,
55
52
  },
56
- // Operation Configuration
53
+ // Tool parameters
57
54
  {
58
55
  displayName: 'Resource',
59
56
  name: 'resource',
60
57
  type: 'options',
61
- noDataExpression: true,
62
58
  options: [
63
- { name: 'User', value: 'user', description: 'Operations with users/employees' },
64
- { name: 'Client', value: 'client', description: 'Operations with clients/customers' },
65
- { name: 'Item', value: 'item', description: 'Operations with items' },
66
- { name: 'Task', value: 'task', description: 'Operations with tasks/checklists' },
67
- { name: 'Service', value: 'service', description: 'Operations with services' },
68
- { name: 'Product', value: 'product', description: 'Operations with products' },
69
- { name: 'Order', value: 'order', description: 'Operations with work orders' },
70
- { name: 'Ticket', value: 'ticket', description: 'Operations with tickets' },
71
- { name: 'Property', value: 'property', description: 'Operations with properties' },
59
+ { name: 'User', value: 'user', description: 'Users/Employees/Funcionários' },
60
+ { name: 'Client', value: 'client', description: 'Clients/Customers/Clientes' },
61
+ { name: 'Item', value: 'item', description: 'Items/Itens' },
62
+ { name: 'Item Type', value: 'itemType', description: 'Item Types/Tipos' },
63
+ { name: 'Task', value: 'task', description: 'Tasks/Tarefas/Checklists' },
64
+ { name: 'Service', value: 'service', description: 'Services/Serviços' },
65
+ { name: 'Product', value: 'product', description: 'Products/Produtos' },
66
+ { name: 'Order (OS)', value: 'order', description: 'Work Orders/OS/Ordens de Serviço/WOS' },
67
+ { name: 'Modality', value: 'modality', description: 'Modalities/Modalidades' },
68
+ { name: 'Ticket', value: 'ticket', description: 'Tickets/Chamados' },
69
+ { name: 'Property', value: 'property', description: 'Properties/Propriedades' },
72
70
  ],
73
71
  default: 'user',
74
- description: 'Type of resource to interact with',
72
+ description: 'CRITICAL: For "OS" or "ordem de serviço" ALWAYS use "order"',
75
73
  },
76
74
  {
77
75
  displayName: 'Operation',
78
76
  name: 'operation',
79
77
  type: 'options',
80
- noDataExpression: true,
81
78
  options: [
82
- {
83
- name: 'Get',
84
- value: 'get',
85
- description: 'Fetch a single record by ID'
86
- },
87
- {
88
- name: 'Get All',
89
- value: 'getAll',
90
- description: 'Fetch all records or list records'
91
- },
92
- {
93
- name: 'Create',
94
- value: 'create',
95
- description: 'Create a new record'
96
- },
97
- {
98
- name: 'Update',
99
- value: 'update',
100
- description: 'Update an existing record'
101
- },
102
- {
103
- name: 'Activate',
104
- value: 'activate',
105
- description: 'Activate/enable a record'
106
- },
107
- {
108
- name: 'Deactivate',
109
- value: 'deactivate',
110
- description: 'Deactivate/disable a record'
111
- },
79
+ { name: 'Get All (List)', value: 'getAll', description: 'List/Listar/Buscar todos/Mostrar todos' },
80
+ { name: 'Get (Single)', value: 'get', description: 'Get one/Buscar um/Obter' },
81
+ { name: 'Create', value: 'create', description: 'Create/Criar/Adicionar' },
82
+ { name: 'Update', value: 'update', description: 'Update/Atualizar/Modificar' },
83
+ { name: 'Activate', value: 'activate', description: 'Activate/Ativar' },
84
+ { name: 'Deactivate', value: 'deactivate', description: 'Deactivate/Desativar' },
112
85
  ],
113
86
  default: 'getAll',
114
- description: 'Operation to perform',
87
+ description: 'CRITICAL: To list/search use "getAll"',
115
88
  },
116
89
  {
117
90
  displayName: 'Record ID',
118
91
  name: 'recordId',
119
92
  type: 'string',
120
93
  default: '',
121
- displayOptions: {
122
- show: {
123
- operation: ['get', 'update', 'activate', 'deactivate'],
124
- },
125
- },
126
- description: 'ID of the record to fetch or modify',
94
+ description: 'Record ID (only for get/update/activate/deactivate)',
127
95
  },
128
96
  {
129
- displayName: 'Data (JSON)',
130
- name: 'dataJson',
131
- type: 'json',
132
- default: '{}',
133
- displayOptions: {
134
- show: {
135
- operation: ['create', 'update'],
136
- },
137
- },
138
- description: 'Data to send in the request body as JSON',
97
+ displayName: 'Data',
98
+ name: 'data',
99
+ type: 'string',
100
+ default: '',
101
+ description: 'JSON data (only for create/update)',
102
+ },
103
+ {
104
+ displayName: 'Filters',
105
+ name: 'filters',
106
+ type: 'string',
107
+ default: '',
108
+ description: 'Filters as JSON. For dates: {"start_date":{"gte":"YYYY-MM-DD"},"end_date":{"lte":"YYYY-MM-DD"}}. For status: {"status":{"eq":"complete"}}',
139
109
  },
140
110
  ],
141
111
  };
142
112
  }
143
113
  /**
144
- * Tool description for AI Agent
145
- * This helps the AI understand when and how to use this tool
114
+ * Parse natural date queries into filter JSON
146
115
  */
147
- getToolDescription() {
148
- return {
149
- name: 'confirm8_api',
150
- description: 'Use this tool to interact with the Confirm8 API. ' +
151
- 'It can fetch, create, update, activate, or deactivate records in the Confirm8 system. ' +
152
- 'Available resources: users, clients, items, tasks, services, products, orders, tickets, properties. ' +
153
- 'Use "Get All" to list records, "Get" to fetch a specific record by ID, ' +
154
- '"Create" to add new records, "Update" to modify existing records, ' +
155
- '"Activate/Deactivate" to enable or disable records.',
156
- properties: [
157
- {
158
- displayName: 'Resource',
159
- name: 'resource',
160
- type: 'options',
161
- required: true,
162
- options: [
163
- { name: 'User', value: 'user' },
164
- { name: 'Client', value: 'client' },
165
- { name: 'Item', value: 'item' },
166
- { name: 'Task', value: 'task' },
167
- { name: 'Service', value: 'service' },
168
- { name: 'Product', value: 'product' },
169
- { name: 'Order', value: 'order' },
170
- { name: 'Ticket', value: 'ticket' },
171
- { name: 'Property', value: 'property' },
172
- ],
173
- description: 'Type of resource: user, client, item, task, service, product, order, ticket, or property',
174
- },
175
- {
176
- displayName: 'Operation',
177
- name: 'operation',
178
- type: 'options',
179
- required: true,
180
- options: [
181
- { name: 'Get', value: 'get' },
182
- { name: 'Get All', value: 'getAll' },
183
- { name: 'Create', value: 'create' },
184
- { name: 'Update', value: 'update' },
185
- { name: 'Activate', value: 'activate' },
186
- { name: 'Deactivate', value: 'deactivate' },
187
- ],
188
- description: 'Operation: get (single), getAll (list), create, update, activate, deactivate',
189
- },
190
- {
191
- displayName: 'Record ID',
192
- name: 'recordId',
193
- type: 'string',
194
- required: false,
195
- description: 'ID of the record (required for get, update, activate, deactivate)',
196
- },
197
- {
198
- displayName: 'Data',
199
- name: 'dataJson',
200
- type: 'json',
201
- required: false,
202
- description: 'JSON data for create/update operations. Example: {"name":"John","email":"john@example.com"}',
203
- },
204
- ],
116
+ parseDateQuery(query) {
117
+ const now = new Date();
118
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
119
+ query = query.toLowerCase().trim();
120
+ // Helper to format date as YYYY-MM-DD
121
+ const formatDate = (date) => {
122
+ return date.toISOString().split('T')[0];
205
123
  };
124
+ // Today
125
+ if (query.includes('hoje') || query.includes('today')) {
126
+ const endOfDay = new Date(today);
127
+ endOfDay.setHours(23, 59, 59, 999);
128
+ return {
129
+ start_date: { gte: formatDate(today) },
130
+ end_date: { lte: formatDate(endOfDay) }
131
+ };
132
+ }
133
+ // This week / semana atual
134
+ if (query.includes('semana') || query.includes('week')) {
135
+ const dayOfWeek = today.getDay();
136
+ const diff = dayOfWeek === 0 ? -6 : 1 - dayOfWeek; // Monday is start
137
+ const weekStart = new Date(today);
138
+ weekStart.setDate(today.getDate() + diff);
139
+ const weekEnd = new Date(weekStart);
140
+ weekEnd.setDate(weekStart.getDate() + 6);
141
+ return {
142
+ start_date: { gte: formatDate(weekStart) },
143
+ end_date: { lte: formatDate(weekEnd) }
144
+ };
145
+ }
146
+ // This month / mês atual
147
+ if (query.includes('mês') || query.includes('mes') || query.includes('month')) {
148
+ const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
149
+ const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);
150
+ return {
151
+ start_date: { gte: formatDate(monthStart) },
152
+ end_date: { lte: formatDate(monthEnd) }
153
+ };
154
+ }
155
+ // Last N days
156
+ const lastDaysMatch = query.match(/últimos?\s+(\d+)\s+dias?|last\s+(\d+)\s+days?/);
157
+ if (lastDaysMatch) {
158
+ const days = parseInt(lastDaysMatch[1] || lastDaysMatch[2]);
159
+ const startDate = new Date(today);
160
+ startDate.setDate(today.getDate() - days);
161
+ return {
162
+ start_date: { gte: formatDate(startDate) },
163
+ end_date: { lte: formatDate(today) }
164
+ };
165
+ }
166
+ // Next N days
167
+ const nextDaysMatch = query.match(/próximos?\s+(\d+)\s+dias?|next\s+(\d+)\s+days?/);
168
+ if (nextDaysMatch) {
169
+ const days = parseInt(nextDaysMatch[1] || nextDaysMatch[2]);
170
+ const endDate = new Date(today);
171
+ endDate.setDate(today.getDate() + days);
172
+ return {
173
+ start_date: { gte: formatDate(today) },
174
+ end_date: { lte: formatDate(endDate) }
175
+ };
176
+ }
177
+ return null;
206
178
  }
207
179
  async execute() {
208
180
  const items = this.getInputData();
209
181
  const returnData = [];
210
182
  for (let i = 0; i < items.length; i++) {
211
183
  try {
212
- // Get API configuration
213
184
  const baseUrl = this.getNodeParameter('baseUrl', i);
214
185
  const bearerToken = this.getNodeParameter('bearerToken', i);
215
186
  const apiDomain = this.getNodeParameter('apiDomain', i);
216
187
  const apiKeyToken = this.getNodeParameter('apiKeyToken', i);
217
- // Get operation parameters
218
- const resource = this.getNodeParameter('resource', i);
219
- const operation = this.getNodeParameter('operation', i);
188
+ let resource = this.getNodeParameter('resource', i, '');
189
+ let operation = this.getNodeParameter('operation', i, '');
220
190
  const recordId = this.getNodeParameter('recordId', i, '');
221
- // Parse data JSON
222
- let dataJson = {};
223
- try {
224
- const dataJsonStr = this.getNodeParameter('dataJson', i, '{}');
225
- if (dataJsonStr && dataJsonStr.trim() !== '{}') {
226
- dataJson = JSON.parse(dataJsonStr);
191
+ // Parse data
192
+ let data = {};
193
+ const dataParam = this.getNodeParameter('data', i, '');
194
+ if (dataParam) {
195
+ try {
196
+ data = JSON.parse(dataParam);
197
+ }
198
+ catch (e) {
199
+ // Ignore
227
200
  }
228
201
  }
229
- catch (e) {
230
- // Data is optional
202
+ // Parse filters
203
+ let filters = {};
204
+ const filtersParam = this.getNodeParameter('filters', i, '');
205
+ if (filtersParam) {
206
+ try {
207
+ // Try to parse as JSON first
208
+ filters = JSON.parse(filtersParam);
209
+ }
210
+ catch (e) {
211
+ // If not JSON, try to parse as natural language date query
212
+ const parsedDate = this.parseDateQuery(filtersParam);
213
+ if (parsedDate) {
214
+ filters = parsedDate;
215
+ }
216
+ }
231
217
  }
232
- // Map resource to endpoint
218
+ // Aggressive normalization - EXPAND THIS
233
219
  const resourceMap = {
234
- user: 'users',
235
- client: 'clients',
236
- item: 'items',
237
- task: 'tasks',
238
- service: 'services',
239
- product: 'products',
240
- order: 'wos',
241
- ticket: 'tickets',
242
- property: 'properties',
220
+ // Standard
221
+ 'users': 'user', 'usuarios': 'user', 'usuários': 'user', 'employees': 'user', 'funcionários': 'user', 'funcionarios': 'user',
222
+ 'clients': 'client', 'clientes': 'client', 'customers': 'client',
223
+ 'items': 'item', 'itens': 'item',
224
+ 'itemtypes': 'itemType', 'tipos': 'itemType',
225
+ 'tasks': 'task', 'tarefas': 'task',
226
+ 'services': 'service', 'serviços': 'service', 'servicos': 'service',
227
+ 'products': 'product', 'produtos': 'product',
228
+ 'modalities': 'modality', 'modalidades': 'modality',
229
+ 'tickets': 'ticket', 'chamados': 'ticket',
230
+ 'properties': 'property', 'propriedades': 'property',
231
+ // CRITICAL: OS mappings
232
+ 'os': 'order',
233
+ 'o.s': 'order',
234
+ 'o.s.': 'order',
235
+ 'orders': 'order',
236
+ 'ordens': 'order',
237
+ 'ordem': 'order',
238
+ 'ordem de serviço': 'order',
239
+ 'ordem de servico': 'order',
240
+ 'ordens de serviço': 'order',
241
+ 'ordens de servico': 'order',
242
+ 'wos': 'order',
243
+ 'work order': 'order',
244
+ 'work orders': 'order',
245
+ 'pedidos': 'order',
246
+ 'pedido': 'order',
247
+ };
248
+ const operationMap = {
249
+ // List/Get All
250
+ 'list': 'getAll',
251
+ 'listar': 'getAll',
252
+ 'mostrar': 'getAll',
253
+ 'buscar': 'getAll',
254
+ 'busque': 'getAll',
255
+ 'buscar todos': 'getAll',
256
+ 'buscar todas': 'getAll',
257
+ 'buscar todo': 'getAll',
258
+ 'buscar toda': 'getAll',
259
+ 'get all': 'getAll',
260
+ 'show all': 'getAll',
261
+ 'list all': 'getAll',
262
+ 'todos': 'getAll',
263
+ 'todas': 'getAll',
264
+ 'obter todos': 'getAll',
265
+ 'obter todas': 'getAll',
266
+ 'pegar todos': 'getAll',
267
+ 'pegar todas': 'getAll',
268
+ // Get Single
269
+ 'obter': 'get',
270
+ 'pegar': 'get',
271
+ 'ver': 'get',
272
+ 'visualizar': 'get',
273
+ // Create
274
+ 'criar': 'create',
275
+ 'adicionar': 'create',
276
+ 'add': 'create',
277
+ 'novo': 'create',
278
+ 'nova': 'create',
279
+ 'new': 'create',
280
+ // Update
281
+ 'atualizar': 'update',
282
+ 'modificar': 'update',
283
+ 'modify': 'update',
284
+ 'editar': 'update',
285
+ 'edit': 'update',
286
+ // Activate/Deactivate
287
+ 'ativar': 'activate',
288
+ 'enable': 'activate',
289
+ 'habilitar': 'activate',
290
+ 'desativar': 'deactivate',
291
+ 'disable': 'deactivate',
292
+ 'desabilitar': 'deactivate',
293
+ };
294
+ // Normalize - clean and map
295
+ const cleanResource = resource.toLowerCase().trim().replace(/\s+/g, ' ');
296
+ const cleanOperation = operation.toLowerCase().trim().replace(/\s+/g, ' ');
297
+ resource = resourceMap[cleanResource] || resource;
298
+ operation = operationMap[cleanOperation] || operation;
299
+ // If still not valid, throw clear error
300
+ const validResources = ['user', 'client', 'item', 'itemType', 'task', 'service', 'product', 'order', 'modality', 'ticket', 'property'];
301
+ const validOperations = ['getAll', 'get', 'create', 'update', 'activate', 'deactivate'];
302
+ if (!validResources.includes(resource)) {
303
+ throw new Error(`Invalid resource: "${resource}". Must be one of: ${validResources.join(', ')}. ` +
304
+ `For OS/Ordem de Serviço use "order".`);
305
+ }
306
+ if (!validOperations.includes(operation)) {
307
+ throw new Error(`Invalid operation: "${operation}". Must be one of: ${validOperations.join(', ')}. ` +
308
+ `To list/search use "getAll".`);
309
+ }
310
+ // Resource config with relations
311
+ const resourceConfig = {
312
+ user: {
313
+ endpoint: 'users',
314
+ relations: ['clients', 'attachments', 'permissions', 'device', 'employee']
315
+ },
316
+ client: {
317
+ endpoint: 'clients',
318
+ relations: ['wos', 'items', 'employees', 'headquarter', 'files', 'userGroup', 'properties']
319
+ },
320
+ item: {
321
+ endpoint: 'items',
322
+ relations: ['client', 'item_type', 'properties', 'parent', 'children', 'collects', 'wos']
323
+ },
324
+ itemType: {
325
+ endpoint: 'itemTypes',
326
+ relations: ['properties']
327
+ },
328
+ task: {
329
+ endpoint: 'tasks',
330
+ relations: ['itemType', 'activities', 'wos', 'modalities']
331
+ },
332
+ service: {
333
+ endpoint: 'services',
334
+ relations: ['task']
335
+ },
336
+ product: {
337
+ endpoint: 'products',
338
+ relations: []
339
+ },
340
+ order: {
341
+ endpoint: 'wos',
342
+ relations: ['client', 'modalities', 'tasks', 'pivot_tasks', 'products', 'users', 'items', 'tickets', 'services', 'collects', 'attachments']
343
+ },
344
+ modality: {
345
+ endpoint: 'modalities',
346
+ relations: ['tasks']
347
+ },
348
+ ticket: {
349
+ endpoint: 'tickets',
350
+ relations: ['client', 'subject_category', 'category', 'status', 'attachments', 'item', 'owner', 'priority', 'users', 'orders', 'properties']
351
+ },
352
+ property: {
353
+ endpoint: 'properties',
354
+ relations: []
355
+ },
243
356
  };
244
- const endpoint = resourceMap[resource] || resource;
245
- let url = '';
357
+ const config = resourceConfig[resource];
358
+ const baseEndpoint = config.endpoint;
359
+ let endpoint = '';
246
360
  let method = 'GET';
247
361
  let body = {};
248
- // Build request based on operation
362
+ // Build endpoint
249
363
  switch (operation) {
250
364
  case 'getAll':
251
- url = `${baseUrl}/${endpoint}`;
252
- method = 'GET';
365
+ endpoint = `/v3/${baseEndpoint}`;
253
366
  break;
254
367
  case 'get':
255
- if (!recordId) {
256
- throw new Error('Record ID is required for Get operation');
257
- }
258
- url = `${baseUrl}/${endpoint}/${recordId}`;
259
- method = 'GET';
368
+ if (!recordId)
369
+ throw new Error('recordId required');
370
+ endpoint = `/v3/${baseEndpoint}/${recordId}`;
260
371
  break;
261
372
  case 'create':
262
- url = `${baseUrl}/${endpoint}`;
373
+ endpoint = `/v3/${baseEndpoint}`;
263
374
  method = 'POST';
264
- body = dataJson;
375
+ body = data;
265
376
  break;
266
377
  case 'update':
267
- if (!recordId) {
268
- throw new Error('Record ID is required for Update operation');
269
- }
270
- url = `${baseUrl}/${endpoint}/${recordId}`;
378
+ if (!recordId)
379
+ throw new Error('recordId required');
380
+ endpoint = `/v3/${baseEndpoint}/${recordId}`;
271
381
  method = 'PUT';
272
- body = dataJson;
382
+ body = data;
273
383
  break;
274
384
  case 'activate':
275
- if (!recordId) {
276
- throw new Error('Record ID is required for Activate operation');
277
- }
278
- url = `${baseUrl}/${endpoint}/${recordId}/active`;
279
- method = resource === 'client' || resource === 'item' || resource === 'task' ||
280
- resource === 'service' || resource === 'product' || resource === 'order' ? 'PUT' : 'PATCH';
385
+ if (!recordId)
386
+ throw new Error('recordId required');
387
+ endpoint = `/v3/${baseEndpoint}/${recordId}/active`;
388
+ method = ['client', 'item', 'itemType', 'task', 'service', 'product', 'order', 'modality'].includes(resource)
389
+ ? 'PUT' : 'PATCH';
281
390
  break;
282
391
  case 'deactivate':
283
- if (!recordId) {
284
- throw new Error('Record ID is required for Deactivate operation');
285
- }
286
- url = `${baseUrl}/${endpoint}/${recordId}/inactive`;
287
- method = resource === 'client' || resource === 'item' || resource === 'task' ||
288
- resource === 'service' || resource === 'product' || resource === 'order' ? 'PUT' : 'PATCH';
392
+ if (!recordId)
393
+ throw new Error('recordId required');
394
+ endpoint = `/v3/${baseEndpoint}/${recordId}/inactive`;
395
+ method = ['client', 'item', 'itemType', 'task', 'service', 'product', 'order', 'modality'].includes(resource)
396
+ ? 'PUT' : 'PATCH';
289
397
  break;
290
- default:
291
- throw new Error(`Unknown operation: ${operation}`);
292
398
  }
293
- // Make HTTP request
399
+ // Build query string
400
+ const queryParams = [];
401
+ // Add relations (only GET)
402
+ if (method === 'GET' && config.relations.length > 0) {
403
+ config.relations.forEach(relation => {
404
+ queryParams.push(`relations=${relation}`);
405
+ });
406
+ }
407
+ // Add filters
408
+ if (Object.keys(filters).length > 0) {
409
+ Object.keys(filters).forEach(field => {
410
+ const operators = filters[field];
411
+ Object.keys(operators).forEach(operator => {
412
+ const value = operators[operator];
413
+ queryParams.push(`filters[${field}][${operator}]=${encodeURIComponent(String(value))}`);
414
+ });
415
+ });
416
+ }
417
+ // Full URL
418
+ let fullUrl = `${baseUrl}${endpoint}`;
419
+ if (queryParams.length > 0) {
420
+ fullUrl += '?' + queryParams.join('&');
421
+ }
422
+ // Make request
294
423
  const options = {
295
424
  method,
296
- uri: url,
425
+ uri: fullUrl,
297
426
  headers: {
298
427
  'Authorization': `Bearer ${bearerToken}`,
299
428
  'X-API-DOMAIN': apiDomain,
@@ -309,8 +438,19 @@ class Confirm8AgentTool {
309
438
  returnData.push({
310
439
  json: {
311
440
  success: true,
441
+ normalized: {
442
+ originalResource: this.getNodeParameter('resource', i, ''),
443
+ originalOperation: this.getNodeParameter('operation', i, ''),
444
+ normalizedResource: resource,
445
+ normalizedOperation: operation,
446
+ },
312
447
  operation,
313
448
  resource,
449
+ endpoint,
450
+ fullUrl,
451
+ relations: config.relations,
452
+ filters: filters,
453
+ method,
314
454
  data: responseData,
315
455
  },
316
456
  pairedItem: { item: i },
@@ -321,7 +461,7 @@ class Confirm8AgentTool {
321
461
  returnData.push({
322
462
  json: {
323
463
  success: false,
324
- error: error || 'Unknown error',
464
+ error: error.message || 'Unknown error',
325
465
  },
326
466
  pairedItem: { item: i },
327
467
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-confirm8",
3
- "version": "0.44.0",
3
+ "version": "0.46.0",
4
4
  "description": "Simple n8n node for Confirm8 API - no credentials needed",
5
5
  "license": "MIT",
6
6
  "author": "Bill Hebert",
@@ -21,7 +21,8 @@
21
21
  },
22
22
  "n8n": {
23
23
  "nodes": [
24
- "dist/nodes/Confirm8/ApiConfirm8.node.js"
24
+ "dist/nodes/Confirm8/ApiConfirm8.node.js",
25
+ "dist/nodes/Confirm8Tool/Confirm8AgentTool.node.js"
25
26
  ]
26
27
  },
27
28
  "peerDependencies": {