@kichat/n8n-nodes-kirimchat 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,6 +5,8 @@ This is an n8n community node for [KirimChat](https://kirim.chat) - a messaging
5
5
  ## Features
6
6
 
7
7
  - **Send Message** - Send text, image, document, audio, video, template, or interactive messages via WhatsApp, Instagram, or Messenger
8
+ - **List Templates** - List all WhatsApp message templates with filtering
9
+ - **Get Template** - Get a specific template by ID
8
10
  - **Mark as Read** - Mark messages as read and send read receipts
9
11
  - **Send Typing Indicator** - Show typing indicator to customers
10
12
  - **Flexible Customer Lookup** - Find customers by ID, phone number, or Instagram username
@@ -63,13 +65,30 @@ Send a message to a customer via WhatsApp, Instagram, or Messenger.
63
65
 
64
66
  #### WhatsApp Template Messages
65
67
 
68
+ Templates can be selected from a dropdown (loaded from your KirimChat account) or entered manually.
69
+
70
+ **Dropdown Mode (Recommended):**
71
+ | Parameter | Description |
72
+ |-----------|-------------|
73
+ | Template | Select from your approved templates (auto-loads from API) |
74
+ | Template Variables | Add values for {{1}}, {{2}}, etc. in order |
75
+
76
+ **Manual Mode:**
66
77
  | Parameter | Description |
67
78
  |-----------|-------------|
68
79
  | Template Name | Name of the approved template |
69
80
  | Template Language | Language code (e.g., `en`, `id`) |
70
- | Template Components | JSON array for variables and buttons |
81
+ | Template Components | JSON array for advanced use cases |
82
+
83
+ **Simple Variables Example:**
71
84
 
72
- **Example Template Components:**
85
+ If your template is: `Hello {{1}}, your order {{2}} is ready!`
86
+
87
+ Just add two variables in order:
88
+ 1. `John` (replaces {{1}})
89
+ 2. `ORD-123` (replaces {{2}})
90
+
91
+ **Advanced Template Components (Manual Mode):**
73
92
  ```json
74
93
  [
75
94
  {
@@ -133,6 +152,73 @@ Mark a message as read and send read receipt to the customer.
133
152
  |-----------|-------------|
134
153
  | Message ID | The ID of the message to mark as read (e.g., `msg_xyz789`) |
135
154
 
155
+ ### List Templates
156
+
157
+ List all WhatsApp message templates with optional filtering.
158
+
159
+ | Parameter | Description |
160
+ |-----------|-------------|
161
+ | Filter by Status | `All`, `APPROVED`, `PENDING`, or `REJECTED` |
162
+ | Filter by Category | `All`, `MARKETING`, `UTILITY`, or `AUTHENTICATION` |
163
+ | Limit | Maximum templates to return (1-500, default 100) |
164
+
165
+ **Example Response:**
166
+ ```json
167
+ {
168
+ "success": true,
169
+ "data": {
170
+ "templates": [
171
+ {
172
+ "id": "tpl_abc123",
173
+ "templateName": "order_confirmation",
174
+ "language": "en",
175
+ "status": "APPROVED",
176
+ "category": "UTILITY",
177
+ "content": "Your order {{1}} has been confirmed.",
178
+ "headerType": "TEXT",
179
+ "headerContent": "Order Update",
180
+ "footerContent": "Thank you for shopping!",
181
+ "variables": ["order_number"],
182
+ "createdAt": "2025-01-15T10:00:00.000Z"
183
+ }
184
+ ],
185
+ "total": 1
186
+ }
187
+ }
188
+ ```
189
+
190
+ ### Get Template
191
+
192
+ Get a specific WhatsApp message template by ID.
193
+
194
+ | Parameter | Description |
195
+ |-----------|-------------|
196
+ | Template ID | The ID of the template (e.g., `tpl_abc123`) |
197
+
198
+ **Example Response:**
199
+ ```json
200
+ {
201
+ "success": true,
202
+ "data": {
203
+ "id": "tpl_abc123",
204
+ "templateName": "order_confirmation",
205
+ "language": "en",
206
+ "status": "APPROVED",
207
+ "category": "UTILITY",
208
+ "content": "Your order {{1}} has been confirmed.",
209
+ "headerType": "TEXT",
210
+ "headerContent": "Order Update",
211
+ "footerContent": "Thank you for shopping!",
212
+ "buttons": [
213
+ { "type": "URL", "text": "Track Order", "url": "https://..." }
214
+ ],
215
+ "variables": ["order_number"],
216
+ "createdAt": "2025-01-15T10:00:00.000Z",
217
+ "updatedAt": "2025-01-15T10:00:00.000Z"
218
+ }
219
+ }
220
+ ```
221
+
136
222
  ### Send Typing Indicator
137
223
 
138
224
  Show typing indicator to a customer before sending a message.
@@ -218,7 +304,16 @@ The node returns standard error responses:
218
304
 
219
305
  ## Changelog
220
306
 
221
- ### v1.1.0 (Latest)
307
+ ### v1.2.0 (Latest)
308
+ - ✅ Added **Template Dropdown** - Select templates from dropdown (auto-loaded from API)
309
+ - ✅ Added **Simple Variables** - Add template variables without writing JSON
310
+ - ✅ Added **List Templates** operation - List all WhatsApp templates with filtering
311
+ - ✅ Added **Get Template** operation - Get template details by ID
312
+ - ✅ Filter templates by status (Approved, Pending, Rejected)
313
+ - ✅ Filter templates by category (Marketing, Utility, Authentication)
314
+ - ✅ Improved template UX - No more manual JSON for simple use cases
315
+
316
+ ### v1.1.0
222
317
  - ✅ Added Messenger channel support
223
318
  - ✅ Added customer lookup by phone number and Instagram username
224
319
  - ✅ Added WhatsApp Interactive messages (CTA URL buttons, Reply buttons)
@@ -1,5 +1,10 @@
1
- import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
1
+ import { IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
2
  export declare class KirimChat implements INodeType {
3
3
  description: INodeTypeDescription;
4
+ methods: {
5
+ loadOptions: {
6
+ getTemplates(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
7
+ };
8
+ };
4
9
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
10
  }
@@ -75,6 +75,18 @@ class KirimChat {
75
75
  description: 'Send a message to a customer via WhatsApp, Instagram, or Messenger',
76
76
  action: 'Send a message',
77
77
  },
78
+ {
79
+ name: 'List Templates',
80
+ value: 'listTemplates',
81
+ description: 'List WhatsApp message templates',
82
+ action: 'List templates',
83
+ },
84
+ {
85
+ name: 'Get Template',
86
+ value: 'getTemplate',
87
+ description: 'Get a specific WhatsApp message template by ID',
88
+ action: 'Get template',
89
+ },
78
90
  {
79
91
  name: 'Mark as Read',
80
92
  value: 'markAsRead',
@@ -374,6 +386,36 @@ class KirimChat {
374
386
  // ============================================
375
387
  // TEMPLATE FIELDS (WhatsApp only)
376
388
  // ============================================
389
+ {
390
+ displayName: 'Template',
391
+ name: 'templateSelect',
392
+ type: 'options',
393
+ required: true,
394
+ displayOptions: {
395
+ show: {
396
+ operation: ['sendMessage'],
397
+ messageType: ['template'],
398
+ },
399
+ },
400
+ typeOptions: {
401
+ loadOptionsMethod: 'getTemplates',
402
+ },
403
+ default: '',
404
+ description: 'Select an approved WhatsApp template. Templates are loaded from your KirimChat account.',
405
+ },
406
+ {
407
+ displayName: 'Or Enter Template Name Manually',
408
+ name: 'templateManualEntry',
409
+ type: 'boolean',
410
+ displayOptions: {
411
+ show: {
412
+ operation: ['sendMessage'],
413
+ messageType: ['template'],
414
+ },
415
+ },
416
+ default: false,
417
+ description: 'Enable to manually enter template name and language instead of selecting from dropdown',
418
+ },
377
419
  {
378
420
  displayName: 'Template Name',
379
421
  name: 'templateName',
@@ -383,6 +425,7 @@ class KirimChat {
383
425
  show: {
384
426
  operation: ['sendMessage'],
385
427
  messageType: ['template'],
428
+ templateManualEntry: [true],
386
429
  },
387
430
  },
388
431
  default: '',
@@ -398,12 +441,45 @@ class KirimChat {
398
441
  show: {
399
442
  operation: ['sendMessage'],
400
443
  messageType: ['template'],
444
+ templateManualEntry: [true],
401
445
  },
402
446
  },
403
447
  default: 'en',
404
- description: 'Language code for the template',
448
+ description: 'Language code for the template (e.g., en, id, es)',
405
449
  placeholder: 'en',
406
450
  },
451
+ {
452
+ displayName: 'Template Variables',
453
+ name: 'templateVariables',
454
+ type: 'fixedCollection',
455
+ displayOptions: {
456
+ show: {
457
+ operation: ['sendMessage'],
458
+ messageType: ['template'],
459
+ },
460
+ },
461
+ default: {},
462
+ placeholder: 'Add Variable',
463
+ description: 'Variables to replace {{1}}, {{2}}, etc. in your template. Add them in order.',
464
+ typeOptions: {
465
+ multipleValues: true,
466
+ },
467
+ options: [
468
+ {
469
+ name: 'variables',
470
+ displayName: 'Variables',
471
+ values: [
472
+ {
473
+ displayName: 'Value',
474
+ name: 'value',
475
+ type: 'string',
476
+ default: '',
477
+ description: 'Value for this variable (replaces {{1}}, {{2}}, etc. in order)',
478
+ },
479
+ ],
480
+ },
481
+ ],
482
+ },
407
483
  {
408
484
  displayName: 'Template Components (JSON)',
409
485
  name: 'templateComponents',
@@ -412,10 +488,11 @@ class KirimChat {
412
488
  show: {
413
489
  operation: ['sendMessage'],
414
490
  messageType: ['template'],
491
+ templateManualEntry: [true],
415
492
  },
416
493
  },
417
494
  default: '[]',
418
- description: 'Template components as JSON array (for variables, buttons, etc.)',
495
+ description: 'Advanced: Full template components JSON (for header media, buttons, etc.). Leave empty to use simple variables above.',
419
496
  },
420
497
  // ============================================
421
498
  // INTERACTIVE MESSAGE FIELDS (WhatsApp only)
@@ -556,6 +633,110 @@ class KirimChat {
556
633
  placeholder: 'msg_xyz789',
557
634
  },
558
635
  // ============================================
636
+ // LIST TEMPLATES FIELDS
637
+ // ============================================
638
+ {
639
+ displayName: 'Filter by Status',
640
+ name: 'templateStatus',
641
+ type: 'options',
642
+ displayOptions: {
643
+ show: {
644
+ operation: ['listTemplates'],
645
+ },
646
+ },
647
+ options: [
648
+ {
649
+ name: 'All',
650
+ value: '',
651
+ description: 'Return all templates',
652
+ },
653
+ {
654
+ name: 'Approved',
655
+ value: 'APPROVED',
656
+ description: 'Only approved templates (ready to use)',
657
+ },
658
+ {
659
+ name: 'Pending',
660
+ value: 'PENDING',
661
+ description: 'Templates pending approval',
662
+ },
663
+ {
664
+ name: 'Rejected',
665
+ value: 'REJECTED',
666
+ description: 'Rejected templates',
667
+ },
668
+ ],
669
+ default: '',
670
+ description: 'Filter templates by approval status',
671
+ },
672
+ {
673
+ displayName: 'Filter by Category',
674
+ name: 'templateCategory',
675
+ type: 'options',
676
+ displayOptions: {
677
+ show: {
678
+ operation: ['listTemplates'],
679
+ },
680
+ },
681
+ options: [
682
+ {
683
+ name: 'All',
684
+ value: '',
685
+ description: 'Return all categories',
686
+ },
687
+ {
688
+ name: 'Marketing',
689
+ value: 'MARKETING',
690
+ description: 'Marketing templates',
691
+ },
692
+ {
693
+ name: 'Utility',
694
+ value: 'UTILITY',
695
+ description: 'Utility templates (order updates, etc.)',
696
+ },
697
+ {
698
+ name: 'Authentication',
699
+ value: 'AUTHENTICATION',
700
+ description: 'Authentication templates (OTP, etc.)',
701
+ },
702
+ ],
703
+ default: '',
704
+ description: 'Filter templates by category',
705
+ },
706
+ {
707
+ displayName: 'Limit',
708
+ name: 'templateLimit',
709
+ type: 'number',
710
+ displayOptions: {
711
+ show: {
712
+ operation: ['listTemplates'],
713
+ },
714
+ },
715
+ default: 100,
716
+ description: 'Maximum number of templates to return (max 500)',
717
+ typeOptions: {
718
+ minValue: 1,
719
+ maxValue: 500,
720
+ },
721
+ },
722
+ // ============================================
723
+ // GET TEMPLATE FIELDS
724
+ // ============================================
725
+ {
726
+ displayName: 'Template ID',
727
+ name: 'templateId',
728
+ type: 'string',
729
+ required: true,
730
+ displayOptions: {
731
+ show: {
732
+ operation: ['getTemplate'],
733
+ },
734
+ },
735
+ default: '',
736
+ description: 'The ID of the template to retrieve',
737
+ placeholder: 'tpl_abc123',
738
+ },
739
+ // ============================================
559
740
  // SEND TYPING FIELDS
560
741
  // ============================================
561
742
  {
@@ -662,6 +843,48 @@ class KirimChat {
662
843
  },
663
844
  ],
664
845
  };
846
+ this.methods = {
847
+ loadOptions: {
848
+ // Load templates from KirimChat API for dropdown
849
+ async getTemplates() {
850
+ const credentials = await this.getCredentials('kirimChatApi');
851
+ const baseUrl = credentials.baseUrl;
852
+ try {
853
+ const response = await this.helpers.httpRequest({
854
+ method: 'GET',
855
+ url: `${baseUrl}/templates?status=APPROVED&limit=500`,
856
+ headers: {
857
+ 'X-API-Key': credentials.apiKey,
858
+ 'Content-Type': 'application/json',
859
+ },
860
+ });
861
+ if (!response.success || !response.data) {
862
+ return [{ name: 'No templates found', value: '' }];
863
+ }
864
+ const templates = response.data;
865
+ if (templates.length === 0) {
866
+ return [{ name: 'No approved templates', value: '' }];
867
+ }
868
+ return templates.map((t) => {
869
+ var _a;
870
+ return ({
871
+ name: `${t.template_name} (${t.language}) - ${t.category}`,
872
+ value: JSON.stringify({
873
+ name: t.template_name,
874
+ language: t.language,
875
+ has_variables: t.has_variables,
876
+ content: t.content,
877
+ }),
878
+ description: ((_a = t.content) === null || _a === void 0 ? void 0 : _a.substring(0, 100)) || '',
879
+ });
880
+ });
881
+ }
882
+ catch (error) {
883
+ return [{ name: 'Error loading templates', value: '' }];
884
+ }
885
+ },
886
+ },
887
+ };
665
888
  }
666
889
  async execute() {
667
890
  const items = this.getInputData();
@@ -699,19 +922,61 @@ class KirimChat {
699
922
  }
700
923
  else if (messageType === 'template') {
701
924
  // WhatsApp template message
702
- const templateName = this.getNodeParameter('templateName', i);
703
- const templateLanguage = this.getNodeParameter('templateLanguage', i);
704
- const templateComponentsJson = this.getNodeParameter('templateComponents', i, '[]');
705
- let templateComponents;
706
- try {
707
- templateComponents = JSON.parse(templateComponentsJson);
925
+ const manualEntry = this.getNodeParameter('templateManualEntry', i, false);
926
+ let templateName;
927
+ let templateLanguage;
928
+ let templateComponents = [];
929
+ if (manualEntry) {
930
+ // Manual entry mode - user types name and language
931
+ templateName = this.getNodeParameter('templateName', i);
932
+ templateLanguage = this.getNodeParameter('templateLanguage', i);
933
+ // Check for advanced JSON components
934
+ const templateComponentsJson = this.getNodeParameter('templateComponents', i, '[]');
935
+ if (templateComponentsJson && templateComponentsJson !== '[]') {
936
+ try {
937
+ templateComponents = JSON.parse(templateComponentsJson);
938
+ }
939
+ catch (error) {
940
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid JSON in Template Components: ${error instanceof Error ? error.message : 'Parse failed'}`, { itemIndex: i });
941
+ }
942
+ // Validate template components structure
943
+ if (!validateTemplateComponents(templateComponents)) {
944
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Template Components must be a JSON array of objects. Example: [{"type": "body", "parameters": [...]}]', { itemIndex: i });
945
+ }
946
+ }
708
947
  }
709
- catch (error) {
710
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid JSON in Template Components: ${error instanceof Error ? error.message : 'Parse failed'}`, { itemIndex: i });
948
+ else {
949
+ // Dropdown selection mode - parse from selected value
950
+ const templateSelectValue = this.getNodeParameter('templateSelect', i);
951
+ if (!templateSelectValue) {
952
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Please select a template or enable manual entry', { itemIndex: i });
953
+ }
954
+ try {
955
+ const templateData = JSON.parse(templateSelectValue);
956
+ templateName = templateData.name;
957
+ templateLanguage = templateData.language;
958
+ }
959
+ catch (error) {
960
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid template selection. Please re-select the template.', { itemIndex: i });
961
+ }
711
962
  }
712
- // Validate template components structure
713
- if (!validateTemplateComponents(templateComponents)) {
714
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Template Components must be a JSON array of objects. Example: [{"type": "body", "parameters": [...]}]', { itemIndex: i });
963
+ // Handle simple variables (from fixedCollection)
964
+ const templateVariablesData = this.getNodeParameter('templateVariables', i, {});
965
+ if (templateVariablesData.variables && templateVariablesData.variables.length > 0) {
966
+ // Build body component with parameters
967
+ const bodyParameters = templateVariablesData.variables.map((v) => ({
968
+ type: 'text',
969
+ text: v.value,
970
+ }));
971
+ // If no components provided, create body component with variables
972
+ if (!Array.isArray(templateComponents) || templateComponents.length === 0) {
973
+ templateComponents = [
974
+ {
975
+ type: 'body',
976
+ parameters: bodyParameters,
977
+ },
978
+ ];
979
+ }
715
980
  }
716
981
  body.template = {
717
982
  name: templateName,
@@ -827,6 +1092,42 @@ class KirimChat {
827
1092
  json: true,
828
1093
  });
829
1094
  }
1095
+ else if (operation === 'listTemplates') {
1096
+ // ============================================
1097
+ // LIST TEMPLATES OPERATION
1098
+ // ============================================
1099
+ const templateStatus = this.getNodeParameter('templateStatus', i, '');
1100
+ const templateCategory = this.getNodeParameter('templateCategory', i, '');
1101
+ const templateLimit = this.getNodeParameter('templateLimit', i, 100);
1102
+ // Build query string
1103
+ const queryParams = [];
1104
+ if (templateStatus) {
1105
+ queryParams.push(`status=${templateStatus}`);
1106
+ }
1107
+ if (templateCategory) {
1108
+ queryParams.push(`category=${templateCategory}`);
1109
+ }
1110
+ if (templateLimit) {
1111
+ queryParams.push(`limit=${templateLimit}`);
1112
+ }
1113
+ const queryString = queryParams.length > 0 ? `?${queryParams.join('&')}` : '';
1114
+ responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'kirimChatApi', {
1115
+ method: 'GET',
1116
+ url: `${baseUrl}/templates${queryString}`,
1117
+ json: true,
1118
+ });
1119
+ }
1120
+ else if (operation === 'getTemplate') {
1121
+ // ============================================
1122
+ // GET TEMPLATE OPERATION
1123
+ // ============================================
1124
+ const templateId = this.getNodeParameter('templateId', i);
1125
+ responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'kirimChatApi', {
1126
+ method: 'GET',
1127
+ url: `${baseUrl}/templates/${templateId}`,
1128
+ json: true,
1129
+ });
1130
+ }
830
1131
  else if (operation === 'sendTyping') {
831
1132
  // ============================================
832
1133
  // SEND TYPING INDICATOR OPERATION
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
- {
2
- "name": "@kichat/n8n-nodes-kirimchat",
3
- "version": "1.1.0",
4
- "description": "n8n community node for KirimChat - Send WhatsApp, Instagram & Messenger messages with interactive buttons, flexible customer lookup, and typing indicators",
5
- "keywords": [
6
- "n8n-community-node-package",
7
- "n8n",
8
- "kirimchat",
9
- "whatsapp",
10
- "instagram",
11
- "messenger",
12
- "facebook",
13
- "messaging",
14
- "chat",
15
- "automation"
16
- ],
1
+ {
2
+ "name": "@kichat/n8n-nodes-kirimchat",
3
+ "version": "1.2.1",
4
+ "description": "n8n community node for KirimChat - Send WhatsApp, Instagram & Messenger messages with interactive buttons, flexible customer lookup, and typing indicators",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "n8n",
8
+ "kirimchat",
9
+ "whatsapp",
10
+ "instagram",
11
+ "messenger",
12
+ "facebook",
13
+ "messaging",
14
+ "chat",
15
+ "automation"
16
+ ],
17
17
  "license": "MIT",
18
18
  "homepage": "https://kirim.chat",
19
19
  "author": {