@kichat/n8n-nodes-kirimchat 1.0.5 → 1.1.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.
package/README.md CHANGED
@@ -1,139 +1,244 @@
1
- # n8n-nodes-kirimchat
2
-
3
- This is an n8n community node for [KirimChat](https://kirim.chat) - a messaging platform that integrates WhatsApp Business API and Instagram DM.
4
-
5
- ## Features
6
-
7
- - **Send Message** - Send text, image, document, audio, video, or template messages via WhatsApp or Instagram
8
- - **Mark as Read** - Mark messages as read and send read receipts
9
- - **Send Typing Indicator** - Show typing indicator to customers
10
-
11
- ## Installation
12
-
13
- ### Community Nodes (Recommended)
14
-
15
- 1. Go to **Settings > Community Nodes**
16
- 2. Select **Install**
17
- 3. Enter `@kichat/n8n-nodes-kirimchat` and confirm
18
-
19
- ## Credentials
20
-
21
- You need a KirimChat API key to use this node:
22
-
23
- 1. Log in to your KirimChat dashboard
24
- 2. Go to **Settings > Developers > API Keys**
25
- 3. Click **Create API Key** and copy the key (shown only once)
26
- 4. In n8n, create new credentials for **KirimChat API**
27
- 5. Paste your API key (starts with `kc_live_`)
28
-
29
- ## Operations
30
-
31
- ### Send Message
32
-
33
- Send a message to a customer via WhatsApp or Instagram.
34
-
35
- | Parameter | Description |
36
- |-----------|-------------|
37
- | Customer ID | The ID of the customer (e.g., `cust_abc123`) |
38
- | Channel | `whatsapp` or `instagram` |
39
- | Message Type | `text`, `image`, `document`, `audio`, `video`, `template` (WhatsApp) or `text`, `image`, `media_share` (Instagram) |
40
- | Content | Text content for text messages |
41
- | Media URL | URL of media file for media messages |
42
- | Caption | Optional caption for media messages |
43
-
44
- **Example Response:**
45
- ```json
46
- {
47
- "success": true,
48
- "data": {
49
- "message_id": "msg_xyz789",
50
- "status": "sent",
51
- "channel": "whatsapp",
52
- "timestamp": "2025-11-26T10:31:00.000Z"
53
- }
54
- }
55
- ```
56
-
57
- ### Mark as Read
58
-
59
- Mark a message as read and send read receipt to the customer.
60
-
61
- | Parameter | Description |
62
- |-----------|-------------|
63
- | Message ID | The ID of the message to mark as read (e.g., `msg_xyz789`) |
64
-
65
- ### Send Typing Indicator
66
-
67
- Show typing indicator to a customer before sending a message.
68
-
69
- | Parameter | Description |
70
- |-----------|-------------|
71
- | Customer ID | The ID of the customer |
72
- | Channel | `whatsapp` or `instagram` |
73
-
74
- > **Note:** Rate limited to 1 request per customer per 3 seconds.
75
-
76
- ## Webhook Trigger
77
-
78
- KirimChat supports outbound webhooks for real-time event notifications. Configure webhooks in your KirimChat dashboard under **Settings > Developers > Webhooks**.
79
-
80
- ### Supported Events
81
-
82
- - `message.received` - New message from customer
83
- - `message.sent` - Message sent to customer
84
- - `message.delivered` - Message delivered
85
- - `message.read` - Message read by customer
86
- - `message.failed` - Message delivery failed
87
-
88
- ### Webhook Payload Example
89
-
90
- ```json
91
- {
92
- "event_type": "message.received",
93
- "event_id": "evt_abc123",
94
- "timestamp": "2025-11-26T10:30:00.000Z",
95
- "data": {
96
- "message_id": "msg_xyz789",
97
- "customer_id": "cust_123",
98
- "customer_phone": "+6281234567890",
99
- "direction": "inbound",
100
- "message_type": "text",
101
- "content": "Hello!",
102
- "channel": "whatsapp"
103
- }
104
- }
105
- ```
106
-
107
- ### Using with n8n Webhook Node
108
-
109
- 1. Create a new workflow with **Webhook** trigger node
110
- 2. Copy the webhook URL
111
- 3. In KirimChat, create a webhook endpoint with this URL
112
- 4. Select the events you want to receive
113
- 5. Connect the webhook to your KirimChat node for automated responses
114
-
115
- ## Rate Limits
116
-
117
- - **Global:** 100 requests per minute per API key
118
- - **Send Message:** 60 messages per minute per API key
119
- - **Typing Indicator:** 1 request per customer per 3 seconds
120
-
121
- ## Error Handling
122
-
123
- The node returns standard error responses:
124
-
125
- | Code | Description |
126
- |------|-------------|
127
- | 401 | Invalid or expired API key |
128
- | 404 | Resource not found |
129
- | 400 | Invalid request or messaging window closed |
130
- | 429 | Rate limit exceeded |
131
-
132
- ## Resources
133
-
134
- - [KirimChat Documentation](https://kirim.chat/developers)
135
- - [n8n Community Nodes](https://docs.n8n.io/integrations/community-nodes/)
136
-
137
- ## License
138
-
139
- MIT
1
+ # n8n-nodes-kirimchat
2
+
3
+ This is an n8n community node for [KirimChat](https://kirim.chat) - a messaging platform that integrates WhatsApp Business API, Instagram DM, and Facebook Messenger.
4
+
5
+ ## Features
6
+
7
+ - **Send Message** - Send text, image, document, audio, video, template, or interactive messages via WhatsApp, Instagram, or Messenger
8
+ - **Mark as Read** - Mark messages as read and send read receipts
9
+ - **Send Typing Indicator** - Show typing indicator to customers
10
+ - **Flexible Customer Lookup** - Find customers by ID, phone number, or Instagram username
11
+
12
+ ## Installation
13
+
14
+ ### Community Nodes (Recommended)
15
+
16
+ 1. Go to **Settings > Community Nodes**
17
+ 2. Select **Install**
18
+ 3. Enter `@kichat/n8n-nodes-kirimchat` and confirm
19
+
20
+ ## Credentials
21
+
22
+ You need a KirimChat API key to use this node:
23
+
24
+ 1. Log in to your KirimChat dashboard
25
+ 2. Go to **Settings > Developers > API Keys**
26
+ 3. Click **Create API Key** and copy the key (shown only once)
27
+ 4. In n8n, create new credentials for **KirimChat API**
28
+ 5. Paste your API key (starts with `kc_live_`)
29
+
30
+ ## Operations
31
+
32
+ ### Send Message
33
+
34
+ Send a message to a customer via WhatsApp, Instagram, or Messenger.
35
+
36
+ #### Customer Lookup Options
37
+
38
+ | Method | Description | Example |
39
+ |--------|-------------|---------|
40
+ | Customer ID | KirimChat customer ID | `cust_abc123` |
41
+ | Phone Number | Phone with country code | `+6281234567890` |
42
+ | Instagram Username | Instagram username | `johndoe` |
43
+
44
+ #### Channels & Message Types
45
+
46
+ | Channel | Message Types |
47
+ |---------|---------------|
48
+ | **WhatsApp** | `text`, `image`, `document`, `audio`, `video`, `template`, `interactive` |
49
+ | **Instagram** | `text`, `image`, `media_share` |
50
+ | **Messenger** | `text`, `image`, `video`, `audio`, `file` |
51
+
52
+ #### Parameters
53
+
54
+ | Parameter | Description |
55
+ |-----------|-------------|
56
+ | Customer Lookup | How to identify the customer |
57
+ | Channel | `whatsapp`, `instagram`, or `messenger` |
58
+ | Message Type | Type of message (varies by channel) |
59
+ | Content | Text content for text messages |
60
+ | Media URL | URL of media file for media messages |
61
+ | Caption | Optional caption for media messages |
62
+ | Filename | Filename for document/file messages |
63
+
64
+ #### WhatsApp Template Messages
65
+
66
+ | Parameter | Description |
67
+ |-----------|-------------|
68
+ | Template Name | Name of the approved template |
69
+ | Template Language | Language code (e.g., `en`, `id`) |
70
+ | Template Components | JSON array for variables and buttons |
71
+
72
+ **Example Template Components:**
73
+ ```json
74
+ [
75
+ {
76
+ "type": "body",
77
+ "parameters": [
78
+ { "type": "text", "text": "John" },
79
+ { "type": "text", "text": "12345" }
80
+ ]
81
+ }
82
+ ]
83
+ ```
84
+
85
+ #### WhatsApp Interactive Messages
86
+
87
+ Send buttons with your messages:
88
+
89
+ **CTA URL Button:**
90
+ | Parameter | Description |
91
+ |-----------|-------------|
92
+ | Interactive Type | `cta_url` |
93
+ | Body Text | Main message text (max 1024 chars) |
94
+ | Header Text | Optional header (max 60 chars) |
95
+ | Footer Text | Optional footer (max 60 chars) |
96
+ | Button Text | Text on button (max 20 chars) |
97
+ | Button URL | URL to open |
98
+
99
+ **Reply Buttons:**
100
+ | Parameter | Description |
101
+ |-----------|-------------|
102
+ | Interactive Type | `button` |
103
+ | Body Text | Main message text |
104
+ | Buttons (JSON) | Array of 1-3 buttons |
105
+
106
+ **Example Reply Buttons:**
107
+ ```json
108
+ [
109
+ { "id": "yes", "title": "Yes" },
110
+ { "id": "no", "title": "No" },
111
+ { "id": "maybe", "title": "Maybe" }
112
+ ]
113
+ ```
114
+
115
+ **Example Response:**
116
+ ```json
117
+ {
118
+ "success": true,
119
+ "data": {
120
+ "message_id": "msg_xyz789",
121
+ "status": "sent",
122
+ "channel": "whatsapp",
123
+ "timestamp": "2025-11-26T10:31:00.000Z"
124
+ }
125
+ }
126
+ ```
127
+
128
+ ### Mark as Read
129
+
130
+ Mark a message as read and send read receipt to the customer.
131
+
132
+ | Parameter | Description |
133
+ |-----------|-------------|
134
+ | Message ID | The ID of the message to mark as read (e.g., `msg_xyz789`) |
135
+
136
+ ### Send Typing Indicator
137
+
138
+ Show typing indicator to a customer before sending a message.
139
+
140
+ | Parameter | Description |
141
+ |-----------|-------------|
142
+ | Customer Lookup | How to identify the customer |
143
+ | Customer ID/Phone/Username | Customer identifier |
144
+ | Channel | `whatsapp`, `instagram`, or auto-detect |
145
+
146
+ > **Note:** Rate limited to 1 request per customer per 3 seconds.
147
+
148
+ ## Webhook Trigger
149
+
150
+ KirimChat supports outbound webhooks for real-time event notifications. Configure webhooks in your KirimChat dashboard under **Settings > Developers > Webhooks**.
151
+
152
+ ### Supported Events
153
+
154
+ - `message.received` - New message from customer
155
+ - `message.sent` - Message sent to customer
156
+ - `message.delivered` - Message delivered
157
+ - `message.read` - Message read by customer
158
+ - `message.failed` - Message delivery failed
159
+
160
+ ### Webhook Payload Example
161
+
162
+ ```json
163
+ {
164
+ "event_type": "message.received",
165
+ "event_id": "evt_abc123",
166
+ "timestamp": "2025-11-26T10:30:00.000Z",
167
+ "data": {
168
+ "message_id": "msg_xyz789",
169
+ "customer_id": "cust_123",
170
+ "customer_phone": "+6281234567890",
171
+ "direction": "inbound",
172
+ "message_type": "text",
173
+ "content": "Hello!",
174
+ "channel": "whatsapp"
175
+ }
176
+ }
177
+ ```
178
+
179
+ ### Using with n8n Webhook Node
180
+
181
+ 1. Create a new workflow with **Webhook** trigger node
182
+ 2. Copy the webhook URL
183
+ 3. In KirimChat, create a webhook endpoint with this URL
184
+ 4. Select the events you want to receive
185
+ 5. Connect the webhook to your KirimChat node for automated responses
186
+
187
+ ## Rate Limits
188
+
189
+ | Limit Type | Rate |
190
+ |------------|------|
191
+ | Global | 100 requests per minute per API key |
192
+ | WhatsApp | 60 messages per minute |
193
+ | Instagram | 180 messages per hour |
194
+ | Messenger | 180 messages per hour |
195
+ | Typing Indicator | 1 request per customer per 3 seconds |
196
+
197
+ ## Error Handling
198
+
199
+ The node returns standard error responses:
200
+
201
+ | Code | Description |
202
+ |------|-------------|
203
+ | 401 | Invalid or expired API key |
204
+ | 403 | Account disconnected or forbidden |
205
+ | 404 | Resource not found |
206
+ | 400 | Invalid request or messaging window closed |
207
+ | 429 | Rate limit exceeded |
208
+
209
+ ### Common Error Codes
210
+
211
+ | Error Code | Description |
212
+ |------------|-------------|
213
+ | `ValidationError` | Invalid input parameters |
214
+ | `WindowClosed` | 24-hour messaging window expired |
215
+ | `ConfigurationError` | Channel not configured |
216
+ | `ConnectionError` | Account disconnected |
217
+ | `RateLimitExceeded` | Too many requests |
218
+
219
+ ## Changelog
220
+
221
+ ### v1.1.0 (Latest)
222
+ - ✅ Added Messenger channel support
223
+ - ✅ Added customer lookup by phone number and Instagram username
224
+ - ✅ Added WhatsApp Interactive messages (CTA URL buttons, Reply buttons)
225
+ - ✅ Improved typing indicator with flexible customer lookup
226
+ - ✅ Enhanced error messages
227
+
228
+ ### v1.0.5
229
+ - Enhanced validation for media URLs
230
+ - Template components structure validation
231
+
232
+ ### v1.0.1
233
+ - Initial security improvements
234
+ - JSON validation for templates
235
+ - Base URL security warning
236
+
237
+ ## Resources
238
+
239
+ - [KirimChat Documentation](https://kirim.chat/developers)
240
+ - [n8n Community Nodes](https://docs.n8n.io/integrations/community-nodes/)
241
+
242
+ ## License
243
+
244
+ MIT
@@ -23,6 +23,24 @@ function validateTemplateComponents(components) {
23
23
  }
24
24
  return components.every((component) => typeof component === 'object' && component !== null);
25
25
  }
26
+ /**
27
+ * Validates interactive message structure
28
+ */
29
+ function validateInteractiveMessage(interactive) {
30
+ if (typeof interactive !== 'object' || interactive === null) {
31
+ return false;
32
+ }
33
+ const obj = interactive;
34
+ // Must have type and body
35
+ if (!obj.type || !obj.body) {
36
+ return false;
37
+ }
38
+ // type must be 'cta_url' or 'button'
39
+ if (obj.type !== 'cta_url' && obj.type !== 'button') {
40
+ return false;
41
+ }
42
+ return true;
43
+ }
26
44
  class KirimChat {
27
45
  constructor() {
28
46
  this.description = {
@@ -32,7 +50,7 @@ class KirimChat {
32
50
  group: ['transform'],
33
51
  version: 1,
34
52
  subtitle: '={{$parameter["operation"]}}',
35
- description: 'Send WhatsApp & Instagram messages, mark as read, and send typing indicators',
53
+ description: 'Send WhatsApp, Instagram & Messenger messages, mark as read, and send typing indicators',
36
54
  defaults: {
37
55
  name: 'KirimChat',
38
56
  },
@@ -54,7 +72,7 @@ class KirimChat {
54
72
  {
55
73
  name: 'Send Message',
56
74
  value: 'sendMessage',
57
- description: 'Send a message to a customer via WhatsApp or Instagram',
75
+ description: 'Send a message to a customer via WhatsApp, Instagram, or Messenger',
58
76
  action: 'Send a message',
59
77
  },
60
78
  {
@@ -72,7 +90,40 @@ class KirimChat {
72
90
  ],
73
91
  default: 'sendMessage',
74
92
  },
75
- // Send Message Fields
93
+ // ============================================
94
+ // SEND MESSAGE FIELDS
95
+ // ============================================
96
+ // Customer Lookup Method
97
+ {
98
+ displayName: 'Customer Lookup',
99
+ name: 'customerLookup',
100
+ type: 'options',
101
+ required: true,
102
+ displayOptions: {
103
+ show: {
104
+ operation: ['sendMessage'],
105
+ },
106
+ },
107
+ options: [
108
+ {
109
+ name: 'Customer ID',
110
+ value: 'customer_id',
111
+ description: 'Lookup by KirimChat customer ID (e.g., cust_abc123)',
112
+ },
113
+ {
114
+ name: 'Phone Number',
115
+ value: 'phone_number',
116
+ description: 'Lookup by phone number (e.g., +6281234567890)',
117
+ },
118
+ {
119
+ name: 'Instagram Username',
120
+ value: 'instagram_username',
121
+ description: 'Lookup by Instagram username',
122
+ },
123
+ ],
124
+ default: 'customer_id',
125
+ description: 'How to identify the customer',
126
+ },
76
127
  {
77
128
  displayName: 'Customer ID',
78
129
  name: 'customerId',
@@ -81,12 +132,44 @@ class KirimChat {
81
132
  displayOptions: {
82
133
  show: {
83
134
  operation: ['sendMessage'],
135
+ customerLookup: ['customer_id'],
84
136
  },
85
137
  },
86
138
  default: '',
87
139
  description: 'The ID of the customer to send the message to',
88
140
  placeholder: 'cust_abc123',
89
141
  },
142
+ {
143
+ displayName: 'Phone Number',
144
+ name: 'phoneNumber',
145
+ type: 'string',
146
+ required: true,
147
+ displayOptions: {
148
+ show: {
149
+ operation: ['sendMessage'],
150
+ customerLookup: ['phone_number'],
151
+ },
152
+ },
153
+ default: '',
154
+ description: 'The phone number of the customer (with country code)',
155
+ placeholder: '+6281234567890',
156
+ },
157
+ {
158
+ displayName: 'Instagram Username',
159
+ name: 'instagramUsername',
160
+ type: 'string',
161
+ required: true,
162
+ displayOptions: {
163
+ show: {
164
+ operation: ['sendMessage'],
165
+ customerLookup: ['instagram_username'],
166
+ },
167
+ },
168
+ default: '',
169
+ description: 'The Instagram username of the customer',
170
+ placeholder: 'username',
171
+ },
172
+ // Channel Selection
90
173
  {
91
174
  displayName: 'Channel',
92
175
  name: 'channel',
@@ -106,10 +189,15 @@ class KirimChat {
106
189
  name: 'Instagram',
107
190
  value: 'instagram',
108
191
  },
192
+ {
193
+ name: 'Messenger',
194
+ value: 'messenger',
195
+ },
109
196
  ],
110
197
  default: 'whatsapp',
111
198
  description: 'The messaging channel to use',
112
199
  },
200
+ // Message Type - WhatsApp
113
201
  {
114
202
  displayName: 'Message Type',
115
203
  name: 'messageType',
@@ -146,10 +234,15 @@ class KirimChat {
146
234
  name: 'Template',
147
235
  value: 'template',
148
236
  },
237
+ {
238
+ name: 'Interactive (Buttons)',
239
+ value: 'interactive',
240
+ },
149
241
  ],
150
242
  default: 'text',
151
243
  description: 'The type of message to send (WhatsApp)',
152
244
  },
245
+ // Message Type - Instagram
153
246
  {
154
247
  displayName: 'Message Type',
155
248
  name: 'messageType',
@@ -178,6 +271,44 @@ class KirimChat {
178
271
  default: 'text',
179
272
  description: 'The type of message to send (Instagram)',
180
273
  },
274
+ // Message Type - Messenger
275
+ {
276
+ displayName: 'Message Type',
277
+ name: 'messageType',
278
+ type: 'options',
279
+ required: true,
280
+ displayOptions: {
281
+ show: {
282
+ operation: ['sendMessage'],
283
+ channel: ['messenger'],
284
+ },
285
+ },
286
+ options: [
287
+ {
288
+ name: 'Text',
289
+ value: 'text',
290
+ },
291
+ {
292
+ name: 'Image',
293
+ value: 'image',
294
+ },
295
+ {
296
+ name: 'Video',
297
+ value: 'video',
298
+ },
299
+ {
300
+ name: 'Audio',
301
+ value: 'audio',
302
+ },
303
+ {
304
+ name: 'File',
305
+ value: 'file',
306
+ },
307
+ ],
308
+ default: 'text',
309
+ description: 'The type of message to send (Messenger)',
310
+ },
311
+ // Text Content
181
312
  {
182
313
  displayName: 'Message Content',
183
314
  name: 'content',
@@ -195,19 +326,23 @@ class KirimChat {
195
326
  rows: 4,
196
327
  },
197
328
  },
329
+ // Media URL (for all media types)
198
330
  {
199
331
  displayName: 'Media URL',
200
332
  name: 'mediaUrl',
201
333
  type: 'string',
334
+ required: true,
202
335
  displayOptions: {
203
336
  show: {
204
337
  operation: ['sendMessage'],
205
- messageType: ['image', 'document', 'audio', 'video', 'media_share'],
338
+ messageType: ['image', 'document', 'audio', 'video', 'media_share', 'file'],
206
339
  },
207
340
  },
208
341
  default: '',
209
- description: 'URL of the media file to send',
342
+ description: 'URL of the media file to send (must be publicly accessible)',
343
+ placeholder: 'https://example.com/image.jpg',
210
344
  },
345
+ // Caption (for media messages)
211
346
  {
212
347
  displayName: 'Caption',
213
348
  name: 'caption',
@@ -221,6 +356,7 @@ class KirimChat {
221
356
  default: '',
222
357
  description: 'Optional caption for media messages',
223
358
  },
359
+ // Filename (for documents)
224
360
  {
225
361
  displayName: 'Filename',
226
362
  name: 'filename',
@@ -228,14 +364,16 @@ class KirimChat {
228
364
  displayOptions: {
229
365
  show: {
230
366
  operation: ['sendMessage'],
231
- messageType: ['document'],
367
+ messageType: ['document', 'file'],
232
368
  },
233
369
  },
234
370
  default: '',
235
- description: 'Filename for document messages',
371
+ description: 'Filename for document/file messages',
236
372
  placeholder: 'invoice.pdf',
237
373
  },
238
- // Template fields (WhatsApp only)
374
+ // ============================================
375
+ // TEMPLATE FIELDS (WhatsApp only)
376
+ // ============================================
239
377
  {
240
378
  displayName: 'Template Name',
241
379
  name: 'templateName',
@@ -279,7 +417,130 @@ class KirimChat {
279
417
  default: '[]',
280
418
  description: 'Template components as JSON array (for variables, buttons, etc.)',
281
419
  },
282
- // Mark as Read Fields
420
+ // ============================================
421
+ // INTERACTIVE MESSAGE FIELDS (WhatsApp only)
422
+ // ============================================
423
+ {
424
+ displayName: 'Interactive Type',
425
+ name: 'interactiveType',
426
+ type: 'options',
427
+ required: true,
428
+ displayOptions: {
429
+ show: {
430
+ operation: ['sendMessage'],
431
+ messageType: ['interactive'],
432
+ },
433
+ },
434
+ options: [
435
+ {
436
+ name: 'CTA URL Button',
437
+ value: 'cta_url',
438
+ description: 'Call-to-action button that opens a URL',
439
+ },
440
+ {
441
+ name: 'Reply Buttons',
442
+ value: 'button',
443
+ description: 'Up to 3 reply buttons',
444
+ },
445
+ ],
446
+ default: 'cta_url',
447
+ description: 'Type of interactive message',
448
+ },
449
+ {
450
+ displayName: 'Body Text',
451
+ name: 'interactiveBody',
452
+ type: 'string',
453
+ required: true,
454
+ displayOptions: {
455
+ show: {
456
+ operation: ['sendMessage'],
457
+ messageType: ['interactive'],
458
+ },
459
+ },
460
+ default: '',
461
+ description: 'Main body text of the interactive message (max 1024 characters)',
462
+ typeOptions: {
463
+ rows: 3,
464
+ },
465
+ },
466
+ {
467
+ displayName: 'Header Text',
468
+ name: 'interactiveHeader',
469
+ type: 'string',
470
+ displayOptions: {
471
+ show: {
472
+ operation: ['sendMessage'],
473
+ messageType: ['interactive'],
474
+ },
475
+ },
476
+ default: '',
477
+ description: 'Optional header text (max 60 characters)',
478
+ },
479
+ {
480
+ displayName: 'Footer Text',
481
+ name: 'interactiveFooter',
482
+ type: 'string',
483
+ displayOptions: {
484
+ show: {
485
+ operation: ['sendMessage'],
486
+ messageType: ['interactive'],
487
+ },
488
+ },
489
+ default: '',
490
+ description: 'Optional footer text (max 60 characters)',
491
+ },
492
+ // CTA URL specific fields
493
+ {
494
+ displayName: 'Button Text',
495
+ name: 'ctaButtonText',
496
+ type: 'string',
497
+ required: true,
498
+ displayOptions: {
499
+ show: {
500
+ operation: ['sendMessage'],
501
+ messageType: ['interactive'],
502
+ interactiveType: ['cta_url'],
503
+ },
504
+ },
505
+ default: '',
506
+ description: 'Text displayed on the button (max 20 characters)',
507
+ placeholder: 'Visit Website',
508
+ },
509
+ {
510
+ displayName: 'Button URL',
511
+ name: 'ctaButtonUrl',
512
+ type: 'string',
513
+ required: true,
514
+ displayOptions: {
515
+ show: {
516
+ operation: ['sendMessage'],
517
+ messageType: ['interactive'],
518
+ interactiveType: ['cta_url'],
519
+ },
520
+ },
521
+ default: '',
522
+ description: 'URL to open when button is clicked',
523
+ placeholder: 'https://example.com',
524
+ },
525
+ // Reply buttons specific fields
526
+ {
527
+ displayName: 'Buttons (JSON)',
528
+ name: 'replyButtons',
529
+ type: 'json',
530
+ required: true,
531
+ displayOptions: {
532
+ show: {
533
+ operation: ['sendMessage'],
534
+ messageType: ['interactive'],
535
+ interactiveType: ['button'],
536
+ },
537
+ },
538
+ default: '[{"id": "btn1", "title": "Yes"}, {"id": "btn2", "title": "No"}]',
539
+ description: 'Array of buttons (1-3). Each button needs "id" (max 256 chars) and "title" (max 20 chars)',
540
+ },
541
+ // ============================================
542
+ // MARK AS READ FIELDS
543
+ // ============================================
283
544
  {
284
545
  displayName: 'Message ID',
285
546
  name: 'messageId',
@@ -294,32 +555,99 @@ class KirimChat {
294
555
  description: 'The ID of the message to mark as read',
295
556
  placeholder: 'msg_xyz789',
296
557
  },
297
- // Send Typing Fields
558
+ // ============================================
559
+ // SEND TYPING FIELDS
560
+ // ============================================
561
+ {
562
+ displayName: 'Customer Lookup',
563
+ name: 'typingCustomerLookup',
564
+ type: 'options',
565
+ required: true,
566
+ displayOptions: {
567
+ show: {
568
+ operation: ['sendTyping'],
569
+ },
570
+ },
571
+ options: [
572
+ {
573
+ name: 'Customer ID',
574
+ value: 'customer_id',
575
+ description: 'Lookup by KirimChat customer ID',
576
+ },
577
+ {
578
+ name: 'Phone Number',
579
+ value: 'phone_number',
580
+ description: 'Lookup by phone number',
581
+ },
582
+ {
583
+ name: 'Instagram Username',
584
+ value: 'instagram_username',
585
+ description: 'Lookup by Instagram username',
586
+ },
587
+ ],
588
+ default: 'customer_id',
589
+ description: 'How to identify the customer',
590
+ },
298
591
  {
299
592
  displayName: 'Customer ID',
300
- name: 'customerId',
593
+ name: 'typingCustomerId',
301
594
  type: 'string',
302
595
  required: true,
303
596
  displayOptions: {
304
597
  show: {
305
598
  operation: ['sendTyping'],
599
+ typingCustomerLookup: ['customer_id'],
306
600
  },
307
601
  },
308
602
  default: '',
309
603
  description: 'The ID of the customer to send typing indicator to',
310
604
  placeholder: 'cust_abc123',
311
605
  },
606
+ {
607
+ displayName: 'Phone Number',
608
+ name: 'typingPhoneNumber',
609
+ type: 'string',
610
+ required: true,
611
+ displayOptions: {
612
+ show: {
613
+ operation: ['sendTyping'],
614
+ typingCustomerLookup: ['phone_number'],
615
+ },
616
+ },
617
+ default: '',
618
+ description: 'The phone number of the customer',
619
+ placeholder: '+6281234567890',
620
+ },
621
+ {
622
+ displayName: 'Instagram Username',
623
+ name: 'typingInstagramUsername',
624
+ type: 'string',
625
+ required: true,
626
+ displayOptions: {
627
+ show: {
628
+ operation: ['sendTyping'],
629
+ typingCustomerLookup: ['instagram_username'],
630
+ },
631
+ },
632
+ default: '',
633
+ description: 'The Instagram username of the customer',
634
+ placeholder: 'username',
635
+ },
312
636
  {
313
637
  displayName: 'Channel',
314
- name: 'channel',
638
+ name: 'typingChannel',
315
639
  type: 'options',
316
- required: true,
317
640
  displayOptions: {
318
641
  show: {
319
642
  operation: ['sendTyping'],
320
643
  },
321
644
  },
322
645
  options: [
646
+ {
647
+ name: 'Auto-detect',
648
+ value: '',
649
+ description: 'Automatically detect based on customer data',
650
+ },
323
651
  {
324
652
  name: 'WhatsApp',
325
653
  value: 'whatsapp',
@@ -329,8 +657,8 @@ class KirimChat {
329
657
  value: 'instagram',
330
658
  },
331
659
  ],
332
- default: 'whatsapp',
333
- description: 'The messaging channel to use',
660
+ default: '',
661
+ description: 'The messaging channel to use (leave empty for auto-detect)',
334
662
  },
335
663
  ],
336
664
  };
@@ -345,15 +673,27 @@ class KirimChat {
345
673
  const operation = this.getNodeParameter('operation', i);
346
674
  let responseData;
347
675
  if (operation === 'sendMessage') {
348
- // Send Message Operation
349
- const customerId = this.getNodeParameter('customerId', i);
676
+ // ============================================
677
+ // SEND MESSAGE OPERATION
678
+ // ============================================
679
+ const customerLookup = this.getNodeParameter('customerLookup', i);
350
680
  const channel = this.getNodeParameter('channel', i);
351
681
  const messageType = this.getNodeParameter('messageType', i);
352
682
  const body = {
353
- customer_id: customerId,
354
683
  channel,
355
684
  message_type: messageType,
356
685
  };
686
+ // Set customer identifier based on lookup method
687
+ if (customerLookup === 'customer_id') {
688
+ body.customer_id = this.getNodeParameter('customerId', i);
689
+ }
690
+ else if (customerLookup === 'phone_number') {
691
+ body.phone_number = this.getNodeParameter('phoneNumber', i);
692
+ }
693
+ else if (customerLookup === 'instagram_username') {
694
+ body.instagram_username = this.getNodeParameter('instagramUsername', i);
695
+ }
696
+ // Handle different message types
357
697
  if (messageType === 'text') {
358
698
  body.content = this.getNodeParameter('content', i);
359
699
  }
@@ -379,24 +719,90 @@ class KirimChat {
379
719
  components: templateComponents,
380
720
  };
381
721
  }
722
+ else if (messageType === 'interactive') {
723
+ // WhatsApp interactive message
724
+ const interactiveType = this.getNodeParameter('interactiveType', i);
725
+ const interactiveBody = this.getNodeParameter('interactiveBody', i);
726
+ const interactiveHeader = this.getNodeParameter('interactiveHeader', i, '');
727
+ const interactiveFooter = this.getNodeParameter('interactiveFooter', i, '');
728
+ const interactive = {
729
+ type: interactiveType,
730
+ body: { text: interactiveBody },
731
+ };
732
+ // Add optional header
733
+ if (interactiveHeader) {
734
+ interactive.header = {
735
+ type: 'text',
736
+ text: interactiveHeader,
737
+ };
738
+ }
739
+ // Add optional footer
740
+ if (interactiveFooter) {
741
+ interactive.footer = { text: interactiveFooter };
742
+ }
743
+ // Add action based on type
744
+ if (interactiveType === 'cta_url') {
745
+ const ctaButtonText = this.getNodeParameter('ctaButtonText', i);
746
+ const ctaButtonUrl = this.getNodeParameter('ctaButtonUrl', i);
747
+ if (!isValidUrl(ctaButtonUrl)) {
748
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid Button URL format: "${ctaButtonUrl}". URL must start with http:// or https://`, { itemIndex: i });
749
+ }
750
+ interactive.action = {
751
+ name: 'cta_url',
752
+ parameters: {
753
+ display_text: ctaButtonText,
754
+ url: ctaButtonUrl,
755
+ },
756
+ };
757
+ }
758
+ else if (interactiveType === 'button') {
759
+ const replyButtonsJson = this.getNodeParameter('replyButtons', i);
760
+ let replyButtons;
761
+ try {
762
+ replyButtons = JSON.parse(replyButtonsJson);
763
+ }
764
+ catch (error) {
765
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid JSON in Reply Buttons: ${error instanceof Error ? error.message : 'Parse failed'}`, { itemIndex: i });
766
+ }
767
+ if (!Array.isArray(replyButtons) || replyButtons.length === 0 || replyButtons.length > 3) {
768
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Reply Buttons must be a JSON array with 1-3 buttons. Example: [{"id": "btn1", "title": "Yes"}]', { itemIndex: i });
769
+ }
770
+ // Transform to n8n format
771
+ const formattedButtons = replyButtons.map((btn) => ({
772
+ type: 'reply',
773
+ reply: {
774
+ id: btn.id || '',
775
+ title: btn.title || '',
776
+ },
777
+ }));
778
+ interactive.action = {
779
+ buttons: formattedButtons,
780
+ };
781
+ }
782
+ if (!validateInteractiveMessage(interactive)) {
783
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid interactive message structure', { itemIndex: i });
784
+ }
785
+ body.interactive = interactive;
786
+ }
382
787
  else {
383
- // Media messages
384
- const mediaUrl = this.getNodeParameter('mediaUrl', i, '');
788
+ // Media messages (image, document, audio, video, media_share, file)
789
+ const mediaUrl = this.getNodeParameter('mediaUrl', i);
385
790
  // Validate media URL is provided
386
791
  if (!mediaUrl) {
387
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Media URL is required for media messages (image, document, audio, video, media_share)', { itemIndex: i });
792
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Media URL is required for ${messageType} messages`, { itemIndex: i });
388
793
  }
389
794
  // Validate media URL format
390
795
  if (!isValidUrl(mediaUrl)) {
391
796
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid Media URL format: "${mediaUrl}". URL must start with http:// or https://`, { itemIndex: i });
392
797
  }
393
798
  body.media_url = mediaUrl;
799
+ // Add caption if provided
394
800
  const caption = this.getNodeParameter('caption', i, '');
395
801
  if (caption) {
396
802
  body.caption = caption;
397
803
  }
398
- // Document filename
399
- if (messageType === 'document') {
804
+ // Add filename for document/file types
805
+ if (messageType === 'document' || messageType === 'file') {
400
806
  const filename = this.getNodeParameter('filename', i, '');
401
807
  if (filename) {
402
808
  body.filename = filename;
@@ -411,7 +817,9 @@ class KirimChat {
411
817
  });
412
818
  }
413
819
  else if (operation === 'markAsRead') {
414
- // Mark as Read Operation
820
+ // ============================================
821
+ // MARK AS READ OPERATION
822
+ // ============================================
415
823
  const messageId = this.getNodeParameter('messageId', i);
416
824
  responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'kirimChatApi', {
417
825
  method: 'POST',
@@ -420,13 +828,30 @@ class KirimChat {
420
828
  });
421
829
  }
422
830
  else if (operation === 'sendTyping') {
423
- // Send Typing Indicator Operation
424
- const customerId = this.getNodeParameter('customerId', i);
425
- const channel = this.getNodeParameter('channel', i);
831
+ // ============================================
832
+ // SEND TYPING INDICATOR OPERATION
833
+ // ============================================
834
+ const typingLookup = this.getNodeParameter('typingCustomerLookup', i);
835
+ const typingChannel = this.getNodeParameter('typingChannel', i, '');
836
+ // Get customer identifier based on lookup method
837
+ let customerIdentifier;
838
+ if (typingLookup === 'customer_id') {
839
+ customerIdentifier = this.getNodeParameter('typingCustomerId', i);
840
+ }
841
+ else if (typingLookup === 'phone_number') {
842
+ customerIdentifier = this.getNodeParameter('typingPhoneNumber', i);
843
+ }
844
+ else {
845
+ customerIdentifier = this.getNodeParameter('typingInstagramUsername', i);
846
+ }
847
+ const typingBody = {};
848
+ if (typingChannel) {
849
+ typingBody.channel = typingChannel;
850
+ }
426
851
  responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'kirimChatApi', {
427
852
  method: 'POST',
428
- url: `${baseUrl}/conversations/${customerId}/typing`,
429
- body: { channel },
853
+ url: `${baseUrl}/conversations/${customerIdentifier}/typing`,
854
+ body: typingBody,
430
855
  json: true,
431
856
  });
432
857
  }
package/package.json CHANGED
@@ -1,16 +1,19 @@
1
- {
2
- "name": "@kichat/n8n-nodes-kirimchat",
3
- "version": "1.0.5",
4
- "description": "n8n community node for KirimChat - Send WhatsApp & Instagram messages, mark as read, and send typing indicators",
5
- "keywords": [
6
- "n8n-community-node-package",
7
- "n8n",
8
- "kirimchat",
9
- "whatsapp",
10
- "instagram",
11
- "messaging",
12
- "chat"
13
- ],
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
+ ],
14
17
  "license": "MIT",
15
18
  "homepage": "https://kirim.chat",
16
19
  "author": {