diff --git a/packages/nodes-base/credentials/DriftApi.credentials.ts b/packages/nodes-base/credentials/DriftApi.credentials.ts
new file mode 100644
index 0000000000..83f6374c7a
--- /dev/null
+++ b/packages/nodes-base/credentials/DriftApi.credentials.ts
@@ -0,0 +1,18 @@
+import {
+ ICredentialType,
+ NodePropertyTypes,
+} from 'n8n-workflow';
+
+export class DriftApi implements ICredentialType {
+ name = 'driftApi';
+ displayName = 'Drift API';
+ properties = [
+ {
+ displayName: 'Personal Access Token',
+ name: 'accessToken',
+ type: 'string' as NodePropertyTypes,
+ default: '',
+ description: 'Visit your account details page, and grab the Access Token. See Drift auth.'
+ },
+ ];
+}
diff --git a/packages/nodes-base/nodes/Drift/ContactDescription.ts b/packages/nodes-base/nodes/Drift/ContactDescription.ts
new file mode 100644
index 0000000000..ad5bc14d5a
--- /dev/null
+++ b/packages/nodes-base/nodes/Drift/ContactDescription.ts
@@ -0,0 +1,206 @@
+import { INodeProperties } from 'n8n-workflow';
+
+export const contactOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a contact',
+ },
+ {
+ name: 'Custom Attributes',
+ value: 'getCustomAttributes',
+ description: 'Get custom attributes',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a contact',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Get a contact',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a contact',
+ },
+ ],
+ default: 'create',
+ description: 'The operation to perform.',
+ },
+] as INodeProperties[];
+
+export const contactFields = [
+
+/* -------------------------------------------------------------------------- */
+/* contact:create */
+/* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ description: 'The email of the Contact',
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ description: 'The name of the Contact',
+ },
+ {
+ displayName: 'Phone',
+ name: 'phone',
+ type: 'string',
+ default: '',
+ description: 'The phone number associated with the Contact',
+ },
+ ],
+ },
+/* -------------------------------------------------------------------------- */
+/* contact:update */
+/* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Contact ID',
+ name: 'contactId',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'update',
+ ]
+ },
+ },
+ description: 'Unique identifier for the contact.',
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ default: '',
+ description: 'The email of the Contact',
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ description: 'The name of the Contact',
+ },
+ {
+ displayName: 'Phone',
+ name: 'phone',
+ type: 'string',
+ default: '',
+ description: 'The phone number associated with the Contact',
+ },
+ ]
+ },
+/* -------------------------------------------------------------------------- */
+/* contact:get */
+/* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Contact ID',
+ name: 'contactId',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'get',
+ ]
+ },
+ },
+ description: 'Unique identifier for the contact.',
+ },
+/* -------------------------------------------------------------------------- */
+/* contact:delete */
+/* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Contact ID',
+ name: 'contactId',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'delete',
+ ]
+ },
+ },
+ description: 'Unique identifier for the contact.',
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Drift/ContactInterface.ts b/packages/nodes-base/nodes/Drift/ContactInterface.ts
new file mode 100644
index 0000000000..d228a1c880
--- /dev/null
+++ b/packages/nodes-base/nodes/Drift/ContactInterface.ts
@@ -0,0 +1,6 @@
+export interface IContact {
+ email?: string;
+ name?: string;
+ phone?: string;
+ tags?: string[];
+}
diff --git a/packages/nodes-base/nodes/Drift/Drift.node.ts b/packages/nodes-base/nodes/Drift/Drift.node.ts
new file mode 100644
index 0000000000..95566dfb60
--- /dev/null
+++ b/packages/nodes-base/nodes/Drift/Drift.node.ts
@@ -0,0 +1,130 @@
+import {
+ IExecuteFunctions,
+} from 'n8n-core';
+import {
+ INodeTypeDescription,
+ INodeType,
+ INodeExecutionData,
+ IDataObject,
+} from 'n8n-workflow';
+import {
+ driftApiRequest,
+} from './GenericFunctions';
+import {
+ contactFields,
+ contactOperations,
+} from './ContactDescription';
+import {
+ IContact,
+} from './ContactInterface';
+
+export class Drift implements INodeType {
+ description: INodeTypeDescription = {
+ displayName: 'Drift',
+ name: 'drift',
+ icon: 'file:drift.png',
+ group: ['output'],
+ version: 1,
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
+ description: 'Consume Drift API',
+ defaults: {
+ name: 'Drift ',
+ color: '#404040',
+ },
+ inputs: ['main'],
+ outputs: ['main'],
+ credentials: [
+ {
+ name: 'driftApi',
+ required: true,
+ },
+ ],
+ properties: [
+ {
+ displayName: 'Resource',
+ name: 'resource',
+ type: 'options',
+ options: [
+ {
+ name: 'Contact',
+ value: 'contact',
+ },
+ ],
+ default: 'contact',
+ description: 'Resource to consume.',
+ },
+ ...contactOperations,
+ ...contactFields,
+ ],
+ };
+
+ async execute(this: IExecuteFunctions): Promise {
+ const items = this.getInputData();
+ const returnData: IDataObject[] = [];
+ const length = items.length as unknown as number;
+ let responseData;
+ const qs: IDataObject = {};
+ const resource = this.getNodeParameter('resource', 0) as string;
+ const operation = this.getNodeParameter('operation', 0) as string;
+ for (let i = 0; i < length; i++) {
+ if (resource === 'contact') {
+ //https://devdocs.drift.com/docs/creating-a-contact
+ if (operation === 'create') {
+ const email = this.getNodeParameter('email', i) as string;
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+ const body: IContact = {
+ email,
+ };
+ if (additionalFields.name) {
+ body.name = additionalFields.name as string;
+ }
+ if (additionalFields.phone) {
+ body.phone = additionalFields.phone as string;
+ }
+ responseData = await driftApiRequest.call(this, 'POST', '/contacts', { attributes: body });
+ responseData = responseData.data
+ }
+ //https://devdocs.drift.com/docs/updating-a-contact
+ if (operation === 'update') {
+ const contactId = this.getNodeParameter('contactId', i) as string;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+ const body: IContact = {};
+ if (updateFields.name) {
+ body.name = updateFields.name as string;
+ }
+ if (updateFields.phone) {
+ body.phone = updateFields.phone as string;
+ }
+ if (updateFields.email) {
+ body.email = updateFields.email as string;
+ }
+ responseData = await driftApiRequest.call(this, 'PATCH', `/contacts/${contactId}`, { attributes: body });
+ responseData = responseData.data
+ }
+ //https://devdocs.drift.com/docs/retrieving-contact
+ if (operation === 'get') {
+ const contactId = this.getNodeParameter('contactId', i) as string;
+ responseData = await driftApiRequest.call(this, 'GET', `/contacts/${contactId}`);
+ responseData = responseData.data
+ }
+ //https://devdocs.drift.com/docs/listing-custom-attributes
+ if (operation === 'getCustomAttributes') {
+ responseData = await driftApiRequest.call(this, 'GET', '/contacts/attributes');
+ responseData = responseData.data.properties;
+ }
+ //https://devdocs.drift.com/docs/removing-a-contact
+ if (operation === 'delete') {
+ const contactId = this.getNodeParameter('contactId', i) as string;
+ responseData = await driftApiRequest.call(this, 'DELETE', `/contacts/${contactId}`);
+ responseData = { success: true };
+ }
+ }
+ if (Array.isArray(responseData)) {
+ returnData.push.apply(returnData, responseData as IDataObject[]);
+ } else {
+ returnData.push(responseData as IDataObject);
+ }
+ }
+ return [this.helpers.returnJsonArray(returnData)];
+ }
+}
diff --git a/packages/nodes-base/nodes/Drift/GenericFunctions.ts b/packages/nodes-base/nodes/Drift/GenericFunctions.ts
new file mode 100644
index 0000000000..3f798398c4
--- /dev/null
+++ b/packages/nodes-base/nodes/Drift/GenericFunctions.ts
@@ -0,0 +1,50 @@
+import { OptionsWithUri } from 'request';
+
+import {
+ IExecuteFunctions,
+ ILoadOptionsFunctions,
+} from 'n8n-core';
+
+import {
+ IDataObject,
+ IHookFunctions,
+ IWebhookFunctions
+} from 'n8n-workflow';
+
+export async function driftApiRequest(this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any
+
+ const credentials = this.getCredentials('driftApi');
+
+ if (credentials === undefined) {
+ throw new Error('No credentials got returned!');
+ }
+
+ const endpoint = 'https://driftapi.com';
+
+ let options: OptionsWithUri = {
+ headers: {
+ Authorization: `Bearer ${credentials.accessToken}`,
+ },
+ method,
+ body,
+ qs: query,
+ uri: uri || `${endpoint}${resource}`,
+ json: true
+ };
+ if (!Object.keys(body).length) {
+ delete options.form;
+ }
+ if (!Object.keys(query).length) {
+ delete options.qs;
+ }
+ options = Object.assign({}, options, option);
+ try {
+ return await this.helpers.request!(options);
+ } catch (error) {
+ if (error.response) {
+ const errorMessage = error.message || (error.response.body && error.response.body.message )
+ throw new Error(`Drift error response [${error.statusCode}]: ${errorMessage}`);
+ }
+ throw error;
+ }
+}
diff --git a/packages/nodes-base/nodes/Drift/drift.png b/packages/nodes-base/nodes/Drift/drift.png
new file mode 100644
index 0000000000..82050d2ca8
Binary files /dev/null and b/packages/nodes-base/nodes/Drift/drift.png differ
diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json
index 876d16ff62..a99c9db2e3 100644
--- a/packages/nodes-base/package.json
+++ b/packages/nodes-base/package.json
@@ -42,6 +42,7 @@
"dist/credentials/CopperApi.credentials.js",
"dist/credentials/DisqusApi.credentials.js",
"dist/credentials/DropboxApi.credentials.js",
+ "dist/credentials/DriftApi.credentials.js",
"dist/credentials/EventbriteApi.credentials.js",
"dist/credentials/FreshdeskApi.credentials.js",
"dist/credentials/FileMaker.credentials.js",
@@ -129,6 +130,7 @@
"dist/nodes/Disqus/Disqus.node.js",
"dist/nodes/Dropbox/Dropbox.node.js",
"dist/nodes/DateTime.node.js",
+ "dist/nodes/Drift/Drift.node.js",
"dist/nodes/EditImage.node.js",
"dist/nodes/EmailReadImap.node.js",
"dist/nodes/EmailSend.node.js",