n8n-nodes-tone 1.0.2
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/dist/credentials/ToneApi.credentials.d.ts +22 -0
- package/dist/credentials/ToneApi.credentials.d.ts.map +1 -0
- package/dist/credentials/ToneApi.credentials.js +44 -0
- package/dist/credentials/ToneApi.credentials.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes/ToneMakeCall/ToneMakeCall.node.d.ts +6 -0
- package/dist/nodes/ToneMakeCall/ToneMakeCall.node.d.ts.map +1 -0
- package/dist/nodes/ToneMakeCall/ToneMakeCall.node.js +110 -0
- package/dist/nodes/ToneMakeCall/ToneMakeCall.node.js.map +1 -0
- package/dist/nodes/ToneSendSms/ToneSendSms.node.d.ts +6 -0
- package/dist/nodes/ToneSendSms/ToneSendSms.node.d.ts.map +1 -0
- package/dist/nodes/ToneSendSms/ToneSendSms.node.js +82 -0
- package/dist/nodes/ToneSendSms/ToneSendSms.node.js.map +1 -0
- package/dist/nodes/ToneStartCampaign/ToneStartCampaign.node.d.ts +6 -0
- package/dist/nodes/ToneStartCampaign/ToneStartCampaign.node.d.ts.map +1 -0
- package/dist/nodes/ToneStartCampaign/ToneStartCampaign.node.js +61 -0
- package/dist/nodes/ToneStartCampaign/ToneStartCampaign.node.js.map +1 -0
- package/dist/nodes/ToneWebhook/ToneWebhook.node.d.ts +13 -0
- package/dist/nodes/ToneWebhook/ToneWebhook.node.d.ts.map +1 -0
- package/dist/nodes/ToneWebhook/ToneWebhook.node.js +129 -0
- package/dist/nodes/ToneWebhook/ToneWebhook.node.js.map +1 -0
- package/package.json +46 -0
- package/src/credentials/ToneApi.credentials.ts +43 -0
- package/src/index.ts +5 -0
- package/src/nodes/ToneMakeCall/ToneMakeCall.node.ts +119 -0
- package/src/nodes/ToneMakeCall/tone.svg +5 -0
- package/src/nodes/ToneSendSms/ToneSendSms.node.ts +91 -0
- package/src/nodes/ToneSendSms/tone.svg +5 -0
- package/src/nodes/ToneStartCampaign/ToneStartCampaign.node.ts +70 -0
- package/src/nodes/ToneStartCampaign/tone.svg +5 -0
- package/src/nodes/ToneWebhook/ToneWebhook.node.ts +147 -0
- package/src/nodes/ToneWebhook/tone.svg +5 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class ToneApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
documentationUrl: string;
|
|
6
|
+
properties: INodeProperties[];
|
|
7
|
+
authenticate: {
|
|
8
|
+
type: "generic";
|
|
9
|
+
properties: {
|
|
10
|
+
headers: {
|
|
11
|
+
Authorization: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
test: {
|
|
16
|
+
request: {
|
|
17
|
+
baseURL: string;
|
|
18
|
+
url: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=ToneApi.credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToneApi.credentials.d.ts","sourceRoot":"","sources":["../../src/credentials/ToneApi.credentials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAEpE,qBAAa,OAAQ,YAAW,eAAe;IAC7C,IAAI,SAAY;IAChB,WAAW,SAAa;IACxB,gBAAgB,SAA0C;IAE1D,UAAU,EAAE,eAAe,EAAE,CAkB5B;IAED,YAAY;;;;;;;MAOX;IAED,IAAI;;;;;MAKH;CACF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToneApi = void 0;
|
|
4
|
+
class ToneApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'toneApi';
|
|
7
|
+
this.displayName = 'Tone API';
|
|
8
|
+
this.documentationUrl = 'https://usetone.ai/docs/api-reference';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'API Key',
|
|
12
|
+
name: 'apiKey',
|
|
13
|
+
type: 'string',
|
|
14
|
+
typeOptions: { password: true },
|
|
15
|
+
default: '',
|
|
16
|
+
placeholder: 'tone_live_...',
|
|
17
|
+
description: 'Your Tone API key. Generate one at https://app.usetone.ai/settings/api-keys',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
displayName: 'Base URL',
|
|
21
|
+
name: 'baseUrl',
|
|
22
|
+
type: 'string',
|
|
23
|
+
default: 'https://tone-production-4819.up.railway.app',
|
|
24
|
+
description: 'Tone API base URL. Leave as default unless using a self-hosted instance.',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
this.authenticate = {
|
|
28
|
+
type: 'generic',
|
|
29
|
+
properties: {
|
|
30
|
+
headers: {
|
|
31
|
+
Authorization: '=Bearer {{$credentials.apiKey}}',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
this.test = {
|
|
36
|
+
request: {
|
|
37
|
+
baseURL: '={{$credentials.baseUrl}}',
|
|
38
|
+
url: '/health',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.ToneApi = ToneApi;
|
|
44
|
+
//# sourceMappingURL=ToneApi.credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToneApi.credentials.js","sourceRoot":"","sources":["../../src/credentials/ToneApi.credentials.ts"],"names":[],"mappings":";;;AAEA,MAAa,OAAO;IAApB;QACE,SAAI,GAAG,SAAS,CAAA;QAChB,gBAAW,GAAG,UAAU,CAAA;QACxB,qBAAgB,GAAG,uCAAuC,CAAA;QAE1D,eAAU,GAAsB;YAC9B;gBACE,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,eAAe;gBAC5B,WAAW,EACT,6EAA6E;aAChF;YACD;gBACE,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,6CAA6C;gBACtD,WAAW,EAAE,0EAA0E;aACxF;SACF,CAAA;QAED,iBAAY,GAAG;YACb,IAAI,EAAE,SAAkB;YACxB,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,aAAa,EAAE,iCAAiC;iBACjD;aACF;SACF,CAAA;QAED,SAAI,GAAG;YACL,OAAO,EAAE;gBACP,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,SAAS;aACf;SACF,CAAA;IACH,CAAC;CAAA;AAxCD,0BAwCC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ToneApi } from './credentials/ToneApi.credentials.js';
|
|
2
|
+
export { ToneWebhook } from './nodes/ToneWebhook/ToneWebhook.node.js';
|
|
3
|
+
export { ToneMakeCall } from './nodes/ToneMakeCall/ToneMakeCall.node.js';
|
|
4
|
+
export { ToneSendSms } from './nodes/ToneSendSms/ToneSendSms.node.js';
|
|
5
|
+
export { ToneStartCampaign } from './nodes/ToneStartCampaign/ToneStartCampaign.node.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAA;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAA;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qDAAqD,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToneStartCampaign = exports.ToneSendSms = exports.ToneMakeCall = exports.ToneWebhook = exports.ToneApi = void 0;
|
|
4
|
+
var ToneApi_credentials_js_1 = require("./credentials/ToneApi.credentials.js");
|
|
5
|
+
Object.defineProperty(exports, "ToneApi", { enumerable: true, get: function () { return ToneApi_credentials_js_1.ToneApi; } });
|
|
6
|
+
var ToneWebhook_node_js_1 = require("./nodes/ToneWebhook/ToneWebhook.node.js");
|
|
7
|
+
Object.defineProperty(exports, "ToneWebhook", { enumerable: true, get: function () { return ToneWebhook_node_js_1.ToneWebhook; } });
|
|
8
|
+
var ToneMakeCall_node_js_1 = require("./nodes/ToneMakeCall/ToneMakeCall.node.js");
|
|
9
|
+
Object.defineProperty(exports, "ToneMakeCall", { enumerable: true, get: function () { return ToneMakeCall_node_js_1.ToneMakeCall; } });
|
|
10
|
+
var ToneSendSms_node_js_1 = require("./nodes/ToneSendSms/ToneSendSms.node.js");
|
|
11
|
+
Object.defineProperty(exports, "ToneSendSms", { enumerable: true, get: function () { return ToneSendSms_node_js_1.ToneSendSms; } });
|
|
12
|
+
var ToneStartCampaign_node_js_1 = require("./nodes/ToneStartCampaign/ToneStartCampaign.node.js");
|
|
13
|
+
Object.defineProperty(exports, "ToneStartCampaign", { enumerable: true, get: function () { return ToneStartCampaign_node_js_1.ToneStartCampaign; } });
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,+EAA8D;AAArD,iHAAA,OAAO,OAAA;AAChB,+EAAqE;AAA5D,kHAAA,WAAW,OAAA;AACpB,kFAAwE;AAA/D,oHAAA,YAAY,OAAA;AACrB,+EAAqE;AAA5D,kHAAA,WAAW,OAAA;AACpB,iGAAuF;AAA9E,8HAAA,iBAAiB,OAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class ToneMakeCall implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=ToneMakeCall.node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToneMakeCall.node.d.ts","sourceRoot":"","sources":["../../../src/nodes/ToneMakeCall/ToneMakeCall.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EAEjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EACrB,MAAM,cAAc,CAAA;AAGrB,qBAAa,YAAa,YAAW,SAAS;IAC5C,WAAW,EAAE,oBAAoB,CA0DhC;IAEK,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CAgDxE"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToneMakeCall = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
class ToneMakeCall {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.description = {
|
|
8
|
+
displayName: 'Tone: Make Call',
|
|
9
|
+
name: 'toneMakeCall',
|
|
10
|
+
icon: 'file:tone.svg',
|
|
11
|
+
group: ['transform'],
|
|
12
|
+
version: 1,
|
|
13
|
+
description: 'Initiate an outbound AI phone call via Tone',
|
|
14
|
+
defaults: { name: 'Tone Make Call' },
|
|
15
|
+
inputs: ['main'],
|
|
16
|
+
outputs: ['main'],
|
|
17
|
+
credentials: [{ name: 'toneApi', required: true }],
|
|
18
|
+
properties: [
|
|
19
|
+
{
|
|
20
|
+
displayName: 'To (Phone Number)',
|
|
21
|
+
name: 'phoneNumber',
|
|
22
|
+
type: 'string',
|
|
23
|
+
default: '',
|
|
24
|
+
placeholder: '+919876543210',
|
|
25
|
+
description: 'Destination phone number in E.164 format',
|
|
26
|
+
required: true,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
displayName: 'From (Caller ID)',
|
|
30
|
+
name: 'callerId',
|
|
31
|
+
type: 'string',
|
|
32
|
+
default: '',
|
|
33
|
+
placeholder: '+918035001234',
|
|
34
|
+
description: 'Your provisioned Tone number in E.164 format',
|
|
35
|
+
required: true,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
displayName: 'Webhook URL',
|
|
39
|
+
name: 'webhookUrl',
|
|
40
|
+
type: 'string',
|
|
41
|
+
default: '',
|
|
42
|
+
description: 'Optional URL to receive call events (call.turn, call.completed)',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
displayName: 'System Prompt / Instructions',
|
|
46
|
+
name: 'systemPrompt',
|
|
47
|
+
type: 'string',
|
|
48
|
+
typeOptions: { rows: 4 },
|
|
49
|
+
default: '',
|
|
50
|
+
description: 'Instructions for the AI voice agent on this call (overrides number-level instructions)',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
displayName: 'Call Type',
|
|
54
|
+
name: 'callType',
|
|
55
|
+
type: 'options',
|
|
56
|
+
options: [
|
|
57
|
+
{ name: 'Transactional', value: 'TRANSACTIONAL', description: 'TRAI-exempt — no DND or time restriction' },
|
|
58
|
+
{ name: 'Promotional', value: 'PROMOTIONAL', description: 'Subject to DND check and 9 AM–9 PM IST calling hours' },
|
|
59
|
+
{ name: 'OTP', value: 'OTP', description: 'One-time password calls — always exempt' },
|
|
60
|
+
],
|
|
61
|
+
default: 'TRANSACTIONAL',
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
async execute() {
|
|
67
|
+
const items = this.getInputData();
|
|
68
|
+
const results = [];
|
|
69
|
+
const credentials = await this.getCredentials('toneApi');
|
|
70
|
+
const baseUrl = credentials.baseUrl;
|
|
71
|
+
for (let i = 0; i < items.length; i++) {
|
|
72
|
+
const phoneNumber = this.getNodeParameter('phoneNumber', i);
|
|
73
|
+
const callerId = this.getNodeParameter('callerId', i);
|
|
74
|
+
const webhookUrl = this.getNodeParameter('webhookUrl', i);
|
|
75
|
+
const systemPrompt = this.getNodeParameter('systemPrompt', i);
|
|
76
|
+
const callType = this.getNodeParameter('callType', i);
|
|
77
|
+
const body = {
|
|
78
|
+
to: phoneNumber,
|
|
79
|
+
from: callerId,
|
|
80
|
+
callType,
|
|
81
|
+
};
|
|
82
|
+
if (webhookUrl)
|
|
83
|
+
body.webhookUrl = webhookUrl;
|
|
84
|
+
if (systemPrompt)
|
|
85
|
+
body.instructions = systemPrompt;
|
|
86
|
+
try {
|
|
87
|
+
const response = await this.helpers.request({
|
|
88
|
+
method: 'POST',
|
|
89
|
+
url: `${baseUrl}/v1/calls`,
|
|
90
|
+
headers: { Authorization: `Bearer ${credentials.apiKey}` },
|
|
91
|
+
body,
|
|
92
|
+
json: true,
|
|
93
|
+
});
|
|
94
|
+
results.push({ json: response, pairedItem: { item: i } });
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (this.continueOnFail()) {
|
|
98
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
99
|
+
results.push({ json: { error: msg }, pairedItem: { item: i } });
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), err instanceof Error ? err : new Error(String(err)), { itemIndex: i });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return [results];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.ToneMakeCall = ToneMakeCall;
|
|
110
|
+
//# sourceMappingURL=ToneMakeCall.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToneMakeCall.node.js","sourceRoot":"","sources":["../../../src/nodes/ToneMakeCall/ToneMakeCall.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class ToneSendSms implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=ToneSendSms.node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToneSendSms.node.d.ts","sourceRoot":"","sources":["../../../src/nodes/ToneSendSms/ToneSendSms.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EAEjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EACrB,MAAM,cAAc,CAAA;AAGrB,qBAAa,WAAY,YAAW,SAAS;IAC3C,WAAW,EAAE,oBAAoB,CAwChC;IAEK,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CAsCxE"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToneSendSms = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
class ToneSendSms {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.description = {
|
|
8
|
+
displayName: 'Tone: Send SMS',
|
|
9
|
+
name: 'toneSendSms',
|
|
10
|
+
icon: 'file:tone.svg',
|
|
11
|
+
group: ['transform'],
|
|
12
|
+
version: 1,
|
|
13
|
+
description: 'Send an SMS message via Tone',
|
|
14
|
+
defaults: { name: 'Tone Send SMS' },
|
|
15
|
+
inputs: ['main'],
|
|
16
|
+
outputs: ['main'],
|
|
17
|
+
credentials: [{ name: 'toneApi', required: true }],
|
|
18
|
+
properties: [
|
|
19
|
+
{
|
|
20
|
+
displayName: 'To (Phone Number)',
|
|
21
|
+
name: 'to',
|
|
22
|
+
type: 'string',
|
|
23
|
+
default: '',
|
|
24
|
+
placeholder: '+919876543210',
|
|
25
|
+
description: 'Recipient phone number in E.164 format',
|
|
26
|
+
required: true,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
displayName: 'From (Sender Number)',
|
|
30
|
+
name: 'from',
|
|
31
|
+
type: 'string',
|
|
32
|
+
default: '',
|
|
33
|
+
placeholder: '+918035001234',
|
|
34
|
+
description: 'Your provisioned Tone number in E.164 format',
|
|
35
|
+
required: true,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
displayName: 'Message',
|
|
39
|
+
name: 'body',
|
|
40
|
+
type: 'string',
|
|
41
|
+
typeOptions: { rows: 3 },
|
|
42
|
+
default: '',
|
|
43
|
+
description: 'SMS body text (max 1600 characters)',
|
|
44
|
+
required: true,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async execute() {
|
|
50
|
+
const items = this.getInputData();
|
|
51
|
+
const results = [];
|
|
52
|
+
const credentials = await this.getCredentials('toneApi');
|
|
53
|
+
const baseUrl = credentials.baseUrl;
|
|
54
|
+
for (let i = 0; i < items.length; i++) {
|
|
55
|
+
const to = this.getNodeParameter('to', i);
|
|
56
|
+
const from = this.getNodeParameter('from', i);
|
|
57
|
+
const body = this.getNodeParameter('body', i);
|
|
58
|
+
try {
|
|
59
|
+
const response = await this.helpers.request({
|
|
60
|
+
method: 'POST',
|
|
61
|
+
url: `${baseUrl}/v1/sms/send`,
|
|
62
|
+
headers: { Authorization: `Bearer ${credentials.apiKey}` },
|
|
63
|
+
body: { to, from, message: body },
|
|
64
|
+
json: true,
|
|
65
|
+
});
|
|
66
|
+
results.push({ json: response, pairedItem: { item: i } });
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
if (this.continueOnFail()) {
|
|
70
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
71
|
+
results.push({ json: { error: msg }, pairedItem: { item: i } });
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), err instanceof Error ? err : new Error(String(err)), { itemIndex: i });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return [results];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.ToneSendSms = ToneSendSms;
|
|
82
|
+
//# sourceMappingURL=ToneSendSms.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToneSendSms.node.js","sourceRoot":"","sources":["../../../src/nodes/ToneSendSms/ToneSendSms.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class ToneStartCampaign implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=ToneStartCampaign.node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToneStartCampaign.node.d.ts","sourceRoot":"","sources":["../../../src/nodes/ToneStartCampaign/ToneStartCampaign.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EAEjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EACrB,MAAM,cAAc,CAAA;AAGrB,qBAAa,iBAAkB,YAAW,SAAS;IACjD,WAAW,EAAE,oBAAoB,CAqBhC;IAEK,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CAoCxE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToneStartCampaign = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
class ToneStartCampaign {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.description = {
|
|
8
|
+
displayName: 'Tone: Start Campaign',
|
|
9
|
+
name: 'toneStartCampaign',
|
|
10
|
+
icon: 'file:tone.svg',
|
|
11
|
+
group: ['transform'],
|
|
12
|
+
version: 1,
|
|
13
|
+
description: 'Start (or resume) a Tone outbound call campaign',
|
|
14
|
+
defaults: { name: 'Tone Start Campaign' },
|
|
15
|
+
inputs: ['main'],
|
|
16
|
+
outputs: ['main'],
|
|
17
|
+
credentials: [{ name: 'toneApi', required: true }],
|
|
18
|
+
properties: [
|
|
19
|
+
{
|
|
20
|
+
displayName: 'Campaign ID',
|
|
21
|
+
name: 'campaignId',
|
|
22
|
+
type: 'string',
|
|
23
|
+
default: '',
|
|
24
|
+
description: 'The Tone campaign ID to start. Create campaigns first via POST /v1/campaigns.',
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async execute() {
|
|
31
|
+
const items = this.getInputData();
|
|
32
|
+
const results = [];
|
|
33
|
+
const credentials = await this.getCredentials('toneApi');
|
|
34
|
+
const baseUrl = credentials.baseUrl;
|
|
35
|
+
for (let i = 0; i < items.length; i++) {
|
|
36
|
+
const campaignId = this.getNodeParameter('campaignId', i);
|
|
37
|
+
try {
|
|
38
|
+
const response = await this.helpers.request({
|
|
39
|
+
method: 'POST',
|
|
40
|
+
url: `${baseUrl}/v1/campaigns/${encodeURIComponent(campaignId)}/start`,
|
|
41
|
+
headers: { Authorization: `Bearer ${credentials.apiKey}` },
|
|
42
|
+
body: {},
|
|
43
|
+
json: true,
|
|
44
|
+
});
|
|
45
|
+
results.push({ json: { campaignId, ...response }, pairedItem: { item: i } });
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
if (this.continueOnFail()) {
|
|
49
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
50
|
+
results.push({ json: { error: msg, campaignId }, pairedItem: { item: i } });
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), err instanceof Error ? err : new Error(String(err)), { itemIndex: i });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return [results];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.ToneStartCampaign = ToneStartCampaign;
|
|
61
|
+
//# sourceMappingURL=ToneStartCampaign.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToneStartCampaign.node.js","sourceRoot":"","sources":["../../../src/nodes/ToneStartCampaign/ToneStartCampaign.node.ts"],"names":[],"mappings":";;;AAOA,+CAAiD;AAEjD,MAAa,iBAAiB;IAA9B;QACE,gBAAW,GAAyB;YAClC,WAAW,EAAE,sBAAsB;YACnC,IAAI,EAAE,mBAAmB;YACzB,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,CAAC,WAAW,CAAC;YACpB,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,iDAAiD;YAC9D,QAAQ,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE;YACzC,MAAM,EAAE,CAAC,MAAM,CAAC;YAChB,OAAO,EAAE,CAAC,MAAM,CAAC;YACjB,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;YAClD,UAAU,EAAE;gBACV;oBACE,WAAW,EAAE,aAAa;oBAC1B,IAAI,EAAE,YAAY;oBAClB,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,EAAE;oBACX,WAAW,EAAE,+EAA+E;oBAC5F,QAAQ,EAAE,IAAI;iBACf;aACF;SACF,CAAA;IAsCH,CAAC;IApCC,KAAK,CAAC,OAAO;QACX,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE,CAAA;QACjC,MAAM,OAAO,GAAyB,EAAE,CAAA;QAExC,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAA;QACxD,MAAM,OAAO,GAAG,WAAW,CAAC,OAAiB,CAAA;QAE7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC,YAAY,EAAE,CAAC,CAAW,CAAA;YAEnE,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;oBAC1C,MAAM,EAAE,MAAM;oBACd,GAAG,EAAE,GAAG,OAAO,iBAAiB,kBAAkB,CAAC,UAAU,CAAC,QAAQ;oBACtE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,WAAW,CAAC,MAAgB,EAAE,EAAE;oBACpE,IAAI,EAAE,EAAE;oBACR,IAAI,EAAE,IAAI;iBACX,CAAgB,CAAA;gBAEjB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,GAAG,QAAQ,EAAE,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;YAC9E,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;oBAC1B,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;oBAC5D,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;gBAC7E,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,iCAAkB,CAC1B,IAAI,CAAC,OAAO,EAAE,EACd,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EACnD,EAAE,SAAS,EAAE,CAAC,EAAE,CACjB,CAAA;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,CAAC,OAAO,CAAC,CAAA;IAClB,CAAC;CACF;AA5DD,8CA4DC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IHookFunctions, IWebhookFunctions, INodeType, INodeTypeDescription, IWebhookResponseData } from 'n8n-workflow';
|
|
2
|
+
export declare class ToneWebhook implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
webhookMethods: {
|
|
5
|
+
default: {
|
|
6
|
+
checkExists(this: IHookFunctions): Promise<boolean>;
|
|
7
|
+
create(this: IHookFunctions): Promise<boolean>;
|
|
8
|
+
delete(this: IHookFunctions): Promise<boolean>;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=ToneWebhook.node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToneWebhook.node.d.ts","sourceRoot":"","sources":["../../../src/nodes/ToneWebhook/ToneWebhook.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EAEjB,SAAS,EACT,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,cAAc,CAAA;AAmBrB,qBAAa,WAAY,YAAW,SAAS;IAC3C,WAAW,EAAE,oBAAoB,CA6BhC;IAID,cAAc;;8BAEc,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;yBAiBtC,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;yBAuBjC,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;;MAsBvD;IAIK,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAkBtE"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToneWebhook = void 0;
|
|
4
|
+
const TONE_EVENTS = [
|
|
5
|
+
{ name: 'call.completed', value: 'call.completed' },
|
|
6
|
+
{ name: 'call.initiated', value: 'call.initiated' },
|
|
7
|
+
{ name: 'call.transfer.initiated', value: 'call.transfer.initiated' },
|
|
8
|
+
{ name: 'call.transfer.connected', value: 'call.transfer.connected' },
|
|
9
|
+
{ name: 'call.transfer.failed', value: 'call.transfer.failed' },
|
|
10
|
+
{ name: 'call.transfer.completed', value: 'call.transfer.completed' },
|
|
11
|
+
{ name: 'campaign.started', value: 'campaign.started' },
|
|
12
|
+
{ name: 'campaign.completed', value: 'campaign.completed' },
|
|
13
|
+
{ name: 'campaign.contact.answered', value: 'campaign.contact.answered' },
|
|
14
|
+
{ name: 'campaign.contact.no_answer', value: 'campaign.contact.no_answer' },
|
|
15
|
+
{ name: 'campaign.contact.failed', value: 'campaign.contact.failed' },
|
|
16
|
+
{ name: 'sms.received', value: 'sms.received' },
|
|
17
|
+
{ name: 'sms.delivered', value: 'sms.delivered' },
|
|
18
|
+
{ name: '* (all events)', value: '*' },
|
|
19
|
+
];
|
|
20
|
+
class ToneWebhook {
|
|
21
|
+
constructor() {
|
|
22
|
+
this.description = {
|
|
23
|
+
displayName: 'Tone Webhook',
|
|
24
|
+
name: 'toneWebhook',
|
|
25
|
+
icon: 'file:tone.svg',
|
|
26
|
+
group: ['trigger'],
|
|
27
|
+
version: 1,
|
|
28
|
+
description: 'Starts the workflow when a Tone call, SMS, or campaign event occurs',
|
|
29
|
+
defaults: { name: 'Tone Webhook' },
|
|
30
|
+
inputs: [],
|
|
31
|
+
outputs: ['main'],
|
|
32
|
+
credentials: [{ name: 'toneApi', required: true }],
|
|
33
|
+
webhooks: [
|
|
34
|
+
{
|
|
35
|
+
name: 'default',
|
|
36
|
+
httpMethod: 'POST',
|
|
37
|
+
responseMode: 'onReceived',
|
|
38
|
+
path: 'tone-webhook',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
properties: [
|
|
42
|
+
{
|
|
43
|
+
displayName: 'Events',
|
|
44
|
+
name: 'events',
|
|
45
|
+
type: 'multiOptions',
|
|
46
|
+
options: TONE_EVENTS,
|
|
47
|
+
default: ['call.completed'],
|
|
48
|
+
description: 'Which Tone events to listen for',
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
// ── Lifecycle hooks ─────────────────────────────────────────────────────────
|
|
53
|
+
this.webhookMethods = {
|
|
54
|
+
default: {
|
|
55
|
+
async checkExists() {
|
|
56
|
+
// Check if a webhook with this URL is already registered on Tone
|
|
57
|
+
const webhookUrl = this.getNodeWebhookUrl('default');
|
|
58
|
+
const credentials = await this.getCredentials('toneApi');
|
|
59
|
+
const baseUrl = credentials.baseUrl;
|
|
60
|
+
const res = await this.helpers.request({
|
|
61
|
+
method: 'GET',
|
|
62
|
+
url: `${baseUrl}/v1/webhooks`,
|
|
63
|
+
headers: { Authorization: `Bearer ${credentials.apiKey}` },
|
|
64
|
+
json: true,
|
|
65
|
+
});
|
|
66
|
+
const webhooks = (res?.webhooks ?? []);
|
|
67
|
+
return webhooks.some((wh) => wh.url === webhookUrl);
|
|
68
|
+
},
|
|
69
|
+
async create() {
|
|
70
|
+
const webhookUrl = this.getNodeWebhookUrl('default');
|
|
71
|
+
const events = this.getNodeParameter('events');
|
|
72
|
+
const credentials = await this.getCredentials('toneApi');
|
|
73
|
+
const baseUrl = credentials.baseUrl;
|
|
74
|
+
const res = await this.helpers.request({
|
|
75
|
+
method: 'POST',
|
|
76
|
+
url: `${baseUrl}/v1/webhooks`,
|
|
77
|
+
headers: { Authorization: `Bearer ${credentials.apiKey}` },
|
|
78
|
+
body: { url: webhookUrl, events },
|
|
79
|
+
json: true,
|
|
80
|
+
});
|
|
81
|
+
const webhookId = (res?.id ?? res?.webhookId);
|
|
82
|
+
if (webhookId) {
|
|
83
|
+
const nodeStaticData = this.getWorkflowStaticData('node');
|
|
84
|
+
nodeStaticData.webhookId = webhookId;
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
},
|
|
88
|
+
async delete() {
|
|
89
|
+
const nodeStaticData = this.getWorkflowStaticData('node');
|
|
90
|
+
const webhookId = nodeStaticData.webhookId;
|
|
91
|
+
if (!webhookId)
|
|
92
|
+
return true;
|
|
93
|
+
const credentials = await this.getCredentials('toneApi');
|
|
94
|
+
const baseUrl = credentials.baseUrl;
|
|
95
|
+
try {
|
|
96
|
+
await this.helpers.request({
|
|
97
|
+
method: 'DELETE',
|
|
98
|
+
url: `${baseUrl}/v1/webhooks/${webhookId}`,
|
|
99
|
+
headers: { Authorization: `Bearer ${credentials.apiKey}` },
|
|
100
|
+
json: true,
|
|
101
|
+
});
|
|
102
|
+
delete nodeStaticData.webhookId;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// ── Incoming webhook handler ────────────────────────────────────────────────
|
|
113
|
+
async webhook() {
|
|
114
|
+
const body = this.getBodyData();
|
|
115
|
+
const events = this.getNodeParameter('events');
|
|
116
|
+
const event = body.event;
|
|
117
|
+
const shouldProcess = events.includes('*') ||
|
|
118
|
+
!event ||
|
|
119
|
+
events.includes(event);
|
|
120
|
+
if (!shouldProcess) {
|
|
121
|
+
return { noWebhookResponse: true };
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
workflowData: [this.helpers.returnJsonArray([body])],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.ToneWebhook = ToneWebhook;
|
|
129
|
+
//# sourceMappingURL=ToneWebhook.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToneWebhook.node.js","sourceRoot":"","sources":["../../../src/nodes/ToneWebhook/ToneWebhook.node.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-tone",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "n8n community nodes for Tone — provision numbers, send SMS, initiate calls, run campaigns",
|
|
5
|
+
"keywords": ["n8n-community-node-package", "tone", "telephony", "sms", "voice", "ai"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://usetone.ai",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Bellatech Marketing Pvt. Ltd.",
|
|
10
|
+
"email": "support@usetone.ai"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/Bellatech-Marketing-Pvt-Ltd/Tone.git",
|
|
15
|
+
"directory": "packages/n8n-nodes-tone"
|
|
16
|
+
},
|
|
17
|
+
"main": "dist/index.js",
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"n8n": {
|
|
28
|
+
"n8nNodesApiVersion": 1,
|
|
29
|
+
"credentials": [
|
|
30
|
+
"dist/credentials/ToneApi.credentials.js"
|
|
31
|
+
],
|
|
32
|
+
"nodes": [
|
|
33
|
+
"dist/nodes/ToneWebhook/ToneWebhook.node.js",
|
|
34
|
+
"dist/nodes/ToneMakeCall/ToneMakeCall.node.js",
|
|
35
|
+
"dist/nodes/ToneSendSms/ToneSendSms.node.js",
|
|
36
|
+
"dist/nodes/ToneStartCampaign/ToneStartCampaign.node.js"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"n8n-workflow": "^1.82.0",
|
|
41
|
+
"typescript": "^5.8.3"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"n8n-workflow": ">=1.0.0"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ICredentialType, INodeProperties } from 'n8n-workflow'
|
|
2
|
+
|
|
3
|
+
export class ToneApi implements ICredentialType {
|
|
4
|
+
name = 'toneApi'
|
|
5
|
+
displayName = 'Tone API'
|
|
6
|
+
documentationUrl = 'https://usetone.ai/docs/api-reference'
|
|
7
|
+
|
|
8
|
+
properties: INodeProperties[] = [
|
|
9
|
+
{
|
|
10
|
+
displayName: 'API Key',
|
|
11
|
+
name: 'apiKey',
|
|
12
|
+
type: 'string',
|
|
13
|
+
typeOptions: { password: true },
|
|
14
|
+
default: '',
|
|
15
|
+
placeholder: 'tone_live_...',
|
|
16
|
+
description:
|
|
17
|
+
'Your Tone API key. Generate one at https://app.usetone.ai/settings/api-keys',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
displayName: 'Base URL',
|
|
21
|
+
name: 'baseUrl',
|
|
22
|
+
type: 'string',
|
|
23
|
+
default: 'https://tone-production-4819.up.railway.app',
|
|
24
|
+
description: 'Tone API base URL. Leave as default unless using a self-hosted instance.',
|
|
25
|
+
},
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
authenticate = {
|
|
29
|
+
type: 'generic' as const,
|
|
30
|
+
properties: {
|
|
31
|
+
headers: {
|
|
32
|
+
Authorization: '=Bearer {{$credentials.apiKey}}',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
test = {
|
|
38
|
+
request: {
|
|
39
|
+
baseURL: '={{$credentials.baseUrl}}',
|
|
40
|
+
url: '/health',
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ToneApi } from './credentials/ToneApi.credentials.js'
|
|
2
|
+
export { ToneWebhook } from './nodes/ToneWebhook/ToneWebhook.node.js'
|
|
3
|
+
export { ToneMakeCall } from './nodes/ToneMakeCall/ToneMakeCall.node.js'
|
|
4
|
+
export { ToneSendSms } from './nodes/ToneSendSms/ToneSendSms.node.js'
|
|
5
|
+
export { ToneStartCampaign } from './nodes/ToneStartCampaign/ToneStartCampaign.node.js'
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IExecuteFunctions,
|
|
3
|
+
IDataObject,
|
|
4
|
+
INodeExecutionData,
|
|
5
|
+
INodeType,
|
|
6
|
+
INodeTypeDescription,
|
|
7
|
+
} from 'n8n-workflow'
|
|
8
|
+
import { NodeOperationError } from 'n8n-workflow'
|
|
9
|
+
|
|
10
|
+
export class ToneMakeCall implements INodeType {
|
|
11
|
+
description: INodeTypeDescription = {
|
|
12
|
+
displayName: 'Tone: Make Call',
|
|
13
|
+
name: 'toneMakeCall',
|
|
14
|
+
icon: 'file:tone.svg',
|
|
15
|
+
group: ['transform'],
|
|
16
|
+
version: 1,
|
|
17
|
+
description: 'Initiate an outbound AI phone call via Tone',
|
|
18
|
+
defaults: { name: 'Tone Make Call' },
|
|
19
|
+
inputs: ['main'],
|
|
20
|
+
outputs: ['main'],
|
|
21
|
+
credentials: [{ name: 'toneApi', required: true }],
|
|
22
|
+
properties: [
|
|
23
|
+
{
|
|
24
|
+
displayName: 'To (Phone Number)',
|
|
25
|
+
name: 'phoneNumber',
|
|
26
|
+
type: 'string',
|
|
27
|
+
default: '',
|
|
28
|
+
placeholder: '+919876543210',
|
|
29
|
+
description: 'Destination phone number in E.164 format',
|
|
30
|
+
required: true,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
displayName: 'From (Caller ID)',
|
|
34
|
+
name: 'callerId',
|
|
35
|
+
type: 'string',
|
|
36
|
+
default: '',
|
|
37
|
+
placeholder: '+918035001234',
|
|
38
|
+
description: 'Your provisioned Tone number in E.164 format',
|
|
39
|
+
required: true,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
displayName: 'Webhook URL',
|
|
43
|
+
name: 'webhookUrl',
|
|
44
|
+
type: 'string',
|
|
45
|
+
default: '',
|
|
46
|
+
description: 'Optional URL to receive call events (call.turn, call.completed)',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
displayName: 'System Prompt / Instructions',
|
|
50
|
+
name: 'systemPrompt',
|
|
51
|
+
type: 'string',
|
|
52
|
+
typeOptions: { rows: 4 },
|
|
53
|
+
default: '',
|
|
54
|
+
description:
|
|
55
|
+
'Instructions for the AI voice agent on this call (overrides number-level instructions)',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
displayName: 'Call Type',
|
|
59
|
+
name: 'callType',
|
|
60
|
+
type: 'options',
|
|
61
|
+
options: [
|
|
62
|
+
{ name: 'Transactional', value: 'TRANSACTIONAL', description: 'TRAI-exempt — no DND or time restriction' },
|
|
63
|
+
{ name: 'Promotional', value: 'PROMOTIONAL', description: 'Subject to DND check and 9 AM–9 PM IST calling hours' },
|
|
64
|
+
{ name: 'OTP', value: 'OTP', description: 'One-time password calls — always exempt' },
|
|
65
|
+
],
|
|
66
|
+
default: 'TRANSACTIONAL',
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
72
|
+
const items = this.getInputData()
|
|
73
|
+
const results: INodeExecutionData[] = []
|
|
74
|
+
|
|
75
|
+
const credentials = await this.getCredentials('toneApi')
|
|
76
|
+
const baseUrl = credentials.baseUrl as string
|
|
77
|
+
|
|
78
|
+
for (let i = 0; i < items.length; i++) {
|
|
79
|
+
const phoneNumber = this.getNodeParameter('phoneNumber', i) as string
|
|
80
|
+
const callerId = this.getNodeParameter('callerId', i) as string
|
|
81
|
+
const webhookUrl = this.getNodeParameter('webhookUrl', i) as string
|
|
82
|
+
const systemPrompt = this.getNodeParameter('systemPrompt', i) as string
|
|
83
|
+
const callType = this.getNodeParameter('callType', i) as string
|
|
84
|
+
|
|
85
|
+
const body: IDataObject = {
|
|
86
|
+
to: phoneNumber,
|
|
87
|
+
from: callerId,
|
|
88
|
+
callType,
|
|
89
|
+
}
|
|
90
|
+
if (webhookUrl) body.webhookUrl = webhookUrl
|
|
91
|
+
if (systemPrompt) body.instructions = systemPrompt
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const response = await this.helpers.request({
|
|
95
|
+
method: 'POST',
|
|
96
|
+
url: `${baseUrl}/v1/calls`,
|
|
97
|
+
headers: { Authorization: `Bearer ${credentials.apiKey as string}` },
|
|
98
|
+
body,
|
|
99
|
+
json: true,
|
|
100
|
+
}) as IDataObject
|
|
101
|
+
|
|
102
|
+
results.push({ json: response, pairedItem: { item: i } })
|
|
103
|
+
} catch (err: unknown) {
|
|
104
|
+
if (this.continueOnFail()) {
|
|
105
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
106
|
+
results.push({ json: { error: msg }, pairedItem: { item: i } })
|
|
107
|
+
} else {
|
|
108
|
+
throw new NodeOperationError(
|
|
109
|
+
this.getNode(),
|
|
110
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
111
|
+
{ itemIndex: i }
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return [results]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" fill="none">
|
|
2
|
+
<rect width="60" height="60" rx="12" fill="#6366F1"/>
|
|
3
|
+
<path d="M18 38 C18 26 26 18 30 18 C34 18 42 26 42 38" stroke="white" stroke-width="3.5" stroke-linecap="round" fill="none"/>
|
|
4
|
+
<circle cx="30" cy="38" r="4" fill="white"/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IExecuteFunctions,
|
|
3
|
+
IDataObject,
|
|
4
|
+
INodeExecutionData,
|
|
5
|
+
INodeType,
|
|
6
|
+
INodeTypeDescription,
|
|
7
|
+
} from 'n8n-workflow'
|
|
8
|
+
import { NodeOperationError } from 'n8n-workflow'
|
|
9
|
+
|
|
10
|
+
export class ToneSendSms implements INodeType {
|
|
11
|
+
description: INodeTypeDescription = {
|
|
12
|
+
displayName: 'Tone: Send SMS',
|
|
13
|
+
name: 'toneSendSms',
|
|
14
|
+
icon: 'file:tone.svg',
|
|
15
|
+
group: ['transform'],
|
|
16
|
+
version: 1,
|
|
17
|
+
description: 'Send an SMS message via Tone',
|
|
18
|
+
defaults: { name: 'Tone Send SMS' },
|
|
19
|
+
inputs: ['main'],
|
|
20
|
+
outputs: ['main'],
|
|
21
|
+
credentials: [{ name: 'toneApi', required: true }],
|
|
22
|
+
properties: [
|
|
23
|
+
{
|
|
24
|
+
displayName: 'To (Phone Number)',
|
|
25
|
+
name: 'to',
|
|
26
|
+
type: 'string',
|
|
27
|
+
default: '',
|
|
28
|
+
placeholder: '+919876543210',
|
|
29
|
+
description: 'Recipient phone number in E.164 format',
|
|
30
|
+
required: true,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
displayName: 'From (Sender Number)',
|
|
34
|
+
name: 'from',
|
|
35
|
+
type: 'string',
|
|
36
|
+
default: '',
|
|
37
|
+
placeholder: '+918035001234',
|
|
38
|
+
description: 'Your provisioned Tone number in E.164 format',
|
|
39
|
+
required: true,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
displayName: 'Message',
|
|
43
|
+
name: 'body',
|
|
44
|
+
type: 'string',
|
|
45
|
+
typeOptions: { rows: 3 },
|
|
46
|
+
default: '',
|
|
47
|
+
description: 'SMS body text (max 1600 characters)',
|
|
48
|
+
required: true,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
54
|
+
const items = this.getInputData()
|
|
55
|
+
const results: INodeExecutionData[] = []
|
|
56
|
+
|
|
57
|
+
const credentials = await this.getCredentials('toneApi')
|
|
58
|
+
const baseUrl = credentials.baseUrl as string
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < items.length; i++) {
|
|
61
|
+
const to = this.getNodeParameter('to', i) as string
|
|
62
|
+
const from = this.getNodeParameter('from', i) as string
|
|
63
|
+
const body = this.getNodeParameter('body', i) as string
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const response = await this.helpers.request({
|
|
67
|
+
method: 'POST',
|
|
68
|
+
url: `${baseUrl}/v1/sms/send`,
|
|
69
|
+
headers: { Authorization: `Bearer ${credentials.apiKey as string}` },
|
|
70
|
+
body: { to, from, message: body },
|
|
71
|
+
json: true,
|
|
72
|
+
}) as IDataObject
|
|
73
|
+
|
|
74
|
+
results.push({ json: response, pairedItem: { item: i } })
|
|
75
|
+
} catch (err: unknown) {
|
|
76
|
+
if (this.continueOnFail()) {
|
|
77
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
78
|
+
results.push({ json: { error: msg }, pairedItem: { item: i } })
|
|
79
|
+
} else {
|
|
80
|
+
throw new NodeOperationError(
|
|
81
|
+
this.getNode(),
|
|
82
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
83
|
+
{ itemIndex: i }
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return [results]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" fill="none">
|
|
2
|
+
<rect width="60" height="60" rx="12" fill="#6366F1"/>
|
|
3
|
+
<path d="M18 38 C18 26 26 18 30 18 C34 18 42 26 42 38" stroke="white" stroke-width="3.5" stroke-linecap="round" fill="none"/>
|
|
4
|
+
<circle cx="30" cy="38" r="4" fill="white"/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IExecuteFunctions,
|
|
3
|
+
IDataObject,
|
|
4
|
+
INodeExecutionData,
|
|
5
|
+
INodeType,
|
|
6
|
+
INodeTypeDescription,
|
|
7
|
+
} from 'n8n-workflow'
|
|
8
|
+
import { NodeOperationError } from 'n8n-workflow'
|
|
9
|
+
|
|
10
|
+
export class ToneStartCampaign implements INodeType {
|
|
11
|
+
description: INodeTypeDescription = {
|
|
12
|
+
displayName: 'Tone: Start Campaign',
|
|
13
|
+
name: 'toneStartCampaign',
|
|
14
|
+
icon: 'file:tone.svg',
|
|
15
|
+
group: ['transform'],
|
|
16
|
+
version: 1,
|
|
17
|
+
description: 'Start (or resume) a Tone outbound call campaign',
|
|
18
|
+
defaults: { name: 'Tone Start Campaign' },
|
|
19
|
+
inputs: ['main'],
|
|
20
|
+
outputs: ['main'],
|
|
21
|
+
credentials: [{ name: 'toneApi', required: true }],
|
|
22
|
+
properties: [
|
|
23
|
+
{
|
|
24
|
+
displayName: 'Campaign ID',
|
|
25
|
+
name: 'campaignId',
|
|
26
|
+
type: 'string',
|
|
27
|
+
default: '',
|
|
28
|
+
description: 'The Tone campaign ID to start. Create campaigns first via POST /v1/campaigns.',
|
|
29
|
+
required: true,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
35
|
+
const items = this.getInputData()
|
|
36
|
+
const results: INodeExecutionData[] = []
|
|
37
|
+
|
|
38
|
+
const credentials = await this.getCredentials('toneApi')
|
|
39
|
+
const baseUrl = credentials.baseUrl as string
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < items.length; i++) {
|
|
42
|
+
const campaignId = this.getNodeParameter('campaignId', i) as string
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const response = await this.helpers.request({
|
|
46
|
+
method: 'POST',
|
|
47
|
+
url: `${baseUrl}/v1/campaigns/${encodeURIComponent(campaignId)}/start`,
|
|
48
|
+
headers: { Authorization: `Bearer ${credentials.apiKey as string}` },
|
|
49
|
+
body: {},
|
|
50
|
+
json: true,
|
|
51
|
+
}) as IDataObject
|
|
52
|
+
|
|
53
|
+
results.push({ json: { campaignId, ...response }, pairedItem: { item: i } })
|
|
54
|
+
} catch (err: unknown) {
|
|
55
|
+
if (this.continueOnFail()) {
|
|
56
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
57
|
+
results.push({ json: { error: msg, campaignId }, pairedItem: { item: i } })
|
|
58
|
+
} else {
|
|
59
|
+
throw new NodeOperationError(
|
|
60
|
+
this.getNode(),
|
|
61
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
62
|
+
{ itemIndex: i }
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return [results]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" fill="none">
|
|
2
|
+
<rect width="60" height="60" rx="12" fill="#6366F1"/>
|
|
3
|
+
<path d="M18 38 C18 26 26 18 30 18 C34 18 42 26 42 38" stroke="white" stroke-width="3.5" stroke-linecap="round" fill="none"/>
|
|
4
|
+
<circle cx="30" cy="38" r="4" fill="white"/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IHookFunctions,
|
|
3
|
+
IWebhookFunctions,
|
|
4
|
+
IDataObject,
|
|
5
|
+
INodeType,
|
|
6
|
+
INodeTypeDescription,
|
|
7
|
+
IWebhookResponseData,
|
|
8
|
+
} from 'n8n-workflow'
|
|
9
|
+
|
|
10
|
+
const TONE_EVENTS = [
|
|
11
|
+
{ name: 'call.completed', value: 'call.completed' },
|
|
12
|
+
{ name: 'call.initiated', value: 'call.initiated' },
|
|
13
|
+
{ name: 'call.transfer.initiated', value: 'call.transfer.initiated' },
|
|
14
|
+
{ name: 'call.transfer.connected', value: 'call.transfer.connected' },
|
|
15
|
+
{ name: 'call.transfer.failed', value: 'call.transfer.failed' },
|
|
16
|
+
{ name: 'call.transfer.completed', value: 'call.transfer.completed' },
|
|
17
|
+
{ name: 'campaign.started', value: 'campaign.started' },
|
|
18
|
+
{ name: 'campaign.completed', value: 'campaign.completed' },
|
|
19
|
+
{ name: 'campaign.contact.answered', value: 'campaign.contact.answered' },
|
|
20
|
+
{ name: 'campaign.contact.no_answer', value: 'campaign.contact.no_answer' },
|
|
21
|
+
{ name: 'campaign.contact.failed', value: 'campaign.contact.failed' },
|
|
22
|
+
{ name: 'sms.received', value: 'sms.received' },
|
|
23
|
+
{ name: 'sms.delivered', value: 'sms.delivered' },
|
|
24
|
+
{ name: '* (all events)', value: '*' },
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
export class ToneWebhook implements INodeType {
|
|
28
|
+
description: INodeTypeDescription = {
|
|
29
|
+
displayName: 'Tone Webhook',
|
|
30
|
+
name: 'toneWebhook',
|
|
31
|
+
icon: 'file:tone.svg',
|
|
32
|
+
group: ['trigger'],
|
|
33
|
+
version: 1,
|
|
34
|
+
description: 'Starts the workflow when a Tone call, SMS, or campaign event occurs',
|
|
35
|
+
defaults: { name: 'Tone Webhook' },
|
|
36
|
+
inputs: [],
|
|
37
|
+
outputs: ['main'],
|
|
38
|
+
credentials: [{ name: 'toneApi', required: true }],
|
|
39
|
+
webhooks: [
|
|
40
|
+
{
|
|
41
|
+
name: 'default',
|
|
42
|
+
httpMethod: 'POST',
|
|
43
|
+
responseMode: 'onReceived',
|
|
44
|
+
path: 'tone-webhook',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
properties: [
|
|
48
|
+
{
|
|
49
|
+
displayName: 'Events',
|
|
50
|
+
name: 'events',
|
|
51
|
+
type: 'multiOptions',
|
|
52
|
+
options: TONE_EVENTS,
|
|
53
|
+
default: ['call.completed'],
|
|
54
|
+
description: 'Which Tone events to listen for',
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ── Lifecycle hooks ─────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
webhookMethods = {
|
|
62
|
+
default: {
|
|
63
|
+
async checkExists(this: IHookFunctions): Promise<boolean> {
|
|
64
|
+
// Check if a webhook with this URL is already registered on Tone
|
|
65
|
+
const webhookUrl = this.getNodeWebhookUrl('default')
|
|
66
|
+
const credentials = await this.getCredentials('toneApi')
|
|
67
|
+
const baseUrl = credentials.baseUrl as string
|
|
68
|
+
|
|
69
|
+
const res = await this.helpers.request({
|
|
70
|
+
method: 'GET',
|
|
71
|
+
url: `${baseUrl}/v1/webhooks`,
|
|
72
|
+
headers: { Authorization: `Bearer ${credentials.apiKey as string}` },
|
|
73
|
+
json: true,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const webhooks = (res?.webhooks ?? []) as Array<{ url: string; id: string }>
|
|
77
|
+
return webhooks.some((wh) => wh.url === webhookUrl)
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
async create(this: IHookFunctions): Promise<boolean> {
|
|
81
|
+
const webhookUrl = this.getNodeWebhookUrl('default')
|
|
82
|
+
const events = this.getNodeParameter('events') as string[]
|
|
83
|
+
const credentials = await this.getCredentials('toneApi')
|
|
84
|
+
const baseUrl = credentials.baseUrl as string
|
|
85
|
+
|
|
86
|
+
const res = await this.helpers.request({
|
|
87
|
+
method: 'POST',
|
|
88
|
+
url: `${baseUrl}/v1/webhooks`,
|
|
89
|
+
headers: { Authorization: `Bearer ${credentials.apiKey as string}` },
|
|
90
|
+
body: { url: webhookUrl, events },
|
|
91
|
+
json: true,
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const webhookId = (res?.id ?? res?.webhookId) as string | undefined
|
|
95
|
+
if (webhookId) {
|
|
96
|
+
const nodeStaticData = this.getWorkflowStaticData('node')
|
|
97
|
+
nodeStaticData.webhookId = webhookId
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return true
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
async delete(this: IHookFunctions): Promise<boolean> {
|
|
104
|
+
const nodeStaticData = this.getWorkflowStaticData('node')
|
|
105
|
+
const webhookId = nodeStaticData.webhookId as string | undefined
|
|
106
|
+
if (!webhookId) return true
|
|
107
|
+
|
|
108
|
+
const credentials = await this.getCredentials('toneApi')
|
|
109
|
+
const baseUrl = credentials.baseUrl as string
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
await this.helpers.request({
|
|
113
|
+
method: 'DELETE',
|
|
114
|
+
url: `${baseUrl}/v1/webhooks/${webhookId}`,
|
|
115
|
+
headers: { Authorization: `Bearer ${credentials.apiKey as string}` },
|
|
116
|
+
json: true,
|
|
117
|
+
})
|
|
118
|
+
delete nodeStaticData.webhookId
|
|
119
|
+
} catch {
|
|
120
|
+
return false
|
|
121
|
+
}
|
|
122
|
+
return true
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── Incoming webhook handler ────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
|
130
|
+
const body = this.getBodyData() as IDataObject
|
|
131
|
+
const events = this.getNodeParameter('events') as string[]
|
|
132
|
+
|
|
133
|
+
const event = body.event as string | undefined
|
|
134
|
+
const shouldProcess =
|
|
135
|
+
events.includes('*') ||
|
|
136
|
+
!event ||
|
|
137
|
+
events.includes(event)
|
|
138
|
+
|
|
139
|
+
if (!shouldProcess) {
|
|
140
|
+
return { noWebhookResponse: true }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
workflowData: [this.helpers.returnJsonArray([body])],
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" fill="none">
|
|
2
|
+
<rect width="60" height="60" rx="12" fill="#6366F1"/>
|
|
3
|
+
<path d="M18 38 C18 26 26 18 30 18 C34 18 42 26 42 38" stroke="white" stroke-width="3.5" stroke-linecap="round" fill="none"/>
|
|
4
|
+
<circle cx="30" cy="38" r="4" fill="white"/>
|
|
5
|
+
</svg>
|