diff --git a/packages/backend/src/apps/zendesk/actions/create-ticket/fields.ts b/packages/backend/src/apps/zendesk/actions/create-ticket/fields.ts
new file mode 100644
index 00000000..390f5f3e
--- /dev/null
+++ b/packages/backend/src/apps/zendesk/actions/create-ticket/fields.ts
@@ -0,0 +1,301 @@
+export const fields = [
+ {
+ label: 'Subject',
+ key: 'subject',
+ type: 'string' as const,
+ required: true,
+ variables: true,
+ description: '',
+ },
+ {
+ label: 'Assignee',
+ key: 'assigneeId',
+ type: 'dropdown' as const,
+ required: false,
+ variables: true,
+ description:
+ 'Note: An error occurs if the assignee is not in the default group (or the specific group chosen below).',
+ source: {
+ type: 'query',
+ name: 'getDynamicData',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listUsers',
+ },
+ {
+ name: 'parameters.showUserRole',
+ value: 'true',
+ },
+ {
+ name: 'parameters.includeAdmins',
+ value: 'true',
+ },
+ ],
+ },
+ },
+ {
+ label: 'Collaborators',
+ key: 'collaborators',
+ type: 'dynamic' as const,
+ required: false,
+ description: '',
+ fields: [
+ {
+ label: 'Collaborator',
+ key: 'collaborator',
+ type: 'dropdown' as const,
+ required: false,
+ variables: true,
+ description: '',
+ source: {
+ type: 'query',
+ name: 'getDynamicData',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listUsers',
+ },
+ {
+ name: 'parameters.includeAdmins',
+ value: 'true',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ {
+ label: 'Collaborator Emails',
+ key: 'collaboratorEmails',
+ type: 'dynamic' as const,
+ required: false,
+ description:
+ 'You have the option to include individuals who are not Zendesk users as Collaborators by adding their email addresses here.',
+ fields: [
+ {
+ label: 'Collaborator Email',
+ key: 'collaboratorEmail',
+ type: 'string' as const,
+ required: false,
+ variables: true,
+ description: '',
+ },
+ ],
+ },
+ {
+ label: 'Group',
+ key: 'groupId',
+ type: 'dropdown' as const,
+ required: false,
+ variables: true,
+ description: 'Allocate this ticket to a specific group.',
+ source: {
+ type: 'query',
+ name: 'getDynamicData',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listGroups',
+ },
+ ],
+ },
+ },
+ {
+ label: 'Requester Name',
+ key: 'requesterName',
+ type: 'string' as const,
+ required: false,
+ variables: true,
+ description:
+ 'To specify the Requester, you need to fill in the Requester Name in this field and provide the Requestor Email in the next field.',
+ },
+ {
+ label: 'Requester Email',
+ key: 'requesterEmail',
+ type: 'string' as const,
+ required: false,
+ variables: true,
+ description:
+ 'To specify the Requester, you need to fill in the Requester Email in this field and provide the Requestor Name in the previous field.',
+ },
+ {
+ label: 'First Comment/Description Format',
+ key: 'format',
+ type: 'dropdown' as const,
+ required: false,
+ variables: true,
+ description: '',
+ options: [
+ { label: 'Plain Text', value: 'Plain Text' },
+ { label: 'HTML', value: 'HTML' },
+ ],
+ },
+ {
+ label: 'First Comment/Description',
+ key: 'comment',
+ type: 'string' as const,
+ required: true,
+ variables: true,
+ description: '',
+ },
+ {
+ label: 'Should the first comment be public?',
+ key: 'publicOrNot',
+ type: 'dropdown' as const,
+ required: false,
+ variables: true,
+ description: '',
+ options: [
+ { label: 'Yes', value: 'yes' },
+ { label: 'No', value: 'no' },
+ ],
+ },
+ {
+ label: 'Tags',
+ key: 'tags',
+ type: 'string' as const,
+ required: false,
+ variables: true,
+ description: 'A comma separated list of tags.',
+ },
+ {
+ label: 'Status',
+ key: 'status',
+ type: 'dropdown' as const,
+ required: false,
+ variables: true,
+ description: '',
+ options: [
+ { label: 'New', value: 'new' },
+ { label: 'Open', value: 'open' },
+ { label: 'Pending', value: 'pending' },
+ { label: 'Hold', value: 'hold' },
+ { label: 'Solved', value: 'solved' },
+ { label: 'Closed', value: 'closed' },
+ ],
+ },
+ {
+ label: 'Type',
+ key: 'type',
+ type: 'dropdown' as const,
+ required: false,
+ variables: true,
+ description: '',
+ options: [
+ { label: 'Problem', value: 'problem' },
+ { label: 'Incident', value: 'incident' },
+ { label: 'Question', value: 'question' },
+ { label: 'Task', value: 'task' },
+ ],
+ },
+ {
+ label: 'Due At',
+ key: 'dueAt',
+ type: 'string' as const,
+ required: false,
+ variables: true,
+ description: 'Limited to tickets typed as "task".',
+ },
+ {
+ label: 'Priority',
+ key: 'priority',
+ type: 'dropdown' as const,
+ required: false,
+ variables: true,
+ description: '',
+ options: [
+ { label: 'Urgent', value: 'urgent' },
+ { label: 'High', value: 'high' },
+ { label: 'Normal', value: 'normal' },
+ { label: 'Low', value: 'low' },
+ ],
+ },
+ {
+ label: 'Submitter',
+ key: 'submitterId',
+ type: 'dropdown' as const,
+ required: false,
+ variables: true,
+ description: '',
+ source: {
+ type: 'query',
+ name: 'getDynamicData',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listUsers',
+ },
+ {
+ name: 'parameters.includeAdmins',
+ value: 'false',
+ },
+ ],
+ },
+ },
+ {
+ label: 'Ticket Form',
+ key: 'ticketForm',
+ type: 'dropdown' as const,
+ required: false,
+ variables: true,
+ description:
+ 'When chosen, this will configure the form displayed for this ticket. Note: This field is solely relevant for Zendesk enterprise accounts.',
+ source: {
+ type: 'query',
+ name: 'getDynamicData',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listTicketForms',
+ },
+ ],
+ },
+ },
+ {
+ label: 'Sharing Agreements',
+ key: 'sharingAgreements',
+ type: 'dynamic' as const,
+ required: false,
+ description: '',
+ fields: [
+ {
+ label: 'Sharing Agreement',
+ key: 'sharingAgreement',
+ type: 'dropdown' as const,
+ required: false,
+ variables: true,
+ description: '',
+ source: {
+ type: 'query',
+ name: 'getDynamicData',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listSharingAgreements',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ {
+ label: 'Brand',
+ key: 'brandId',
+ type: 'dropdown' as const,
+ required: false,
+ variables: true,
+ description:
+ 'This applies exclusively to Zendesk customers subscribed to plans that include multi-brand support.',
+ source: {
+ type: 'query',
+ name: 'getDynamicData',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listBrands',
+ },
+ ],
+ },
+ },
+];
diff --git a/packages/backend/src/apps/zendesk/actions/create-ticket/index.ts b/packages/backend/src/apps/zendesk/actions/create-ticket/index.ts
new file mode 100644
index 00000000..942b54a5
--- /dev/null
+++ b/packages/backend/src/apps/zendesk/actions/create-ticket/index.ts
@@ -0,0 +1,101 @@
+import { IJSONArray, IJSONObject } from '@automatisch/types';
+import defineAction from '../../../../helpers/define-action';
+import { fields } from './fields';
+import isEmpty from 'lodash/isEmpty';
+
+type Payload = {
+ ticket: IJSONObject;
+};
+
+export default defineAction({
+ name: 'Create ticket',
+ key: 'createTicket',
+ description: 'Creates a new ticket',
+ arguments: fields,
+
+ async run($) {
+ const {
+ subject,
+ assigneeId,
+ groupId,
+ requesterName,
+ requesterEmail,
+ format,
+ comment,
+ publicOrNot,
+ status,
+ type,
+ dueAt,
+ priority,
+ submitterId,
+ ticketForm,
+ brandId,
+ } = $.step.parameters;
+
+ const collaborators = $.step.parameters.collaborators as IJSONArray;
+ const collaboratorIds = collaborators?.map(
+ (collaborator: IJSONObject) => collaborator.collaborator
+ );
+
+ const collaboratorEmails = $.step.parameters
+ .collaboratorEmails as IJSONArray;
+ const formattedCollaboratorEmails = collaboratorEmails?.map(
+ (collaboratorEmail: IJSONObject) => collaboratorEmail.collaboratorEmail
+ );
+
+ const formattedCollaborators = [
+ ...collaboratorIds,
+ ...formattedCollaboratorEmails,
+ ];
+
+ const sharingAgreements = $.step.parameters.sharingAgreements as IJSONArray;
+ const sharingAgreementIds = sharingAgreements
+ ?.filter(isEmpty)
+ .map((sharingAgreement: IJSONObject) =>
+ Number(sharingAgreement.sharingAgreement)
+ );
+
+ const tags = $.step.parameters.tags as string;
+ const formattedTags = tags.split(',');
+
+ const payload: Payload = {
+ ticket: {
+ subject,
+ assignee_id: assigneeId,
+ collaborators: formattedCollaborators,
+ group_id: groupId,
+ is_public: publicOrNot,
+ tags: formattedTags,
+ status,
+ type,
+ due_at: dueAt,
+ priority,
+ submitter_id: submitterId,
+ ticket_form_id: ticketForm,
+ sharing_agreement_ids: sharingAgreementIds,
+ brand_id: brandId,
+ },
+ };
+
+ if (requesterName && requesterEmail) {
+ payload.ticket.requester = {
+ name: requesterName,
+ email: requesterEmail,
+ };
+ }
+
+ if (format === 'HTML') {
+ payload.ticket.comment = {
+ html_body: comment,
+ };
+ } else {
+ payload.ticket.comment = {
+ body: comment,
+ };
+ }
+
+ const response = await $.http.post('/api/v2/tickets', payload);
+
+ $.setActionItem({ raw: response.data });
+ },
+});
diff --git a/packages/backend/src/apps/zendesk/actions/index.ts b/packages/backend/src/apps/zendesk/actions/index.ts
new file mode 100644
index 00000000..fa3cea56
--- /dev/null
+++ b/packages/backend/src/apps/zendesk/actions/index.ts
@@ -0,0 +1,3 @@
+import createTicket from './create-ticket';
+
+export default [createTicket];
diff --git a/packages/backend/src/apps/zendesk/dynamic-data/index.ts b/packages/backend/src/apps/zendesk/dynamic-data/index.ts
new file mode 100644
index 00000000..4cd089e7
--- /dev/null
+++ b/packages/backend/src/apps/zendesk/dynamic-data/index.ts
@@ -0,0 +1,13 @@
+import listUsers from './list-users';
+import listBrands from './list-brands';
+import listGroups from './list-groups';
+import listSharingAgreements from './list-sharing-agreements';
+import listTicketForms from './list-ticket-forms';
+
+export default [
+ listUsers,
+ listBrands,
+ listGroups,
+ listSharingAgreements,
+ listTicketForms,
+];
diff --git a/packages/backend/src/apps/zendesk/dynamic-data/list-brands/index.ts b/packages/backend/src/apps/zendesk/dynamic-data/list-brands/index.ts
new file mode 100644
index 00000000..3b6f43d8
--- /dev/null
+++ b/packages/backend/src/apps/zendesk/dynamic-data/list-brands/index.ts
@@ -0,0 +1,38 @@
+import { IGlobalVariable, IJSONObject } from '@automatisch/types';
+
+export default {
+ name: 'List brands',
+ key: 'listBrands',
+
+ async run($: IGlobalVariable) {
+ const brands: {
+ data: IJSONObject[];
+ } = {
+ data: [],
+ };
+
+ const params = {
+ page: 1,
+ per_page: 100,
+ };
+
+ let nextPage;
+ do {
+ const response = await $.http.get('/api/v2/brands', { params });
+ const allBrands = response?.data?.brands;
+ nextPage = response.data.next_page;
+ params.page = params.page + 1;
+
+ if (allBrands?.length) {
+ for (const brand of allBrands) {
+ brands.data.push({
+ value: brand.id,
+ name: brand.name,
+ });
+ }
+ }
+ } while (nextPage);
+
+ return brands;
+ },
+};
diff --git a/packages/backend/src/apps/zendesk/dynamic-data/list-groups/index.ts b/packages/backend/src/apps/zendesk/dynamic-data/list-groups/index.ts
new file mode 100644
index 00000000..02db70fb
--- /dev/null
+++ b/packages/backend/src/apps/zendesk/dynamic-data/list-groups/index.ts
@@ -0,0 +1,38 @@
+import { IGlobalVariable, IJSONObject } from '@automatisch/types';
+
+export default {
+ name: 'List groups',
+ key: 'listGroups',
+
+ async run($: IGlobalVariable) {
+ const groups: {
+ data: IJSONObject[];
+ } = {
+ data: [],
+ };
+ let hasMore;
+
+ const params = {
+ 'page[size]': 100,
+ 'page[after]': undefined as unknown as string,
+ };
+
+ do {
+ const response = await $.http.get('/api/v2/groups', { params });
+ const allGroups = response?.data?.groups;
+ hasMore = response?.data?.meta?.has_more;
+ params['page[after]'] = response.data.links?.after_cursor;
+
+ if (allGroups?.length) {
+ for (const group of allGroups) {
+ groups.data.push({
+ value: group.id,
+ name: group.name,
+ });
+ }
+ }
+ } while (hasMore);
+
+ return groups;
+ },
+};
diff --git a/packages/backend/src/apps/zendesk/dynamic-data/list-sharing-agreements/index.ts b/packages/backend/src/apps/zendesk/dynamic-data/list-sharing-agreements/index.ts
new file mode 100644
index 00000000..81c49835
--- /dev/null
+++ b/packages/backend/src/apps/zendesk/dynamic-data/list-sharing-agreements/index.ts
@@ -0,0 +1,40 @@
+import { IGlobalVariable, IJSONObject } from '@automatisch/types';
+
+export default {
+ name: 'List sharing agreements',
+ key: 'listSharingAgreements',
+
+ async run($: IGlobalVariable) {
+ const sharingAgreements: {
+ data: IJSONObject[];
+ } = {
+ data: [],
+ };
+
+ const params = {
+ page: 1,
+ per_page: 100,
+ };
+
+ let nextPage;
+ do {
+ const response = await $.http.get('/api/v2/sharing_agreements', {
+ params,
+ });
+ const allSharingAgreements = response?.data?.sharing_agreements;
+ nextPage = response.data.next_page;
+ params.page = params.page + 1;
+
+ if (allSharingAgreements?.length) {
+ for (const sharingAgreement of allSharingAgreements) {
+ sharingAgreements.data.push({
+ value: sharingAgreement.id,
+ name: sharingAgreement.name,
+ });
+ }
+ }
+ } while (nextPage);
+
+ return sharingAgreements;
+ },
+};
diff --git a/packages/backend/src/apps/zendesk/dynamic-data/list-ticket-forms/index.ts b/packages/backend/src/apps/zendesk/dynamic-data/list-ticket-forms/index.ts
new file mode 100644
index 00000000..1ec08ddf
--- /dev/null
+++ b/packages/backend/src/apps/zendesk/dynamic-data/list-ticket-forms/index.ts
@@ -0,0 +1,38 @@
+import { IGlobalVariable, IJSONObject } from '@automatisch/types';
+
+export default {
+ name: 'List ticket forms',
+ key: 'listTicketForms',
+
+ async run($: IGlobalVariable) {
+ const ticketForms: {
+ data: IJSONObject[];
+ } = {
+ data: [],
+ };
+
+ const params = {
+ page: 1,
+ per_page: 100,
+ };
+
+ let nextPage;
+ do {
+ const response = await $.http.get('/api/v2/ticket_forms', { params });
+ const allTicketForms = response?.data?.ticket_forms;
+ nextPage = response.data.next_page;
+ params.page = params.page + 1;
+
+ if (allTicketForms?.length) {
+ for (const ticketForm of allTicketForms) {
+ ticketForms.data.push({
+ value: ticketForm.id,
+ name: ticketForm.name,
+ });
+ }
+ }
+ } while (nextPage);
+
+ return ticketForms;
+ },
+};
diff --git a/packages/backend/src/apps/zendesk/dynamic-data/list-users/index.ts b/packages/backend/src/apps/zendesk/dynamic-data/list-users/index.ts
new file mode 100644
index 00000000..46b3bd89
--- /dev/null
+++ b/packages/backend/src/apps/zendesk/dynamic-data/list-users/index.ts
@@ -0,0 +1,43 @@
+import { IGlobalVariable, IJSONObject } from '@automatisch/types';
+
+export default {
+ name: 'List users',
+ key: 'listUsers',
+
+ async run($: IGlobalVariable) {
+ const users: {
+ data: IJSONObject[];
+ } = {
+ data: [],
+ };
+ let hasMore;
+ const showUserRole = $.step.parameters.showUserRole === 'true';
+ const includeAdmins = $.step.parameters.includeAdmins === 'true';
+ const role = includeAdmins ? ['admin', 'agent'] : ['agent'];
+
+ const params = {
+ 'page[size]': 100,
+ role,
+ 'page[after]': undefined as unknown as string,
+ };
+
+ do {
+ const response = await $.http.get('/api/v2/users', { params });
+ const allUsers = response?.data?.users;
+ hasMore = response?.data?.meta?.has_more;
+ params['page[after]'] = response.data.links?.after_cursor;
+
+ if (allUsers?.length) {
+ for (const user of allUsers) {
+ const name = showUserRole ? `${user.name} ${user.role}` : user.name;
+ users.data.push({
+ value: user.id,
+ name,
+ });
+ }
+ }
+ } while (hasMore);
+
+ return users;
+ },
+};
diff --git a/packages/backend/src/apps/zendesk/index.ts b/packages/backend/src/apps/zendesk/index.ts
index 64655b35..54810011 100644
--- a/packages/backend/src/apps/zendesk/index.ts
+++ b/packages/backend/src/apps/zendesk/index.ts
@@ -1,6 +1,8 @@
import defineApp from '../../helpers/define-app';
import addAuthHeader from './common/add-auth-headers';
import auth from './auth';
+import actions from './actions';
+import dynamicData from './dynamic-data';
export default defineApp({
name: 'Zendesk',
@@ -13,4 +15,6 @@ export default defineApp({
supportsConnections: true,
beforeRequest: [addAuthHeader],
auth,
+ actions,
+ dynamicData,
});
diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js
index 15e92517..f8c5db37 100644
--- a/packages/docs/pages/.vitepress/config.js
+++ b/packages/docs/pages/.vitepress/config.js
@@ -442,7 +442,10 @@ export default defineConfig({
text: 'Zendesk',
collapsible: true,
collapsed: true,
- items: [{ text: 'Connection', link: '/apps/zendesk/connection' }],
+ items: [
+ { text: 'Actions', link: '/apps/zendesk/actions' },
+ { text: 'Connection', link: '/apps/zendesk/connection' },
+ ],
},
],
'/': [
diff --git a/packages/docs/pages/apps/zendesk/actions.md b/packages/docs/pages/apps/zendesk/actions.md
new file mode 100644
index 00000000..f989c244
--- /dev/null
+++ b/packages/docs/pages/apps/zendesk/actions.md
@@ -0,0 +1,12 @@
+---
+favicon: /favicons/zendesk.svg
+items:
+ - name: Create ticket
+ desc: Creates a new ticket.
+---
+
+
+
+
diff --git a/packages/docs/pages/guide/available-apps.md b/packages/docs/pages/guide/available-apps.md
index cda867be..e2b6b154 100644
--- a/packages/docs/pages/guide/available-apps.md
+++ b/packages/docs/pages/guide/available-apps.md
@@ -46,3 +46,4 @@ The following integrations are currently supported by Automatisch.
- [Webhooks](/apps/webhooks/triggers)
- [WordPress](/apps/wordpress/triggers)
- [Youtube](/apps/youtube/triggers)
+- [Zendesk](/apps/zendesk/actions)