n8n-nodes-clientify 0.2.3 → 0.2.5

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.
Files changed (185) hide show
  1. package/README.md +34 -504
  2. package/dist/README.md +34 -504
  3. package/dist/appmixer/clientify/crm/AddCompanyAddress/AddCompanyAddress.js +33 -0
  4. package/dist/appmixer/clientify/crm/AddCompanyAddress/component.json +115 -0
  5. package/dist/appmixer/clientify/crm/AddCompanyCall/AddCompanyCall.js +32 -0
  6. package/dist/appmixer/clientify/crm/AddCompanyCall/component.json +109 -0
  7. package/dist/appmixer/clientify/crm/AddCompanyCheckin/AddCompanyCheckin.js +31 -0
  8. package/dist/appmixer/clientify/crm/AddCompanyCheckin/component.json +98 -0
  9. package/dist/appmixer/clientify/crm/AddCompanyEmail/AddCompanyEmail.js +25 -0
  10. package/dist/appmixer/clientify/crm/AddCompanyEmail/component.json +84 -0
  11. package/dist/appmixer/clientify/crm/AddCompanyEmployee/AddCompanyEmployee.js +22 -0
  12. package/dist/appmixer/clientify/crm/AddCompanyEmployee/component.json +65 -0
  13. package/dist/appmixer/clientify/crm/AddCompanyNote/AddCompanyNote.js +25 -0
  14. package/dist/appmixer/clientify/crm/AddCompanyNote/component.json +75 -0
  15. package/dist/appmixer/clientify/crm/AddCompanyPhone/AddCompanyPhone.js +25 -0
  16. package/dist/appmixer/clientify/crm/AddCompanyPhone/component.json +87 -0
  17. package/dist/appmixer/clientify/crm/AddCompanyTag/AddCompanyTag.js +23 -0
  18. package/dist/appmixer/clientify/crm/AddCompanyTag/component.json +71 -0
  19. package/dist/appmixer/clientify/crm/AddCompanyWebsite/AddCompanyWebsite.js +23 -0
  20. package/dist/appmixer/clientify/crm/AddCompanyWebsite/component.json +65 -0
  21. package/dist/appmixer/clientify/crm/AddContactAddress/AddContactAddress.js +33 -0
  22. package/dist/appmixer/clientify/crm/AddContactAddress/component.json +115 -0
  23. package/dist/appmixer/clientify/crm/AddContactCall/AddContactCall.js +20 -0
  24. package/dist/appmixer/clientify/crm/AddContactCall/component.json +55 -0
  25. package/dist/appmixer/clientify/crm/AddContactCheckin/AddContactCheckin.js +33 -0
  26. package/dist/appmixer/clientify/crm/AddContactCheckin/component.json +109 -0
  27. package/dist/appmixer/clientify/crm/AddContactEmail/AddContactEmail.js +25 -0
  28. package/dist/appmixer/clientify/crm/AddContactEmail/component.json +85 -0
  29. package/dist/appmixer/clientify/crm/AddContactNote/AddContactNote.js +25 -0
  30. package/dist/appmixer/clientify/crm/AddContactNote/component.json +75 -0
  31. package/dist/appmixer/clientify/crm/AddContactOtherCompany/AddContactOtherCompany.js +23 -0
  32. package/dist/appmixer/clientify/crm/AddContactOtherCompany/component.json +65 -0
  33. package/dist/appmixer/clientify/crm/AddContactPhone/AddContactPhone.js +25 -0
  34. package/dist/appmixer/clientify/crm/AddContactPhone/component.json +88 -0
  35. package/dist/appmixer/clientify/crm/AddContactTag/AddContactTag.js +23 -0
  36. package/dist/appmixer/clientify/crm/AddContactTag/component.json +71 -0
  37. package/dist/appmixer/clientify/crm/AssignContactOwner/AssignContactOwner.js +23 -0
  38. package/dist/appmixer/clientify/crm/AssignContactOwner/component.json +93 -0
  39. package/dist/appmixer/clientify/crm/BatchCreateContacts/BatchCreateContacts.js +23 -0
  40. package/dist/appmixer/clientify/crm/BatchCreateContacts/component.json +90 -0
  41. package/dist/appmixer/clientify/crm/CompleteTask/CompleteTask.js +24 -0
  42. package/dist/appmixer/clientify/crm/CompleteTask/component.json +69 -0
  43. package/dist/appmixer/clientify/crm/CreateCompany/CreateCompany.js +19 -0
  44. package/dist/appmixer/clientify/crm/CreateCompany/component.json +58 -0
  45. package/dist/appmixer/clientify/crm/CreateContact/CreateContact.js +19 -0
  46. package/dist/appmixer/clientify/crm/CreateContact/component.json +88 -0
  47. package/dist/appmixer/clientify/crm/CreateTask/CreateTask.js +19 -0
  48. package/dist/appmixer/clientify/crm/CreateTask/component.json +167 -0
  49. package/dist/appmixer/clientify/crm/DeleteCompany/DeleteCompany.js +22 -0
  50. package/dist/appmixer/clientify/crm/DeleteCompany/component.json +51 -0
  51. package/dist/appmixer/clientify/crm/DeleteCompanyAddress/DeleteCompanyAddress.js +22 -0
  52. package/dist/appmixer/clientify/crm/DeleteCompanyAddress/component.json +62 -0
  53. package/dist/appmixer/clientify/crm/DeleteCompanyEmail/DeleteCompanyEmail.js +22 -0
  54. package/dist/appmixer/clientify/crm/DeleteCompanyEmail/component.json +62 -0
  55. package/dist/appmixer/clientify/crm/DeleteCompanyPhone/DeleteCompanyPhone.js +22 -0
  56. package/dist/appmixer/clientify/crm/DeleteCompanyPhone/component.json +62 -0
  57. package/dist/appmixer/clientify/crm/DeleteCompanyWebsite/DeleteCompanyWebsite.js +22 -0
  58. package/dist/appmixer/clientify/crm/DeleteCompanyWebsite/component.json +62 -0
  59. package/dist/appmixer/clientify/crm/DeleteContact/DeleteContact.js +22 -0
  60. package/dist/appmixer/clientify/crm/DeleteContact/component.json +51 -0
  61. package/dist/appmixer/clientify/crm/DeleteContactAddress/DeleteContactAddress.js +22 -0
  62. package/dist/appmixer/clientify/crm/DeleteContactAddress/component.json +62 -0
  63. package/dist/appmixer/clientify/crm/DeleteContactEmail/DeleteContactEmail.js +22 -0
  64. package/dist/appmixer/clientify/crm/DeleteContactEmail/component.json +62 -0
  65. package/dist/appmixer/clientify/crm/DeleteContactOtherCompany/DeleteContactOtherCompany.js +22 -0
  66. package/dist/appmixer/clientify/crm/DeleteContactOtherCompany/component.json +62 -0
  67. package/dist/appmixer/clientify/crm/DeleteContactPhone/DeleteContactPhone.js +22 -0
  68. package/dist/appmixer/clientify/crm/DeleteContactPhone/component.json +62 -0
  69. package/dist/appmixer/clientify/crm/GetCompany/GetCompany.js +24 -0
  70. package/dist/appmixer/clientify/crm/GetCompany/component.json +69 -0
  71. package/dist/appmixer/clientify/crm/GetContact/GetContact.js +24 -0
  72. package/dist/appmixer/clientify/crm/GetContact/component.json +90 -0
  73. package/dist/appmixer/clientify/crm/GetCurrentUser/GetCurrentUser.js +17 -0
  74. package/dist/appmixer/clientify/crm/GetCurrentUser/component.json +49 -0
  75. package/dist/appmixer/clientify/crm/GetTask/GetTask.js +24 -0
  76. package/dist/appmixer/clientify/crm/GetTask/component.json +78 -0
  77. package/dist/appmixer/clientify/crm/ListAllCompanyTags/ListAllCompanyTags.js +16 -0
  78. package/dist/appmixer/clientify/crm/ListAllCompanyTags/component.json +67 -0
  79. package/dist/appmixer/clientify/crm/ListAllContactTags/ListAllContactTags.js +16 -0
  80. package/dist/appmixer/clientify/crm/ListAllContactTags/component.json +67 -0
  81. package/dist/appmixer/clientify/crm/ListCompanies/ListCompanies.js +26 -0
  82. package/dist/appmixer/clientify/crm/ListCompanies/component.json +117 -0
  83. package/dist/appmixer/clientify/crm/ListCompanyAddresses/ListCompanyAddresses.js +20 -0
  84. package/dist/appmixer/clientify/crm/ListCompanyAddresses/component.json +79 -0
  85. package/dist/appmixer/clientify/crm/ListCompanyCustomfields/ListCompanyCustomfields.js +20 -0
  86. package/dist/appmixer/clientify/crm/ListCompanyCustomfields/component.json +79 -0
  87. package/dist/appmixer/clientify/crm/ListCompanyEmails/ListCompanyEmails.js +20 -0
  88. package/dist/appmixer/clientify/crm/ListCompanyEmails/component.json +88 -0
  89. package/dist/appmixer/clientify/crm/ListCompanyEmployees/ListCompanyEmployees.js +20 -0
  90. package/dist/appmixer/clientify/crm/ListCompanyEmployees/component.json +79 -0
  91. package/dist/appmixer/clientify/crm/ListCompanyPhones/ListCompanyPhones.js +20 -0
  92. package/dist/appmixer/clientify/crm/ListCompanyPhones/component.json +91 -0
  93. package/dist/appmixer/clientify/crm/ListCompanySectors/ListCompanySectors.js +16 -0
  94. package/dist/appmixer/clientify/crm/ListCompanySectors/component.json +64 -0
  95. package/dist/appmixer/clientify/crm/ListCompanyTags/ListCompanyTags.js +20 -0
  96. package/dist/appmixer/clientify/crm/ListCompanyTags/component.json +85 -0
  97. package/dist/appmixer/clientify/crm/ListCompanyTasks/ListCompanyTasks.js +20 -0
  98. package/dist/appmixer/clientify/crm/ListCompanyTasks/component.json +79 -0
  99. package/dist/appmixer/clientify/crm/ListCompanyWallentries/ListCompanyWallentries.js +20 -0
  100. package/dist/appmixer/clientify/crm/ListCompanyWallentries/component.json +79 -0
  101. package/dist/appmixer/clientify/crm/ListCompanyWebsites/ListCompanyWebsites.js +20 -0
  102. package/dist/appmixer/clientify/crm/ListCompanyWebsites/component.json +79 -0
  103. package/dist/appmixer/clientify/crm/ListContactAddresses/ListContactAddresses.js +20 -0
  104. package/dist/appmixer/clientify/crm/ListContactAddresses/component.json +79 -0
  105. package/dist/appmixer/clientify/crm/ListContactCustomfields/ListContactCustomfields.js +20 -0
  106. package/dist/appmixer/clientify/crm/ListContactCustomfields/component.json +79 -0
  107. package/dist/appmixer/clientify/crm/ListContactEmails/ListContactEmails.js +20 -0
  108. package/dist/appmixer/clientify/crm/ListContactEmails/component.json +88 -0
  109. package/dist/appmixer/clientify/crm/ListContactOtherCompanies/ListContactOtherCompanies.js +20 -0
  110. package/dist/appmixer/clientify/crm/ListContactOtherCompanies/component.json +85 -0
  111. package/dist/appmixer/clientify/crm/ListContactPhones/ListContactPhones.js +20 -0
  112. package/dist/appmixer/clientify/crm/ListContactPhones/component.json +91 -0
  113. package/dist/appmixer/clientify/crm/ListContactTags/ListContactTags.js +20 -0
  114. package/dist/appmixer/clientify/crm/ListContactTags/component.json +85 -0
  115. package/dist/appmixer/clientify/crm/ListContactTasks/ListContactTasks.js +20 -0
  116. package/dist/appmixer/clientify/crm/ListContactTasks/component.json +79 -0
  117. package/dist/appmixer/clientify/crm/ListContactWallentries/ListContactWallentries.js +20 -0
  118. package/dist/appmixer/clientify/crm/ListContactWallentries/component.json +79 -0
  119. package/dist/appmixer/clientify/crm/ListContacts/ListContacts.js +26 -0
  120. package/dist/appmixer/clientify/crm/ListContacts/component.json +138 -0
  121. package/dist/appmixer/clientify/crm/ListCustomFieldValues/ListCustomFieldValues.js +19 -0
  122. package/dist/appmixer/clientify/crm/ListCustomFieldValues/component.json +77 -0
  123. package/dist/appmixer/clientify/crm/ListCustomFields/ListCustomFields.js +19 -0
  124. package/dist/appmixer/clientify/crm/ListCustomFields/component.json +77 -0
  125. package/dist/appmixer/clientify/crm/ListTaskStages/ListTaskStages.js +19 -0
  126. package/dist/appmixer/clientify/crm/ListTaskStages/component.json +87 -0
  127. package/dist/appmixer/clientify/crm/ListTaskTypes/ListTaskTypes.js +19 -0
  128. package/dist/appmixer/clientify/crm/ListTaskTypes/component.json +87 -0
  129. package/dist/appmixer/clientify/crm/ListTasks/ListTasks.js +25 -0
  130. package/dist/appmixer/clientify/crm/ListTasks/component.json +102 -0
  131. package/dist/appmixer/clientify/crm/ListUsers/ListUsers.js +24 -0
  132. package/dist/appmixer/clientify/crm/ListUsers/component.json +93 -0
  133. package/dist/appmixer/clientify/crm/RemoveCompanyEmployee/RemoveCompanyEmployee.js +22 -0
  134. package/dist/appmixer/clientify/crm/RemoveCompanyEmployee/component.json +62 -0
  135. package/dist/appmixer/clientify/crm/RemoveCompanyTag/RemoveCompanyTag.js +22 -0
  136. package/dist/appmixer/clientify/crm/RemoveCompanyTag/component.json +62 -0
  137. package/dist/appmixer/clientify/crm/RemoveContactTag/RemoveContactTag.js +22 -0
  138. package/dist/appmixer/clientify/crm/RemoveContactTag/component.json +62 -0
  139. package/dist/appmixer/clientify/crm/SearchCompanies/SearchCompanies.js +29 -0
  140. package/dist/appmixer/clientify/crm/SearchCompanies/component.json +111 -0
  141. package/dist/appmixer/clientify/crm/UpdateCompany/UpdateCompany.js +24 -0
  142. package/dist/appmixer/clientify/crm/UpdateCompany/component.json +60 -0
  143. package/dist/appmixer/clientify/crm/UpdateCompanyAddress/UpdateCompanyAddress.js +35 -0
  144. package/dist/appmixer/clientify/crm/UpdateCompanyAddress/component.json +131 -0
  145. package/dist/appmixer/clientify/crm/UpdateCompanyEmail/UpdateCompanyEmail.js +27 -0
  146. package/dist/appmixer/clientify/crm/UpdateCompanyEmail/component.json +91 -0
  147. package/dist/appmixer/clientify/crm/UpdateCompanyPhone/UpdateCompanyPhone.js +27 -0
  148. package/dist/appmixer/clientify/crm/UpdateCompanyPhone/component.json +91 -0
  149. package/dist/appmixer/clientify/crm/UpdateCompanyWebsite/UpdateCompanyWebsite.js +25 -0
  150. package/dist/appmixer/clientify/crm/UpdateCompanyWebsite/component.json +81 -0
  151. package/dist/appmixer/clientify/crm/UpdateContact/UpdateContact.js +24 -0
  152. package/dist/appmixer/clientify/crm/UpdateContact/component.json +81 -0
  153. package/dist/appmixer/clientify/crm/UpdateContactAddress/UpdateContactAddress.js +35 -0
  154. package/dist/appmixer/clientify/crm/UpdateContactAddress/component.json +152 -0
  155. package/dist/appmixer/clientify/crm/UpdateContactEmail/UpdateContactEmail.js +27 -0
  156. package/dist/appmixer/clientify/crm/UpdateContactEmail/component.json +112 -0
  157. package/dist/appmixer/clientify/crm/UpdateContactMarketingStatus/UpdateContactMarketingStatus.js +25 -0
  158. package/dist/appmixer/clientify/crm/UpdateContactMarketingStatus/component.json +92 -0
  159. package/dist/appmixer/clientify/crm/UpdateContactOtherCompany/UpdateContactOtherCompany.js +25 -0
  160. package/dist/appmixer/clientify/crm/UpdateContactOtherCompany/component.json +102 -0
  161. package/dist/appmixer/clientify/crm/UpdateContactPhone/UpdateContactPhone.js +27 -0
  162. package/dist/appmixer/clientify/crm/UpdateContactPhone/component.json +112 -0
  163. package/dist/appmixer/clientify/crm/UpdateTask/UpdateTask.js +24 -0
  164. package/dist/appmixer/clientify/crm/UpdateTask/component.json +69 -0
  165. package/dist/appmixer/clientify/crm/clientify.js +49 -0
  166. package/dist/appmixer/clientify/crm/fields.js +24 -0
  167. package/dist/appmixer/clientify/crm/module.json +7 -0
  168. package/dist/credentials/{ClientifyMcpApi.credentials.d.ts → ClientifyApi.credentials.d.ts} +1 -1
  169. package/dist/credentials/ClientifyApi.credentials.js +33 -0
  170. package/dist/nodes/{ClientifyMcp/ClientifyMcpDynamic.node.d.ts → ClientifyApi/ClientifyApi.node.d.ts} +1 -1
  171. package/dist/nodes/ClientifyApi/ClientifyApi.node.js +139 -0
  172. package/dist/nodes/ClientifyApi/ClientifyApiCatalog.d.ts +23 -0
  173. package/dist/nodes/ClientifyApi/ClientifyApiCatalog.js +217 -0
  174. package/dist/nodes/{ClientifyMcp/ClientifyMcpTrigger.node.d.ts → ClientifyTrigger/ClientifyTrigger.node.d.ts} +1 -1
  175. package/dist/nodes/{ClientifyMcp/ClientifyMcpTrigger.node.js → ClientifyTrigger/ClientifyTrigger.node.js} +6 -14
  176. package/dist/nodes/ClientifyTrigger/clientify.png +0 -0
  177. package/dist/nodes/ClientifyTrigger/clientify.svg +5 -0
  178. package/dist/package.json +13 -6
  179. package/package.json +13 -6
  180. package/dist/credentials/ClientifyMcpApi.credentials.js +0 -25
  181. package/dist/nodes/ClientifyMcp/ClientifyMcpDynamic.node.js +0 -211
  182. package/dist/nodes/ClientifyMcp/ClientifyMcpFields.d.ts +0 -15
  183. package/dist/nodes/ClientifyMcp/ClientifyMcpFields.js +0 -894
  184. /package/dist/nodes/{ClientifyMcp → ClientifyApi}/clientify.png +0 -0
  185. /package/dist/nodes/{ClientifyMcp → ClientifyApi}/clientify.svg +0 -0
@@ -0,0 +1,24 @@
1
+ const { clientifyRequest } = require('../clientify');
2
+
3
+ module.exports = {
4
+ async receive(context) {
5
+ const { apiKey, baseUrl } = context.auth;
6
+ const input = (context.messages.in && context.messages.in.content) || {};
7
+ const taskId = input.taskId;
8
+ if (!taskId) {
9
+ throw new context.CancelError('taskId is required');
10
+ }
11
+
12
+ const { taskId: _ignored, ...body } = input;
13
+ const { data } = await clientifyRequest(context, {
14
+ apiKey,
15
+ baseUrl,
16
+ method: 'PUT',
17
+ path: `/tasks/${taskId}/`,
18
+ data: body,
19
+ });
20
+
21
+ return context.sendJson(data, 'out');
22
+ },
23
+ };
24
+
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "appmixer.clientify.crm.UpdateTask",
3
+ "author": "Clientify",
4
+ "label": "Update Task",
5
+ "description": "Update an existing task.",
6
+ "private": false,
7
+ "version": "0.1.0",
8
+ "auth": {
9
+ "service": "appmixer:clientify"
10
+ },
11
+ "inPorts": [
12
+ {
13
+ "name": "in",
14
+ "schema": {
15
+ "type": "object",
16
+ "properties": {
17
+ "taskId": {
18
+ "type": "number"
19
+ }
20
+ },
21
+ "required": [
22
+ "taskId"
23
+ ]
24
+ },
25
+ "inspector": {
26
+ "inputs": {
27
+ "taskId": {
28
+ "type": "number",
29
+ "label": "Task ID",
30
+ "tooltip": "Set Task ID (can be mapped from previous steps).",
31
+ "index": 1
32
+ }
33
+ }
34
+ }
35
+ }
36
+ ],
37
+ "outPorts": [
38
+ {
39
+ "name": "out",
40
+ "schema": {
41
+ "type": "object",
42
+ "additionalProperties": true,
43
+ "properties": {
44
+ "id": {
45
+ "type": "number"
46
+ },
47
+ "url": {
48
+ "type": "string"
49
+ },
50
+ "name": {
51
+ "type": "string"
52
+ },
53
+ "status": {
54
+ "type": "string"
55
+ },
56
+ "due_date": {
57
+ "type": [
58
+ "string",
59
+ "null"
60
+ ]
61
+ }
62
+ },
63
+ "required": [
64
+ "id"
65
+ ]
66
+ }
67
+ }
68
+ ]
69
+ }
@@ -0,0 +1,49 @@
1
+ const DEFAULT_BASE_URL = 'https://api-plus.clientify.com/v2';
2
+
3
+ function normalizeBaseUrl(baseUrl) {
4
+ const raw = String(baseUrl || DEFAULT_BASE_URL).trim();
5
+ return raw.endsWith('/') ? raw.slice(0, -1) : raw;
6
+ }
7
+
8
+ function buildUrl(baseUrl, path, query) {
9
+ const base = normalizeBaseUrl(baseUrl);
10
+ const p = path.startsWith('/') ? path : `/${path}`;
11
+ const url = new URL(`${base}${p}`);
12
+ if (query && typeof query === 'object') {
13
+ for (const [key, value] of Object.entries(query)) {
14
+ if (value === undefined || value === null) continue;
15
+ url.searchParams.set(key, String(value));
16
+ }
17
+ }
18
+ return url.toString();
19
+ }
20
+
21
+ /**
22
+ * Minimal Clientify request helper for AppMixer components.
23
+ *
24
+ * We prefer `context.httpRequest()` so AppMixer can handle retries/logging.
25
+ */
26
+ async function clientifyRequest(context, { apiKey, baseUrl, method, path, query, data }) {
27
+ if (!context?.httpRequest) {
28
+ throw new Error('Expected AppMixer context.httpRequest to exist.');
29
+ }
30
+ if (!apiKey) {
31
+ throw new Error('Missing apiKey in AppMixer connection.');
32
+ }
33
+ const url = buildUrl(baseUrl || DEFAULT_BASE_URL, path, query);
34
+ return context.httpRequest({
35
+ method,
36
+ url,
37
+ headers: {
38
+ Authorization: `Token ${apiKey}`,
39
+ 'Content-Type': 'application/json',
40
+ Accept: 'application/json',
41
+ },
42
+ data,
43
+ });
44
+ }
45
+
46
+ module.exports = {
47
+ DEFAULT_BASE_URL,
48
+ clientifyRequest,
49
+ };
@@ -0,0 +1,24 @@
1
+ module.exports = {
2
+ contacts: {
3
+ list:
4
+ 'id,owner_id,first_name,last_name,status,title,company_id,contact_type,contact_source,picture_url,description,remarks,summary,created',
5
+ detail:
6
+ 'id,owner_id,first_name,last_name,status,title,company_id,company_name,company_picture,contact_type,contact_source,picture_url,description,remarks,summary',
7
+ },
8
+ companies: {
9
+ list:
10
+ 'id,name,sector,company_sector,business_name,taxpayer_identification_number,fax,number_of_employees,number_of_employees_desc,owner,owner_name,rank,rank_manual,picture_url,facebook_url,linkedin_url,twitter_url,private,last_viewed,last_interaction,facebook_id,twitter_id,linkedin_id,founded,online_since,full_contact_extra,approx_employees,description,remarks,summary,linkedin_picture_url,created,modified,youtube_url,instagram_url,owner_picture,industry',
11
+ detail:
12
+ 'id,name,sector,company_sector,business_name,taxpayer_identification_number,fax,number_of_employees,number_of_employees_desc,owner,owner_name,rank,rank_manual,picture_url,facebook_url,linkedin_url,twitter_url,private,last_viewed,last_interaction,facebook_id,twitter_id,linkedin_id,founded,online_since,full_contact_extra,approx_employees,description,remarks,summary,linkedin_picture_url,created,modified,youtube_url,instagram_url,owner_picture,industry',
13
+ },
14
+ tasks: {
15
+ list:
16
+ 'url,id,owner,owner_name,owner_id,assigned_to,assigned_to_name,assigned_to_id,name,description,remarks,due_date,start_datetime,end_datetime,duration,type,status,status_desc,task_type,type_desc,task_stage,related_companies,additional_option,location,guest_users,created,modified,related_contacts,priority,recurring_type,recurring_end_date,recurring_interval,tags,activity_type,url_meeting,assigned_to_picture,parent_task_id,colors,recurring_days_of_week,recurring_days_of_month,recurring_days_of_year,number_of_repetitions,outcome,meeting_goal,is_holiday',
17
+ detail:
18
+ 'url,id,owner,owner_name,owner_id,assigned_to,assigned_to_name,assigned_to_id,assigned_to_picture,name,description,remarks,due_date,start_datetime,end_datetime,duration,type,status,status_desc,deals,task_type,type_desc,task_stage,priority,related_companies,related_companies_names,related_companies_data,related_contacts,related_contacts_names,related_contacts_data,related_deals_data,tags,notes,phone_number,colors,additional_option,location,guest_users,created,modified,completed_date,activity_type,parent_task,parent_task_id,recurring_days_of_week,recurring_days_of_month,recurring_days_of_year,recurring_type,recurring_end_date,recurring_interval,meeting_goal,url_meeting,number_of_repetitions,outcome,is_holiday',
19
+ },
20
+ users: {
21
+ list: 'id,username,full_name',
22
+ me: 'id,email,username,full_name',
23
+ },
24
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "appmixer.clientify.crm",
3
+ "label": "CRM",
4
+ "category": "crm",
5
+ "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCIgdmlld0JveD0iMCAwIDY0IDY0Ij48cmVjdCB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIHJ4PSIxMiIgZmlsbD0iIzBlYTVlOSIvPjx0ZXh0IHg9IjMyIiB5PSI0MSIgZm9udC1mYW1pbHk9IkFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMjgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiNmZmYiPkM8L3RleHQ+PC9zdmc+",
6
+ "description": "Clientify CRM actions."
7
+ }
@@ -1,5 +1,5 @@
1
1
  import { ICredentialType, INodeProperties } from 'n8n-workflow';
2
- export declare class ClientifyMcpApi implements ICredentialType {
2
+ export declare class ClientifyApi implements ICredentialType {
3
3
  name: string;
4
4
  displayName: string;
5
5
  documentationUrl: string;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClientifyApi = void 0;
4
+ class ClientifyApi {
5
+ constructor() {
6
+ this.name = 'clientifyApi';
7
+ this.displayName = 'Clientify API';
8
+ this.documentationUrl = 'https://newapi.clientify.com/';
9
+ this.properties = [
10
+ {
11
+ displayName: 'API Key',
12
+ name: 'apiKey',
13
+ type: 'string',
14
+ typeOptions: {
15
+ password: true,
16
+ },
17
+ default: '',
18
+ required: true,
19
+ placeholder: 'Enter your Clientify API key',
20
+ description: 'Clientify API key (used as \"Authorization: Token <apiKey>\")',
21
+ },
22
+ {
23
+ displayName: 'Base URL',
24
+ name: 'baseUrl',
25
+ type: 'string',
26
+ default: 'https://api-plus.clientify.com/v2',
27
+ placeholder: 'https://api-plus.clientify.com/v2',
28
+ description: 'Optional override for the Clientify API base URL',
29
+ },
30
+ ];
31
+ }
32
+ }
33
+ exports.ClientifyApi = ClientifyApi;
@@ -1,5 +1,5 @@
1
1
  import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
- export declare class ClientifyMcpDynamic implements INodeType {
2
+ export declare class ClientifyApi implements INodeType {
3
3
  description: INodeTypeDescription;
4
4
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
5
  }
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClientifyApi = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const api_client_1 = require("@clientify/api-client");
6
+ const ClientifyApiCatalog_1 = require("./ClientifyApiCatalog");
7
+ class ClientifyApi {
8
+ constructor() {
9
+ this.description = {
10
+ displayName: 'Clientify',
11
+ name: 'clientifyApi',
12
+ icon: 'file:clientify.svg',
13
+ group: ['transform'],
14
+ version: 1,
15
+ subtitle: '={{$parameter["operation"]}}',
16
+ description: 'Clientify CRM (direct API via @clientify/api-client)',
17
+ defaults: {
18
+ name: 'Clientify',
19
+ },
20
+ inputs: ['main'],
21
+ outputs: ['main'],
22
+ credentials: [
23
+ {
24
+ name: 'clientifyApi',
25
+ required: true,
26
+ },
27
+ ],
28
+ properties: (() => {
29
+ const props = [
30
+ {
31
+ displayName: 'Action',
32
+ name: 'operation',
33
+ type: 'options',
34
+ options: ClientifyApiCatalog_1.operationOptions,
35
+ default: 'GetCurrentUser',
36
+ required: true,
37
+ noDataExpression: true,
38
+ description: 'Select the Clientify action to execute (mirrors the AppMixer connector action list)',
39
+ },
40
+ ...ClientifyApiCatalog_1.operationFields,
41
+ ];
42
+ return props;
43
+ })(),
44
+ };
45
+ }
46
+ async execute() {
47
+ const items = this.getInputData();
48
+ const returnData = [];
49
+ const credentials = await this.getCredentials('clientifyApi');
50
+ const apiKey = credentials.apiKey;
51
+ const baseUrl = credentials.baseUrl || 'https://api-plus.clientify.com/v2';
52
+ const client = new api_client_1.ClientifyClient({ apiKey, baseUrl });
53
+ const http = client.getHttpClient();
54
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
55
+ const operation = this.getNodeParameter('operation', itemIndex);
56
+ const def = ClientifyApiCatalog_1.operationDefinitions[operation];
57
+ if (!def) {
58
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation "${operation}". Ensure the AppMixer CRM catalog is present in this node package build output.`);
59
+ }
60
+ try {
61
+ const input = {};
62
+ for (const fieldName of def.fieldNames) {
63
+ let value;
64
+ try {
65
+ value = this.getNodeParameter(fieldName, itemIndex);
66
+ }
67
+ catch (_a) {
68
+ continue;
69
+ }
70
+ // Apply per-operation defaults if user left the field empty.
71
+ if ((value === '' || value === null || value === undefined) && fieldName in def.fieldDefaults) {
72
+ value = def.fieldDefaults[fieldName];
73
+ }
74
+ // Avoid accidentally sending empty optional values (n8n defaults).
75
+ const isRequired = def.requiredFieldNames.includes(fieldName);
76
+ if (!isRequired) {
77
+ if (value === '' || value === null || value === undefined)
78
+ continue;
79
+ if (typeof value === 'number' && value === 0)
80
+ continue;
81
+ if (typeof value === 'boolean' && value === false)
82
+ continue;
83
+ }
84
+ input[fieldName] = value;
85
+ }
86
+ // Validate required fields are present (avoid confusing API errors).
87
+ for (const fieldName of def.requiredFieldNames) {
88
+ const value = input[fieldName];
89
+ if (value === undefined || value === null || value === '') {
90
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `${fieldName} is required`);
91
+ }
92
+ if (typeof value === 'number' && value <= 0) {
93
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `${fieldName} must be a positive number`);
94
+ }
95
+ }
96
+ const url = (0, ClientifyApiCatalog_1.renderPathTemplate)(def.pathTemplate, input);
97
+ const rest = (0, ClientifyApiCatalog_1.omitKeys)(input, def.pathParamNames);
98
+ const isQueryMethod = def.method === 'GET' || def.method === 'DELETE';
99
+ const params = isQueryMethod ? Object.assign(Object.assign({}, def.fixedQuery), rest) : undefined;
100
+ const data = !isQueryMethod && Object.keys(rest).length > 0 ? rest : undefined;
101
+ const result = await http.request({
102
+ method: def.method,
103
+ url,
104
+ params,
105
+ data,
106
+ });
107
+ const normalized = result === undefined || result === null || result === ''
108
+ ? { ok: true }
109
+ : typeof result === 'object'
110
+ ? result
111
+ : { data: result };
112
+ returnData.push({
113
+ json: Object.assign(Object.assign({}, normalized), { _meta: {
114
+ operation,
115
+ method: def.method,
116
+ path: url,
117
+ } }),
118
+ pairedItem: itemIndex,
119
+ });
120
+ }
121
+ catch (error) {
122
+ if (this.continueOnFail()) {
123
+ returnData.push({
124
+ json: {
125
+ success: false,
126
+ operation,
127
+ error: error instanceof Error ? error.message : String(error),
128
+ },
129
+ pairedItem: itemIndex,
130
+ });
131
+ continue;
132
+ }
133
+ throw error;
134
+ }
135
+ }
136
+ return [returnData];
137
+ }
138
+ }
139
+ exports.ClientifyApi = ClientifyApi;
@@ -0,0 +1,23 @@
1
+ import { INodeProperties } from 'n8n-workflow';
2
+ export type ClientifyHttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
3
+ export interface ClientifyOperationDefinition {
4
+ operation: string;
5
+ label: string;
6
+ description: string;
7
+ method: ClientifyHttpMethod;
8
+ pathTemplate: string;
9
+ pathParamNames: string[];
10
+ fixedQuery: Record<string, unknown>;
11
+ fieldNames: string[];
12
+ requiredFieldNames: string[];
13
+ fieldDefaults: Record<string, unknown>;
14
+ }
15
+ export declare const operationDefinitions: Record<string, ClientifyOperationDefinition>;
16
+ export declare const operationOptions: {
17
+ name: string;
18
+ value: string;
19
+ description: string;
20
+ }[];
21
+ export declare const operationFields: INodeProperties[];
22
+ export declare function renderPathTemplate(pathTemplate: string, input: Record<string, unknown>): string;
23
+ export declare function omitKeys<T extends Record<string, unknown>>(obj: T, keys: string[]): Record<string, unknown>;
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.operationFields = exports.operationOptions = exports.operationDefinitions = void 0;
7
+ exports.renderPathTemplate = renderPathTemplate;
8
+ exports.omitKeys = omitKeys;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ function safeReadJson(filePath) {
12
+ try {
13
+ return JSON.parse(fs_1.default.readFileSync(filePath, 'utf8'));
14
+ }
15
+ catch (_a) {
16
+ return null;
17
+ }
18
+ }
19
+ function extractMethodAndPath(jsSource) {
20
+ var _a;
21
+ const methodMatch = jsSource.match(/\bmethod:\s*'([A-Z]+)'\s*,/);
22
+ const pathMatch = jsSource.match(/\bpath:\s*(?:'([^']+)'|\"([^\"]+)\"|`([^`]+)`)\s*,?/);
23
+ const rawMethod = methodMatch === null || methodMatch === void 0 ? void 0 : methodMatch[1];
24
+ const pathTemplate = (_a = ((pathMatch === null || pathMatch === void 0 ? void 0 : pathMatch[1]) || (pathMatch === null || pathMatch === void 0 ? void 0 : pathMatch[2]) || (pathMatch === null || pathMatch === void 0 ? void 0 : pathMatch[3]))) === null || _a === void 0 ? void 0 : _a.trim();
25
+ if (!rawMethod || !pathTemplate)
26
+ return null;
27
+ if (!['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(rawMethod))
28
+ return null;
29
+ return { method: rawMethod, pathTemplate };
30
+ }
31
+ function extractFixedQuery(jsSource) {
32
+ // We only extract simple, literal query values (e.g. GetCurrentUser sets `fields: 'id,email'`).
33
+ const queryBlock = jsSource.match(/\bquery:\s*\{([\s\S]*?)\}\s*,/);
34
+ if (!queryBlock)
35
+ return {};
36
+ const content = queryBlock[1];
37
+ const out = {};
38
+ const fieldsMatch = content.match(/\bfields:\s*'([^']+)'/) || content.match(/\bfields:\s*\"([^\"]+)\"/);
39
+ if (fieldsMatch)
40
+ out.fields = fieldsMatch[1];
41
+ return out;
42
+ }
43
+ function getPathParamNames(pathTemplate) {
44
+ var _a;
45
+ const names = new Set();
46
+ const re = /\$\{([^}]+)\}/g;
47
+ let match;
48
+ while ((match = re.exec(pathTemplate))) {
49
+ const expr = (_a = match[1]) === null || _a === void 0 ? void 0 : _a.trim();
50
+ if (expr && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(expr)) {
51
+ names.add(expr);
52
+ }
53
+ }
54
+ return Array.from(names);
55
+ }
56
+ function mapInspectorTypeToN8nType(type) {
57
+ if (type === 'number')
58
+ return 'number';
59
+ if (type === 'toggle')
60
+ return 'boolean';
61
+ return 'string';
62
+ }
63
+ function loadAppmixerFieldsModule() {
64
+ try {
65
+ // In dist, we copy `appmixer/clientify/crm` to `dist/appmixer/clientify/crm`.
66
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
67
+ return require(path_1.default.resolve(__dirname, '../../appmixer/clientify/crm/fields.js'));
68
+ }
69
+ catch (_a) {
70
+ return null;
71
+ }
72
+ }
73
+ function getDefaultFieldsValue(operation, pathTemplate) {
74
+ var _a, _b, _c, _d, _e, _f, _g, _h;
75
+ const fields = loadAppmixerFieldsModule();
76
+ if (!fields)
77
+ return undefined;
78
+ const isContact = pathTemplate.startsWith('/contacts/');
79
+ const isCompany = pathTemplate.startsWith('/companies/');
80
+ const isTask = pathTemplate.startsWith('/tasks/');
81
+ const isUser = pathTemplate.startsWith('/users/');
82
+ if (operation === 'ListContacts' && isContact)
83
+ return (_a = fields.contacts) === null || _a === void 0 ? void 0 : _a.list;
84
+ if (operation === 'GetContact' && isContact)
85
+ return (_b = fields.contacts) === null || _b === void 0 ? void 0 : _b.detail;
86
+ if (operation === 'ListCompanies' && isCompany)
87
+ return (_c = fields.companies) === null || _c === void 0 ? void 0 : _c.list;
88
+ if (operation === 'SearchCompanies' && isCompany)
89
+ return (_d = fields.companies) === null || _d === void 0 ? void 0 : _d.list;
90
+ if (operation === 'GetCompany' && isCompany)
91
+ return (_e = fields.companies) === null || _e === void 0 ? void 0 : _e.detail;
92
+ if (operation === 'ListTasks' && isTask)
93
+ return (_f = fields.tasks) === null || _f === void 0 ? void 0 : _f.list;
94
+ if (operation === 'GetTask' && isTask)
95
+ return (_g = fields.tasks) === null || _g === void 0 ? void 0 : _g.detail;
96
+ if (operation === 'ListUsers' && isUser)
97
+ return (_h = fields.users) === null || _h === void 0 ? void 0 : _h.list;
98
+ return undefined;
99
+ }
100
+ function discoverOperations() {
101
+ var _a, _b, _c, _d, _e, _f, _g;
102
+ const catalog = {};
103
+ const crmDir = path_1.default.resolve(__dirname, '../../appmixer/clientify/crm');
104
+ if (!fs_1.default.existsSync(crmDir)) {
105
+ // Don’t throw — allow n8n to boot, but the node will have no operations.
106
+ // eslint-disable-next-line no-console
107
+ console.warn(`[Clientify n8n] Missing AppMixer CRM catalog at ${crmDir}.`);
108
+ return catalog;
109
+ }
110
+ const entries = fs_1.default.readdirSync(crmDir, { withFileTypes: true }).filter((d) => d.isDirectory());
111
+ for (const entry of entries) {
112
+ const operation = entry.name;
113
+ const componentPath = path_1.default.join(crmDir, operation, 'component.json');
114
+ const jsPath = path_1.default.join(crmDir, operation, `${operation}.js`);
115
+ const componentJson = safeReadJson(componentPath);
116
+ if (!componentJson || !fs_1.default.existsSync(jsPath))
117
+ continue;
118
+ const jsSource = fs_1.default.readFileSync(jsPath, 'utf8');
119
+ const methodAndPath = extractMethodAndPath(jsSource);
120
+ if (!methodAndPath)
121
+ continue;
122
+ const fixedQuery = extractFixedQuery(jsSource);
123
+ const inPort = (_b = (_a = componentJson.inPorts) === null || _a === void 0 ? void 0 : _a.find((p) => (p === null || p === void 0 ? void 0 : p.name) === 'in')) !== null && _b !== void 0 ? _b : (_c = componentJson.inPorts) === null || _c === void 0 ? void 0 : _c[0];
124
+ const requiredFieldNames = ((_e = (_d = inPort === null || inPort === void 0 ? void 0 : inPort.schema) === null || _d === void 0 ? void 0 : _d.required) !== null && _e !== void 0 ? _e : []).filter((k) => typeof k === 'string');
125
+ const inspectorInputs = (_g = (_f = inPort === null || inPort === void 0 ? void 0 : inPort.inspector) === null || _f === void 0 ? void 0 : _f.inputs) !== null && _g !== void 0 ? _g : {};
126
+ const sortedFieldEntries = Object.entries(inspectorInputs).sort((a, b) => {
127
+ var _a, _b, _c, _d;
128
+ const ai = (_b = (_a = a[1]) === null || _a === void 0 ? void 0 : _a.index) !== null && _b !== void 0 ? _b : 9999;
129
+ const bi = (_d = (_c = b[1]) === null || _c === void 0 ? void 0 : _c.index) !== null && _d !== void 0 ? _d : 9999;
130
+ return ai - bi;
131
+ });
132
+ const fieldNames = sortedFieldEntries.map(([key]) => key);
133
+ const pathParamNames = getPathParamNames(methodAndPath.pathTemplate);
134
+ const fieldDefaults = {};
135
+ if (fieldNames.includes('fields')) {
136
+ const defaultFields = getDefaultFieldsValue(operation, methodAndPath.pathTemplate);
137
+ if (defaultFields)
138
+ fieldDefaults.fields = defaultFields;
139
+ }
140
+ catalog[operation] = {
141
+ operation,
142
+ label: componentJson.label || operation,
143
+ description: componentJson.description || '',
144
+ method: methodAndPath.method,
145
+ pathTemplate: methodAndPath.pathTemplate,
146
+ pathParamNames,
147
+ fixedQuery,
148
+ fieldNames,
149
+ requiredFieldNames,
150
+ fieldDefaults,
151
+ };
152
+ }
153
+ return catalog;
154
+ }
155
+ exports.operationDefinitions = discoverOperations();
156
+ exports.operationOptions = Object.values(exports.operationDefinitions)
157
+ .sort((a, b) => a.label.localeCompare(b.label))
158
+ .map((def) => ({
159
+ name: def.label,
160
+ value: def.operation,
161
+ description: def.description,
162
+ }));
163
+ exports.operationFields = (() => {
164
+ var _a, _b, _c, _d, _e, _f;
165
+ const fields = [];
166
+ for (const def of Object.values(exports.operationDefinitions)) {
167
+ const crmDir = path_1.default.resolve(__dirname, '../../appmixer/clientify/crm');
168
+ const componentPath = path_1.default.join(crmDir, def.operation, 'component.json');
169
+ const componentJson = safeReadJson(componentPath);
170
+ const inPort = (_b = (_a = componentJson === null || componentJson === void 0 ? void 0 : componentJson.inPorts) === null || _a === void 0 ? void 0 : _a.find((p) => (p === null || p === void 0 ? void 0 : p.name) === 'in')) !== null && _b !== void 0 ? _b : (_c = componentJson === null || componentJson === void 0 ? void 0 : componentJson.inPorts) === null || _c === void 0 ? void 0 : _c[0];
171
+ const inspectorInputs = (_e = (_d = inPort === null || inPort === void 0 ? void 0 : inPort.inspector) === null || _d === void 0 ? void 0 : _d.inputs) !== null && _e !== void 0 ? _e : {};
172
+ const sortedFieldEntries = Object.entries(inspectorInputs).sort((a, b) => {
173
+ var _a, _b, _c, _d;
174
+ const ai = (_b = (_a = a[1]) === null || _a === void 0 ? void 0 : _a.index) !== null && _b !== void 0 ? _b : 9999;
175
+ const bi = (_d = (_c = b[1]) === null || _c === void 0 ? void 0 : _c.index) !== null && _d !== void 0 ? _d : 9999;
176
+ return ai - bi;
177
+ });
178
+ for (const [fieldName, inputDef] of sortedFieldEntries) {
179
+ const isRequired = def.requiredFieldNames.includes(fieldName);
180
+ const n8nType = mapInspectorTypeToN8nType(inputDef === null || inputDef === void 0 ? void 0 : inputDef.type);
181
+ const defaultValue = (_f = def.fieldDefaults[fieldName]) !== null && _f !== void 0 ? _f : (n8nType === 'number' ? 0 : n8nType === 'boolean' ? false : '');
182
+ fields.push({
183
+ displayName: (inputDef === null || inputDef === void 0 ? void 0 : inputDef.label) || fieldName,
184
+ name: fieldName,
185
+ type: n8nType,
186
+ default: defaultValue,
187
+ required: isRequired,
188
+ description: (inputDef === null || inputDef === void 0 ? void 0 : inputDef.tooltip) || '',
189
+ displayOptions: {
190
+ show: {
191
+ operation: [def.operation],
192
+ },
193
+ },
194
+ });
195
+ }
196
+ }
197
+ return fields;
198
+ })();
199
+ function renderPathTemplate(pathTemplate, input) {
200
+ return pathTemplate.replace(/\$\{([^}]+)\}/g, (_m, expr) => {
201
+ const key = String(expr).trim();
202
+ const value = input[key];
203
+ if (value === undefined || value === null)
204
+ return '';
205
+ return encodeURIComponent(String(value));
206
+ });
207
+ }
208
+ function omitKeys(obj, keys) {
209
+ const omit = new Set(keys);
210
+ const out = {};
211
+ for (const [k, v] of Object.entries(obj)) {
212
+ if (omit.has(k))
213
+ continue;
214
+ out[k] = v;
215
+ }
216
+ return out;
217
+ }
@@ -1,5 +1,5 @@
1
1
  import { IWebhookFunctions, INodeType, INodeTypeDescription, IWebhookResponseData } from 'n8n-workflow';
2
- export declare class ClientifyMcpTrigger implements INodeType {
2
+ export declare class ClientifyTrigger implements INodeType {
3
3
  description: INodeTypeDescription;
4
4
  webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
5
5
  }