first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
IDataObject,
|
||||
IHttpRequestMethods,
|
||||
IRequestOptions,
|
||||
IHttpRequestOptions,
|
||||
ILoadOptionsFunctions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
/**
|
||||
* Make an API request to Twilio
|
||||
*
|
||||
*/
|
||||
export async function twilioApiRequest(
|
||||
this: IHookFunctions | IExecuteFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
body: IDataObject,
|
||||
query?: IDataObject,
|
||||
): Promise<any> {
|
||||
const credentials = await this.getCredentials<{
|
||||
accountSid: string;
|
||||
authType: 'authToken' | 'apiKey';
|
||||
authToken: string;
|
||||
apiKeySid: string;
|
||||
apiKeySecret: string;
|
||||
}>('twilioApi');
|
||||
|
||||
if (query === undefined) {
|
||||
query = {};
|
||||
}
|
||||
|
||||
const options: IRequestOptions = {
|
||||
method,
|
||||
form: body,
|
||||
qs: query,
|
||||
uri: `https://api.twilio.com/2010-04-01/Accounts/${credentials.accountSid}${endpoint}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
return await this.helpers.requestWithAuthentication.call(this, 'twilioApi', options);
|
||||
}
|
||||
|
||||
export async function twilioTriggerApiRequest(
|
||||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
body: FormData | IDataObject = {},
|
||||
): Promise<any> {
|
||||
const options: IHttpRequestOptions = {
|
||||
method,
|
||||
body,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
url: `https://events.twilio.com/v1/${endpoint}`,
|
||||
json: true,
|
||||
};
|
||||
return await this.helpers.requestWithAuthentication.call(this, 'twilioApi', options);
|
||||
}
|
||||
|
||||
const XML_CHAR_MAP: { [key: string]: string } = {
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'&': '&',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
};
|
||||
|
||||
export function escapeXml(str: string) {
|
||||
return str.replace(/[<>&"']/g, (ch: string) => {
|
||||
return XML_CHAR_MAP[ch];
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.twilio",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Communication", "Development"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/credentials/twilio/"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.twilio/"
|
||||
}
|
||||
],
|
||||
"generic": [
|
||||
{
|
||||
"label": "Love at first sight: Ricardo’s n8n journey",
|
||||
"icon": "❤️",
|
||||
"url": "https://n8n.io/blog/love-at-first-sight-ricardos-n8n-journey/"
|
||||
},
|
||||
{
|
||||
"label": "Database Monitoring and Alerting with n8n",
|
||||
"icon": "📡",
|
||||
"url": "https://n8n.io/blog/database-monitoring-and-alerting-with-n8n/"
|
||||
},
|
||||
{
|
||||
"label": "Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n",
|
||||
"icon": "🧾",
|
||||
"url": "https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/"
|
||||
},
|
||||
{
|
||||
"label": "Tracking Time Spent in Meetings With Google Calendar, Twilio, and n8n",
|
||||
"icon": "🗓",
|
||||
"url": "https://n8n.io/blog/tracking-time-spent-in-meetings-with-google-calendar-twilio-and-n8n/"
|
||||
},
|
||||
{
|
||||
"label": "Creating Error Workflows in n8n",
|
||||
"icon": "🌪",
|
||||
"url": "https://n8n.io/blog/creating-error-workflows-in-n8n/"
|
||||
},
|
||||
{
|
||||
"label": "Sending Automated Congratulations with Google Sheets, Twilio, and n8n ",
|
||||
"icon": "🙌",
|
||||
"url": "https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/"
|
||||
},
|
||||
{
|
||||
"label": "Learn to Build Powerful API Endpoints Using Webhooks",
|
||||
"icon": "🧰",
|
||||
"url": "https://n8n.io/blog/learn-to-build-powerful-api-endpoints-using-webhooks/"
|
||||
},
|
||||
{
|
||||
"label": "Sending SMS the Low-Code Way with Airtable, Twilio Programmable SMS, and n8n",
|
||||
"icon": "📱",
|
||||
"url": "https://n8n.io/blog/sending-sms-the-low-code-way-with-airtable-twilio-programmable-sms-and-n8n/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"alias": ["SMS", "Phone", "Voice"]
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
IHttpRequestMethods,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionTypes, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { escapeXml, twilioApiRequest } from './GenericFunctions';
|
||||
|
||||
export class Twilio implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Twilio',
|
||||
name: 'twilio',
|
||||
icon: 'file:twilio.svg',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Send SMS and WhatsApp messages or make phone calls',
|
||||
defaults: {
|
||||
name: 'Twilio',
|
||||
},
|
||||
usableAsTool: true,
|
||||
inputs: [NodeConnectionTypes.Main],
|
||||
outputs: [NodeConnectionTypes.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'twilioApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Call',
|
||||
value: 'call',
|
||||
},
|
||||
{
|
||||
name: 'SMS',
|
||||
value: 'sms',
|
||||
},
|
||||
],
|
||||
default: 'sms',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['sms'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Send',
|
||||
value: 'send',
|
||||
description: 'Send SMS/MMS/WhatsApp message',
|
||||
action: 'Send an SMS/MMS/WhatsApp message',
|
||||
},
|
||||
],
|
||||
default: 'send',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['call'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Make',
|
||||
value: 'make',
|
||||
action: 'Make a call',
|
||||
},
|
||||
],
|
||||
default: 'make',
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// sms / call
|
||||
// ----------------------------------
|
||||
|
||||
// ----------------------------------
|
||||
// sms:send / call:make
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'From',
|
||||
name: 'from',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '+14155238886',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['send', 'make'],
|
||||
resource: ['sms', 'call'],
|
||||
},
|
||||
},
|
||||
description: 'The number from which to send the message',
|
||||
},
|
||||
{
|
||||
displayName: 'To',
|
||||
name: 'to',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '+14155238886',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['send', 'make'],
|
||||
resource: ['sms', 'call'],
|
||||
},
|
||||
},
|
||||
description: 'The number to which to send the message',
|
||||
},
|
||||
{
|
||||
displayName: 'To Whatsapp',
|
||||
name: 'toWhatsapp',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['send'],
|
||||
resource: ['sms'],
|
||||
},
|
||||
},
|
||||
description: 'Whether the message should be sent to WhatsApp',
|
||||
},
|
||||
{
|
||||
displayName: 'Message',
|
||||
name: 'message',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['send'],
|
||||
resource: ['sms'],
|
||||
},
|
||||
},
|
||||
description: 'The message to send',
|
||||
},
|
||||
{
|
||||
displayName: 'Use TwiML',
|
||||
name: 'twiml',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['make'],
|
||||
resource: ['call'],
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Whether to use the <a href="https://www.twilio.com/docs/voice/twiml">Twilio Markup Language</a> in the message',
|
||||
},
|
||||
{
|
||||
displayName: 'Message',
|
||||
name: 'message',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['make'],
|
||||
resource: ['call'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Status Callback',
|
||||
name: 'statusCallback',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'Status Callbacks allow you to receive events related to the REST resources managed by Twilio: Rooms, Recordings and Compositions',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
let operation: string;
|
||||
let resource: string;
|
||||
|
||||
// For Post
|
||||
let body: IDataObject;
|
||||
// For Query string
|
||||
let qs: IDataObject;
|
||||
|
||||
let requestMethod: IHttpRequestMethods;
|
||||
let endpoint: string;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
try {
|
||||
requestMethod = 'GET';
|
||||
endpoint = '';
|
||||
body = {};
|
||||
qs = {};
|
||||
|
||||
resource = this.getNodeParameter('resource', i);
|
||||
operation = this.getNodeParameter('operation', i);
|
||||
|
||||
if (resource === 'sms') {
|
||||
if (operation === 'send') {
|
||||
// ----------------------------------
|
||||
// sms:send
|
||||
// ----------------------------------
|
||||
|
||||
requestMethod = 'POST';
|
||||
endpoint = '/Messages.json';
|
||||
|
||||
body.From = this.getNodeParameter('from', i) as string;
|
||||
body.To = this.getNodeParameter('to', i) as string;
|
||||
body.Body = this.getNodeParameter('message', i) as string;
|
||||
body.StatusCallback = this.getNodeParameter('options.statusCallback', i, '') as string;
|
||||
|
||||
const toWhatsapp = this.getNodeParameter('toWhatsapp', i) as boolean;
|
||||
|
||||
if (toWhatsapp) {
|
||||
body.From = `whatsapp:${body.From}`;
|
||||
body.To = `whatsapp:${body.To}`;
|
||||
}
|
||||
} else {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`The operation "${operation}" is not known!`,
|
||||
{ itemIndex: i },
|
||||
);
|
||||
}
|
||||
} else if (resource === 'call') {
|
||||
if (operation === 'make') {
|
||||
// ----------------------------------
|
||||
// call:make
|
||||
// ----------------------------------
|
||||
|
||||
requestMethod = 'POST';
|
||||
endpoint = '/Calls.json';
|
||||
|
||||
const message = this.getNodeParameter('message', i) as string;
|
||||
const useTwiml = this.getNodeParameter('twiml', i) as boolean;
|
||||
body.From = this.getNodeParameter('from', i) as string;
|
||||
body.To = this.getNodeParameter('to', i) as string;
|
||||
|
||||
if (useTwiml) {
|
||||
body.Twiml = message;
|
||||
} else {
|
||||
body.Twiml = `<Response><Say>${escapeXml(message)}</Say></Response>`;
|
||||
}
|
||||
|
||||
body.StatusCallback = this.getNodeParameter('options.statusCallback', i, '') as string;
|
||||
} else {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`The operation "${operation}" is not known!`,
|
||||
{ itemIndex: i },
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new NodeOperationError(this.getNode(), `The resource "${resource}" is not known!`, {
|
||||
itemIndex: i,
|
||||
});
|
||||
}
|
||||
|
||||
const responseData = await twilioApiRequest.call(this, requestMethod, endpoint, body, qs);
|
||||
|
||||
returnData.push(responseData as IDataObject);
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.message });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.twilioTrigger",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Communication", "Development"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/credentials/twilio/"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.twiliotrigger/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"alias": ["SMS", "Phone", "Voice"]
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
import {
|
||||
type IHookFunctions,
|
||||
type IWebhookFunctions,
|
||||
type INodeType,
|
||||
type INodeTypeDescription,
|
||||
type IWebhookResponseData,
|
||||
NodeConnectionTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { twilioTriggerApiRequest } from './GenericFunctions';
|
||||
|
||||
export class TwilioTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Twilio Trigger',
|
||||
name: 'twilioTrigger',
|
||||
icon: 'file:twilio.svg',
|
||||
group: ['trigger'],
|
||||
version: [1],
|
||||
defaultVersion: 1,
|
||||
subtitle: '=Updates: {{$parameter["updates"].join(", ")}}',
|
||||
description: 'Starts the workflow on a Twilio update',
|
||||
defaults: {
|
||||
name: 'Twilio Trigger',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionTypes.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'twilioApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'POST',
|
||||
responseMode: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Trigger On',
|
||||
name: 'updates',
|
||||
type: 'multiOptions',
|
||||
options: [
|
||||
{
|
||||
name: 'New SMS',
|
||||
value: 'com.twilio.messaging.inbound-message.received',
|
||||
description: 'When an SMS message is received',
|
||||
},
|
||||
{
|
||||
name: 'New Call',
|
||||
value: 'com.twilio.voice.insights.call-summary.complete',
|
||||
description: 'When a call is received',
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: "The 'New Call' event may take up to thirty minutes to be triggered",
|
||||
name: 'callTriggerNotice',
|
||||
type: 'notice',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
updates: ['com.twilio.voice.insights.call-summary.complete'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
webhookMethods = {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
|
||||
const { sinks } = (await twilioTriggerApiRequest.call(this, 'GET', 'Sinks')) || {};
|
||||
|
||||
const sink = sinks.find(
|
||||
(entry: { sink_configuration: { destination: string | undefined } }) =>
|
||||
entry.sink_configuration.destination === webhookUrl,
|
||||
);
|
||||
|
||||
if (sink) {
|
||||
const { subscriptions } =
|
||||
(await twilioTriggerApiRequest.call(this, 'GET', 'Subscriptions')) || {};
|
||||
|
||||
const subscription = subscriptions.find(
|
||||
(entry: { sink_sid: any }) => entry.sink_sid === sink.sid,
|
||||
);
|
||||
|
||||
if (subscription) {
|
||||
const { types } =
|
||||
(await twilioTriggerApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`Subscriptions/${subscription.sid}/SubscribedEvents`,
|
||||
)) || {};
|
||||
|
||||
const typesFound = types.map((type: { type: any }) => type.type);
|
||||
|
||||
const allowedUpdates = this.getNodeParameter('updates') as string[];
|
||||
|
||||
if (typesFound.sort().join(',') === allowedUpdates.sort().join(',')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
const workflowData = this.getWorkflowStaticData('node');
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
|
||||
const allowedUpdates = this.getNodeParameter('updates') as string[];
|
||||
|
||||
const bodySink = {
|
||||
Description: 'Sink created by n8n Twilio Trigger Node.',
|
||||
SinkConfiguration: `{ "destination": "${webhookUrl}", "method": "POST" }`,
|
||||
SinkType: 'webhook',
|
||||
};
|
||||
|
||||
const sink = await twilioTriggerApiRequest.call(this, 'POST', 'Sinks', bodySink);
|
||||
|
||||
workflowData.sinkId = sink.sid;
|
||||
|
||||
const body = {
|
||||
Description: 'Subscription created by n8n Twilio Trigger Node.',
|
||||
Types: `{ "type": "${allowedUpdates[0]}" }`,
|
||||
SinkSid: sink.sid,
|
||||
};
|
||||
|
||||
const subscription = await twilioTriggerApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
'Subscriptions',
|
||||
body,
|
||||
);
|
||||
workflowData.subscriptionId = subscription.sid;
|
||||
// if there is more than one event type add the others on the existing subscription
|
||||
if (allowedUpdates.length > 1) {
|
||||
for (let index = 1; index < allowedUpdates.length; index++) {
|
||||
await twilioTriggerApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
`Subscriptions/${workflowData.subscriptionId}/SubscribedEvents`,
|
||||
{
|
||||
Type: allowedUpdates[index],
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
const workflowData = this.getWorkflowStaticData('node');
|
||||
const sinkId = workflowData.sinkId;
|
||||
const subscriptionId = workflowData.subscriptionId;
|
||||
|
||||
try {
|
||||
if (sinkId) {
|
||||
await twilioTriggerApiRequest.call(this, 'DELETE', `Sinks/${sinkId}`, {});
|
||||
workflowData.sinkId = '';
|
||||
}
|
||||
if (subscriptionId) {
|
||||
await twilioTriggerApiRequest.call(
|
||||
this,
|
||||
'DELETE',
|
||||
`Subscriptions/${subscriptionId}`,
|
||||
{},
|
||||
);
|
||||
workflowData.subscriptionId = '';
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
const bodyData = this.getBodyData();
|
||||
|
||||
return {
|
||||
workflowData: [this.helpers.returnJsonArray(bodyData)],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"account_sid": {
|
||||
"type": "string"
|
||||
},
|
||||
"annotation": {
|
||||
"type": "null"
|
||||
},
|
||||
"answered_by": {
|
||||
"type": "null"
|
||||
},
|
||||
"api_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"caller_name": {
|
||||
"type": "null"
|
||||
},
|
||||
"date_created": {
|
||||
"type": "null"
|
||||
},
|
||||
"date_updated": {
|
||||
"type": "null"
|
||||
},
|
||||
"direction": {
|
||||
"type": "string"
|
||||
},
|
||||
"duration": {
|
||||
"type": "null"
|
||||
},
|
||||
"end_time": {
|
||||
"type": "null"
|
||||
},
|
||||
"forwarded_from": {
|
||||
"type": "null"
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
},
|
||||
"from_formatted": {
|
||||
"type": "string"
|
||||
},
|
||||
"group_sid": {
|
||||
"type": "null"
|
||||
},
|
||||
"parent_call_sid": {
|
||||
"type": "null"
|
||||
},
|
||||
"price": {
|
||||
"type": "null"
|
||||
},
|
||||
"price_unit": {
|
||||
"type": "string"
|
||||
},
|
||||
"queue_time": {
|
||||
"type": "string"
|
||||
},
|
||||
"sid": {
|
||||
"type": "string"
|
||||
},
|
||||
"start_time": {
|
||||
"type": "null"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"subresource_uris": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"events": {
|
||||
"type": "string"
|
||||
},
|
||||
"notifications": {
|
||||
"type": "string"
|
||||
},
|
||||
"payments": {
|
||||
"type": "string"
|
||||
},
|
||||
"recordings": {
|
||||
"type": "string"
|
||||
},
|
||||
"siprec": {
|
||||
"type": "string"
|
||||
},
|
||||
"streams": {
|
||||
"type": "string"
|
||||
},
|
||||
"transcriptions": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_defined_message_subscriptions": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_defined_messages": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"to": {
|
||||
"type": "string"
|
||||
},
|
||||
"to_formatted": {
|
||||
"type": "string"
|
||||
},
|
||||
"trunk_sid": {
|
||||
"type": "null"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"account_sid": {
|
||||
"type": "string"
|
||||
},
|
||||
"api_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"body": {
|
||||
"type": "string"
|
||||
},
|
||||
"date_created": {
|
||||
"type": "string"
|
||||
},
|
||||
"date_sent": {
|
||||
"type": "null"
|
||||
},
|
||||
"date_updated": {
|
||||
"type": "string"
|
||||
},
|
||||
"direction": {
|
||||
"type": "string"
|
||||
},
|
||||
"error_code": {
|
||||
"type": "null"
|
||||
},
|
||||
"error_message": {
|
||||
"type": "null"
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
},
|
||||
"messaging_service_sid": {
|
||||
"type": "string"
|
||||
},
|
||||
"num_media": {
|
||||
"type": "string"
|
||||
},
|
||||
"num_segments": {
|
||||
"type": "string"
|
||||
},
|
||||
"price": {
|
||||
"type": "null"
|
||||
},
|
||||
"sid": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"subresource_uris": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"media": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"to": {
|
||||
"type": "string"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 5
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 65 65"><use xlink:href="#a" x=".5" y=".5"/><symbol id="a" overflow="visible"><g fill="#e31e26" fill-rule="nonzero" stroke="none"><path d="M31.953 0C14.337 0 0 14.337 0 31.953s14.337 31.953 31.953 31.953C49.663 64 64 49.663 64 31.953S49.663 0 31.953 0m0 55.567c-12.931 0-23.52-10.589-23.52-23.614 0-12.931 10.589-23.52 23.52-23.52 13.025 0 23.614 10.589 23.614 23.52 0 13.025-10.589 23.614-23.614 23.614"/><use xlink:href="#b"/><use xlink:href="#b" y="15.93"/><path d="M17.335 39.918a6.64 6.64 0 0 1 6.653-6.653 6.653 6.653 0 1 1 0 13.306 6.64 6.64 0 0 1-6.653-6.653m0-15.93a6.64 6.64 0 0 1 6.653-6.653 6.64 6.64 0 0 1 6.653 6.653 6.64 6.64 0 0 1-6.653 6.653 6.64 6.64 0 0 1-6.653-6.653"/></g></symbol><defs><path id="b" d="M33.265 23.988a6.64 6.64 0 1 1 13.306 0 6.64 6.64 0 1 1-13.306 0"/></defs></svg>
|
||||
|
After Width: | Height: | Size: 992 B |
Reference in New Issue
Block a user