@kichat/n8n-nodes-kirimchat 1.0.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 ADDED
@@ -0,0 +1,145 @@
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 `n8n-nodes-kirimchat` and confirm
18
+
19
+ ### Manual Installation
20
+
21
+ ```bash
22
+ npm install n8n-nodes-kirimchat
23
+ ```
24
+
25
+ ## Credentials
26
+
27
+ You need a KirimChat API key to use this node:
28
+
29
+ 1. Log in to your KirimChat dashboard
30
+ 2. Go to **Settings > Developers > API Keys**
31
+ 3. Click **Create API Key** and copy the key (shown only once)
32
+ 4. In n8n, create new credentials for **KirimChat API**
33
+ 5. Paste your API key (starts with `kc_live_`)
34
+
35
+ ## Operations
36
+
37
+ ### Send Message
38
+
39
+ Send a message to a customer via WhatsApp or Instagram.
40
+
41
+ | Parameter | Description |
42
+ |-----------|-------------|
43
+ | Customer ID | The ID of the customer (e.g., `cust_abc123`) |
44
+ | Channel | `whatsapp` or `instagram` |
45
+ | Message Type | `text`, `image`, `document`, `audio`, `video`, `template` (WhatsApp) or `text`, `image`, `media_share` (Instagram) |
46
+ | Content | Text content for text messages |
47
+ | Media URL | URL of media file for media messages |
48
+ | Caption | Optional caption for media messages |
49
+
50
+ **Example Response:**
51
+ ```json
52
+ {
53
+ "success": true,
54
+ "data": {
55
+ "message_id": "msg_xyz789",
56
+ "status": "sent",
57
+ "channel": "whatsapp",
58
+ "timestamp": "2025-11-26T10:31:00.000Z"
59
+ }
60
+ }
61
+ ```
62
+
63
+ ### Mark as Read
64
+
65
+ Mark a message as read and send read receipt to the customer.
66
+
67
+ | Parameter | Description |
68
+ |-----------|-------------|
69
+ | Message ID | The ID of the message to mark as read (e.g., `msg_xyz789`) |
70
+
71
+ ### Send Typing Indicator
72
+
73
+ Show typing indicator to a customer before sending a message.
74
+
75
+ | Parameter | Description |
76
+ |-----------|-------------|
77
+ | Customer ID | The ID of the customer |
78
+ | Channel | `whatsapp` or `instagram` |
79
+
80
+ > **Note:** Rate limited to 1 request per customer per 3 seconds.
81
+
82
+ ## Webhook Trigger
83
+
84
+ KirimChat supports outbound webhooks for real-time event notifications. Configure webhooks in your KirimChat dashboard under **Settings > Developers > Webhooks**.
85
+
86
+ ### Supported Events
87
+
88
+ - `message.received` - New message from customer
89
+ - `message.sent` - Message sent to customer
90
+ - `message.delivered` - Message delivered
91
+ - `message.read` - Message read by customer
92
+ - `message.failed` - Message delivery failed
93
+
94
+ ### Webhook Payload Example
95
+
96
+ ```json
97
+ {
98
+ "event_type": "message.received",
99
+ "event_id": "evt_abc123",
100
+ "timestamp": "2025-11-26T10:30:00.000Z",
101
+ "data": {
102
+ "message_id": "msg_xyz789",
103
+ "customer_id": "cust_123",
104
+ "customer_phone": "+6281234567890",
105
+ "direction": "inbound",
106
+ "message_type": "text",
107
+ "content": "Hello!",
108
+ "channel": "whatsapp"
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### Using with n8n Webhook Node
114
+
115
+ 1. Create a new workflow with **Webhook** trigger node
116
+ 2. Copy the webhook URL
117
+ 3. In KirimChat, create a webhook endpoint with this URL
118
+ 4. Select the events you want to receive
119
+ 5. Connect the webhook to your KirimChat node for automated responses
120
+
121
+ ## Rate Limits
122
+
123
+ - **Global:** 100 requests per minute per API key
124
+ - **Send Message:** 60 messages per minute per API key
125
+ - **Typing Indicator:** 1 request per customer per 3 seconds
126
+
127
+ ## Error Handling
128
+
129
+ The node returns standard error responses:
130
+
131
+ | Code | Description |
132
+ |------|-------------|
133
+ | 401 | Invalid or expired API key |
134
+ | 404 | Resource not found |
135
+ | 400 | Invalid request or messaging window closed |
136
+ | 429 | Rate limit exceeded |
137
+
138
+ ## Resources
139
+
140
+ - [KirimChat Documentation](https://kirim.chat/developers)
141
+ - [n8n Community Nodes](https://docs.n8n.io/integrations/community-nodes/)
142
+
143
+ ## License
144
+
145
+ MIT
@@ -0,0 +1,9 @@
1
+ import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class KirimChatApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ properties: INodeProperties[];
7
+ authenticate: IAuthenticateGeneric;
8
+ test: ICredentialTestRequest;
9
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KirimChatApi = void 0;
4
+ class KirimChatApi {
5
+ constructor() {
6
+ this.name = 'kirimChatApi';
7
+ this.displayName = 'KirimChat API';
8
+ this.documentationUrl = 'https://kirim.chat/developers';
9
+ this.properties = [
10
+ {
11
+ displayName: 'API Key',
12
+ name: 'apiKey',
13
+ type: 'string',
14
+ typeOptions: { password: true },
15
+ default: '',
16
+ required: true,
17
+ description: 'Your KirimChat API key (starts with kc_live_)',
18
+ placeholder: 'kc_live_your_api_key_here',
19
+ },
20
+ {
21
+ displayName: 'API Base URL',
22
+ name: 'baseUrl',
23
+ type: 'string',
24
+ default: 'https://api.paijoe.com/api/v1/public',
25
+ required: true,
26
+ description: 'The base URL for the KirimChat API',
27
+ },
28
+ ];
29
+ this.authenticate = {
30
+ type: 'generic',
31
+ properties: {
32
+ headers: {
33
+ Authorization: '=Bearer {{$credentials.apiKey}}',
34
+ },
35
+ },
36
+ };
37
+ this.test = {
38
+ request: {
39
+ baseURL: '={{$credentials.baseUrl}}',
40
+ url: '/health',
41
+ method: 'GET',
42
+ },
43
+ };
44
+ }
45
+ }
46
+ exports.KirimChatApi = KirimChatApi;
@@ -0,0 +1,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class KirimChat implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,424 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KirimChat = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ class KirimChat {
6
+ constructor() {
7
+ this.description = {
8
+ displayName: 'KirimChat',
9
+ name: 'kirimChat',
10
+ icon: 'file:kirimchat.svg',
11
+ group: ['transform'],
12
+ version: 1,
13
+ subtitle: '={{$parameter["operation"]}}',
14
+ description: 'Send WhatsApp & Instagram messages, mark as read, and send typing indicators',
15
+ defaults: {
16
+ name: 'KirimChat',
17
+ },
18
+ inputs: ['main'],
19
+ outputs: ['main'],
20
+ credentials: [
21
+ {
22
+ name: 'kirimChatApi',
23
+ required: true,
24
+ },
25
+ ],
26
+ properties: [
27
+ {
28
+ displayName: 'Operation',
29
+ name: 'operation',
30
+ type: 'options',
31
+ noDataExpression: true,
32
+ options: [
33
+ {
34
+ name: 'Send Message',
35
+ value: 'sendMessage',
36
+ description: 'Send a message to a customer via WhatsApp or Instagram',
37
+ action: 'Send a message',
38
+ },
39
+ {
40
+ name: 'Mark as Read',
41
+ value: 'markAsRead',
42
+ description: 'Mark a message as read',
43
+ action: 'Mark a message as read',
44
+ },
45
+ {
46
+ name: 'Send Typing Indicator',
47
+ value: 'sendTyping',
48
+ description: 'Send typing indicator to show you are typing',
49
+ action: 'Send typing indicator',
50
+ },
51
+ ],
52
+ default: 'sendMessage',
53
+ },
54
+ // Send Message Fields
55
+ {
56
+ displayName: 'Customer ID',
57
+ name: 'customerId',
58
+ type: 'string',
59
+ required: true,
60
+ displayOptions: {
61
+ show: {
62
+ operation: ['sendMessage'],
63
+ },
64
+ },
65
+ default: '',
66
+ description: 'The ID of the customer to send the message to',
67
+ placeholder: 'cust_abc123',
68
+ },
69
+ {
70
+ displayName: 'Channel',
71
+ name: 'channel',
72
+ type: 'options',
73
+ required: true,
74
+ displayOptions: {
75
+ show: {
76
+ operation: ['sendMessage'],
77
+ },
78
+ },
79
+ options: [
80
+ {
81
+ name: 'WhatsApp',
82
+ value: 'whatsapp',
83
+ },
84
+ {
85
+ name: 'Instagram',
86
+ value: 'instagram',
87
+ },
88
+ ],
89
+ default: 'whatsapp',
90
+ description: 'The messaging channel to use',
91
+ },
92
+ {
93
+ displayName: 'Message Type',
94
+ name: 'messageType',
95
+ type: 'options',
96
+ required: true,
97
+ displayOptions: {
98
+ show: {
99
+ operation: ['sendMessage'],
100
+ channel: ['whatsapp'],
101
+ },
102
+ },
103
+ options: [
104
+ {
105
+ name: 'Text',
106
+ value: 'text',
107
+ },
108
+ {
109
+ name: 'Image',
110
+ value: 'image',
111
+ },
112
+ {
113
+ name: 'Document',
114
+ value: 'document',
115
+ },
116
+ {
117
+ name: 'Audio',
118
+ value: 'audio',
119
+ },
120
+ {
121
+ name: 'Video',
122
+ value: 'video',
123
+ },
124
+ {
125
+ name: 'Template',
126
+ value: 'template',
127
+ },
128
+ ],
129
+ default: 'text',
130
+ description: 'The type of message to send (WhatsApp)',
131
+ },
132
+ {
133
+ displayName: 'Message Type',
134
+ name: 'messageType',
135
+ type: 'options',
136
+ required: true,
137
+ displayOptions: {
138
+ show: {
139
+ operation: ['sendMessage'],
140
+ channel: ['instagram'],
141
+ },
142
+ },
143
+ options: [
144
+ {
145
+ name: 'Text',
146
+ value: 'text',
147
+ },
148
+ {
149
+ name: 'Image',
150
+ value: 'image',
151
+ },
152
+ {
153
+ name: 'Media Share',
154
+ value: 'media_share',
155
+ },
156
+ ],
157
+ default: 'text',
158
+ description: 'The type of message to send (Instagram)',
159
+ },
160
+ {
161
+ displayName: 'Message Content',
162
+ name: 'content',
163
+ type: 'string',
164
+ required: true,
165
+ displayOptions: {
166
+ show: {
167
+ operation: ['sendMessage'],
168
+ messageType: ['text'],
169
+ },
170
+ },
171
+ default: '',
172
+ description: 'The text content of the message',
173
+ typeOptions: {
174
+ rows: 4,
175
+ },
176
+ },
177
+ {
178
+ displayName: 'Media URL',
179
+ name: 'mediaUrl',
180
+ type: 'string',
181
+ displayOptions: {
182
+ show: {
183
+ operation: ['sendMessage'],
184
+ messageType: ['image', 'document', 'audio', 'video', 'media_share'],
185
+ },
186
+ },
187
+ default: '',
188
+ description: 'URL of the media file to send',
189
+ },
190
+ {
191
+ displayName: 'Caption',
192
+ name: 'caption',
193
+ type: 'string',
194
+ displayOptions: {
195
+ show: {
196
+ operation: ['sendMessage'],
197
+ messageType: ['image', 'document', 'video'],
198
+ },
199
+ },
200
+ default: '',
201
+ description: 'Optional caption for media messages',
202
+ },
203
+ {
204
+ displayName: 'Filename',
205
+ name: 'filename',
206
+ type: 'string',
207
+ displayOptions: {
208
+ show: {
209
+ operation: ['sendMessage'],
210
+ messageType: ['document'],
211
+ },
212
+ },
213
+ default: '',
214
+ description: 'Filename for document messages',
215
+ placeholder: 'invoice.pdf',
216
+ },
217
+ // Template fields (WhatsApp only)
218
+ {
219
+ displayName: 'Template Name',
220
+ name: 'templateName',
221
+ type: 'string',
222
+ required: true,
223
+ displayOptions: {
224
+ show: {
225
+ operation: ['sendMessage'],
226
+ messageType: ['template'],
227
+ },
228
+ },
229
+ default: '',
230
+ description: 'Name of the WhatsApp template to send',
231
+ placeholder: 'hello_world',
232
+ },
233
+ {
234
+ displayName: 'Template Language',
235
+ name: 'templateLanguage',
236
+ type: 'string',
237
+ required: true,
238
+ displayOptions: {
239
+ show: {
240
+ operation: ['sendMessage'],
241
+ messageType: ['template'],
242
+ },
243
+ },
244
+ default: 'en',
245
+ description: 'Language code for the template',
246
+ placeholder: 'en',
247
+ },
248
+ {
249
+ displayName: 'Template Components (JSON)',
250
+ name: 'templateComponents',
251
+ type: 'json',
252
+ displayOptions: {
253
+ show: {
254
+ operation: ['sendMessage'],
255
+ messageType: ['template'],
256
+ },
257
+ },
258
+ default: '[]',
259
+ description: 'Template components as JSON array (for variables, buttons, etc.)',
260
+ },
261
+ // Mark as Read Fields
262
+ {
263
+ displayName: 'Message ID',
264
+ name: 'messageId',
265
+ type: 'string',
266
+ required: true,
267
+ displayOptions: {
268
+ show: {
269
+ operation: ['markAsRead'],
270
+ },
271
+ },
272
+ default: '',
273
+ description: 'The ID of the message to mark as read',
274
+ placeholder: 'msg_xyz789',
275
+ },
276
+ // Send Typing Fields
277
+ {
278
+ displayName: 'Customer ID',
279
+ name: 'customerId',
280
+ type: 'string',
281
+ required: true,
282
+ displayOptions: {
283
+ show: {
284
+ operation: ['sendTyping'],
285
+ },
286
+ },
287
+ default: '',
288
+ description: 'The ID of the customer to send typing indicator to',
289
+ placeholder: 'cust_abc123',
290
+ },
291
+ {
292
+ displayName: 'Channel',
293
+ name: 'channel',
294
+ type: 'options',
295
+ required: true,
296
+ displayOptions: {
297
+ show: {
298
+ operation: ['sendTyping'],
299
+ },
300
+ },
301
+ options: [
302
+ {
303
+ name: 'WhatsApp',
304
+ value: 'whatsapp',
305
+ },
306
+ {
307
+ name: 'Instagram',
308
+ value: 'instagram',
309
+ },
310
+ ],
311
+ default: 'whatsapp',
312
+ description: 'The messaging channel to use',
313
+ },
314
+ ],
315
+ };
316
+ }
317
+ async execute() {
318
+ const items = this.getInputData();
319
+ const returnData = [];
320
+ const credentials = await this.getCredentials('kirimChatApi');
321
+ const baseUrl = credentials.baseUrl;
322
+ for (let i = 0; i < items.length; i++) {
323
+ try {
324
+ const operation = this.getNodeParameter('operation', i);
325
+ let responseData;
326
+ if (operation === 'sendMessage') {
327
+ // Send Message Operation
328
+ const customerId = this.getNodeParameter('customerId', i);
329
+ const channel = this.getNodeParameter('channel', i);
330
+ const messageType = this.getNodeParameter('messageType', i);
331
+ const body = {
332
+ customer_id: customerId,
333
+ channel,
334
+ message_type: messageType,
335
+ };
336
+ if (messageType === 'text') {
337
+ body.content = this.getNodeParameter('content', i);
338
+ }
339
+ else if (messageType === 'template') {
340
+ // WhatsApp template message
341
+ const templateName = this.getNodeParameter('templateName', i);
342
+ const templateLanguage = this.getNodeParameter('templateLanguage', i);
343
+ const templateComponentsJson = this.getNodeParameter('templateComponents', i, '[]');
344
+ let templateComponents = [];
345
+ try {
346
+ templateComponents = JSON.parse(templateComponentsJson);
347
+ }
348
+ catch {
349
+ // Keep empty array if invalid JSON
350
+ }
351
+ body.template = {
352
+ name: templateName,
353
+ language: { code: templateLanguage },
354
+ components: templateComponents,
355
+ };
356
+ }
357
+ else {
358
+ // Media messages
359
+ body.media_url = this.getNodeParameter('mediaUrl', i, '');
360
+ const caption = this.getNodeParameter('caption', i, '');
361
+ if (caption) {
362
+ body.caption = caption;
363
+ }
364
+ // Document filename
365
+ if (messageType === 'document') {
366
+ const filename = this.getNodeParameter('filename', i, '');
367
+ if (filename) {
368
+ body.filename = filename;
369
+ }
370
+ }
371
+ }
372
+ responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'kirimChatApi', {
373
+ method: 'POST',
374
+ url: `${baseUrl}/messages/send`,
375
+ body,
376
+ json: true,
377
+ });
378
+ }
379
+ else if (operation === 'markAsRead') {
380
+ // Mark as Read Operation
381
+ const messageId = this.getNodeParameter('messageId', i);
382
+ responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'kirimChatApi', {
383
+ method: 'POST',
384
+ url: `${baseUrl}/messages/${messageId}/read`,
385
+ json: true,
386
+ });
387
+ }
388
+ else if (operation === 'sendTyping') {
389
+ // Send Typing Indicator Operation
390
+ const customerId = this.getNodeParameter('customerId', i);
391
+ const channel = this.getNodeParameter('channel', i);
392
+ responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'kirimChatApi', {
393
+ method: 'POST',
394
+ url: `${baseUrl}/conversations/${customerId}/typing`,
395
+ body: { channel },
396
+ json: true,
397
+ });
398
+ }
399
+ else {
400
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The operation "${operation}" is not supported!`);
401
+ }
402
+ const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
403
+ returnData.push(...executionData);
404
+ }
405
+ catch (error) {
406
+ if (this.continueOnFail()) {
407
+ const errorMessage = error instanceof Error ? error.message : String(error);
408
+ returnData.push({
409
+ json: {
410
+ error: errorMessage,
411
+ },
412
+ pairedItem: {
413
+ item: i,
414
+ },
415
+ });
416
+ continue;
417
+ }
418
+ throw error;
419
+ }
420
+ }
421
+ return [returnData];
422
+ }
423
+ }
424
+ exports.KirimChat = KirimChat;
@@ -0,0 +1,6 @@
1
+ <svg width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- Chat Bubble - White background with Black border -->
3
+ <path d="M3,9 Q3,3 9,3 H51 Q57,3 57,9 V42 Q57,48 51,48 H18 L3,57 V9 Z" fill="white" stroke="black" stroke-width="3"/>
4
+ <!-- Text - Black color -->
5
+ <text x="30" y="30" font-family="Arial, sans-serif" font-size="24" font-weight="900" fill="black" text-anchor="middle" dominant-baseline="middle">KC</text>
6
+ </svg>
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@kichat/n8n-nodes-kirimchat",
3
+ "version": "1.0.0",
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
+ ],
14
+ "license": "MIT",
15
+ "homepage": "https://kirim.chat",
16
+ "author": {
17
+ "name": "KirimChat",
18
+ "email": "support@kirim.chat"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/kirimchat/n8n-nodes-kirimchat.git"
23
+ },
24
+ "main": "dist/nodes/KirimChat/KirimChat.node.js",
25
+ "scripts": {
26
+ "build": "tsc && npm run copy-icons",
27
+ "copy-icons": "node -e \"require('fs').cpSync('nodes/KirimChat/kirimchat.svg', 'dist/nodes/KirimChat/kirimchat.svg')\"",
28
+ "dev": "tsc --watch",
29
+ "format": "prettier nodes credentials --write",
30
+ "lint": "eslint nodes credentials package.json",
31
+ "prepublishOnly": "npm run build"
32
+ },
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "n8n": {
37
+ "n8nNodesApiVersion": 1,
38
+ "credentials": [
39
+ "dist/credentials/KirimChatApi.credentials.js"
40
+ ],
41
+ "nodes": [
42
+ "dist/nodes/KirimChat/KirimChat.node.js"
43
+ ]
44
+ },
45
+ "devDependencies": {
46
+ "@typescript-eslint/parser": "^7.0.0",
47
+ "copyfiles": "^2.4.1",
48
+ "eslint": "^8.0.0",
49
+ "n8n-workflow": "^1.0.0",
50
+ "prettier": "^3.0.0",
51
+ "typescript": "^5.0.0"
52
+ },
53
+ "peerDependencies": {
54
+ "n8n-workflow": "*"
55
+ }
56
+ }