diff --git a/packages/backend/src/apps/monday/actions/create-board/index.js b/packages/backend/src/apps/monday/actions/create-board/index.js
new file mode 100644
index 00000000..2afd0d85
--- /dev/null
+++ b/packages/backend/src/apps/monday/actions/create-board/index.js
@@ -0,0 +1,70 @@
+import defineAction from '../../../../helpers/define-action.js';
+
+export default defineAction({
+ name: 'Create board',
+ key: 'createBoard',
+ description: 'Creates a new board.',
+ arguments: [
+ {
+ label: 'Board Name',
+ key: 'boardName',
+ type: 'string',
+ required: true,
+ description: 'Title for the board.',
+ variables: true,
+ },
+ {
+ label: 'Board Kind',
+ key: 'boardKind',
+ type: 'dropdown',
+ required: true,
+ description: '',
+ variables: true,
+ options: [
+ {
+ label: 'Main',
+ value: 'public',
+ },
+ {
+ label: 'Private',
+ value: 'private',
+ },
+ {
+ label: 'Shareable',
+ value: 'share',
+ },
+ ],
+ },
+ {
+ label: 'Template ID',
+ key: 'templateId',
+ type: 'string',
+ required: false,
+ description:
+ "When you switch on developer mode, you'll spot the template IDs in your template store. Additionally, you have the option to utilize the Board ID from any board you've saved as a template.",
+ variables: true,
+ },
+ ],
+
+ async run($) {
+ const { boardName, boardKind, templateId } = $.step.parameters;
+
+ const body = {
+ query: `mutation {
+ create_board (board_name: "${boardName}", board_kind: ${boardKind}${
+ templateId ? `, template_id: ${templateId}` : ''
+ }) {
+ id
+ name
+ board_kind
+ }
+ }`,
+ };
+
+ const { data } = await $.http.post('/', body);
+
+ $.setActionItem({
+ raw: data,
+ });
+ },
+});
diff --git a/packages/backend/src/apps/monday/actions/create-column/index.js b/packages/backend/src/apps/monday/actions/create-column/index.js
new file mode 100644
index 00000000..2ea3d0ae
--- /dev/null
+++ b/packages/backend/src/apps/monday/actions/create-column/index.js
@@ -0,0 +1,98 @@
+import defineAction from '../../../../helpers/define-action.js';
+
+export default defineAction({
+ name: 'Create column',
+ key: 'createColumn',
+ description: 'Creates a new column in a board.',
+ arguments: [
+ {
+ label: 'Board',
+ key: 'boardId',
+ type: 'dropdown',
+ required: true,
+ description: '',
+ variables: true,
+ source: {
+ type: 'query',
+ name: 'getDynamicData',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listBoards',
+ },
+ ],
+ },
+ },
+ {
+ label: 'Column Title',
+ key: 'columnTitle',
+ type: 'string',
+ required: true,
+ description: '',
+ variables: true,
+ },
+ {
+ label: 'Column Type',
+ key: 'columnType',
+ type: 'dropdown',
+ required: true,
+ description: '',
+ variables: true,
+ options: [
+ { label: 'Button', value: 'button' },
+ { label: 'Checkbox', value: 'checkbox' },
+ { label: 'Color Picker', value: 'color_picker' },
+ { label: 'Connect Boards', value: 'board_relation' },
+ { label: 'Country', value: 'country' },
+ { label: 'Creation Log', value: 'creation_log' },
+ { label: 'Date', value: 'date' },
+ { label: 'Dependency', value: 'dependency' },
+ { label: 'Dropdown', value: 'dropdown' },
+ { label: 'Email', value: 'email' },
+ { label: 'Files', value: 'file' },
+ { label: 'Formula', value: 'formula' },
+ { label: 'Hour', value: 'hour' },
+ { label: 'Item ID', value: 'item_id' },
+ { label: 'Last Updated', value: 'last_updated' },
+ { label: 'Link', value: 'link' },
+ { label: 'Location', value: 'location' },
+ { label: 'Long Text', value: 'long_text' },
+ { label: 'Mirror', value: 'mirror' },
+ { label: 'monday Doc', value: 'doc' },
+ { label: 'Name', value: 'name' },
+ { label: 'Numbers', value: 'numbers' },
+ { label: 'People', value: 'people' },
+ { label: 'Phone', value: 'phone' },
+ { label: 'Rating', value: 'rating' },
+ { label: 'Status', value: 'status' },
+ { label: 'Tags', value: 'tags' },
+ { label: 'Text', value: 'text' },
+ { label: 'Timeline', value: 'timeline' },
+ { label: 'Time Tracking', value: 'time_tracking' },
+ { label: 'Vote', value: 'vote' },
+ { label: 'Week', value: 'week' },
+ { label: 'World Clock', value: 'world_clock' },
+ ],
+ },
+ ],
+
+ async run($) {
+ const { boardId, columnTitle, columnType } = $.step.parameters;
+
+ const body = {
+ query: `
+ mutation{
+ create_column (board_id: ${boardId}, title: "${columnTitle}", column_type: ${columnType}) {
+ id
+ title
+ }
+ }`,
+ };
+
+ const { data } = await $.http.post('/', body);
+
+ $.setActionItem({
+ raw: data,
+ });
+ },
+});
diff --git a/packages/backend/src/apps/monday/actions/create-item/index.js b/packages/backend/src/apps/monday/actions/create-item/index.js
new file mode 100644
index 00000000..605148ee
--- /dev/null
+++ b/packages/backend/src/apps/monday/actions/create-item/index.js
@@ -0,0 +1,112 @@
+import defineAction from '../../../../helpers/define-action.js';
+
+export default defineAction({
+ name: 'Create item',
+ key: 'createItem',
+ description: 'Creates a new item in a board.',
+ arguments: [
+ {
+ label: 'Board',
+ key: 'boardId',
+ type: 'dropdown',
+ required: true,
+ description: '',
+ variables: true,
+ source: {
+ type: 'query',
+ name: 'getDynamicData',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listBoards',
+ },
+ ],
+ },
+ },
+ {
+ label: 'Group',
+ key: 'groupId',
+ type: 'dropdown',
+ required: false,
+ description: '',
+ dependsOn: ['parameters.boardId'],
+ variables: true,
+ source: {
+ type: 'query',
+ name: 'getDynamicData',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listGroups',
+ },
+ {
+ name: 'parameters.boardId',
+ value: '{parameters.boardId}',
+ },
+ ],
+ },
+ },
+ {
+ label: 'Item Name',
+ key: 'itemName',
+ type: 'string',
+ required: true,
+ description: '',
+ variables: true,
+ },
+ {
+ label: 'Subitem Names',
+ key: 'subitemNames',
+ type: 'dynamic',
+ required: false,
+ description: '',
+ fields: [
+ {
+ label: 'Subitem Name',
+ key: 'subitemName',
+ type: 'string',
+ required: false,
+ description: '',
+ variables: true,
+ },
+ ],
+ },
+ ],
+
+ async run($) {
+ const { boardId, groupId, itemName, subitemNames } = $.step.parameters;
+ const allSubitems = subitemNames.map((entry) => entry.subitemName);
+
+ const body = {
+ query: `
+ mutation {
+ create_item (board_id: ${boardId}${
+ groupId ? `, group_id: "${groupId}"` : ''
+ }, item_name: "${itemName}") {
+ id
+ }
+ }`,
+ };
+
+ const { data } = await $.http.post('/', body);
+
+ const itemId = data.data.create_item.id;
+
+ for (let subitemName of allSubitems) {
+ let body = {
+ query: `
+ mutation {
+ create_subitem (parent_item_id:${itemId}, item_name:"${subitemName}") {
+ id
+ }
+ }`,
+ };
+
+ await $.http.post('/', body);
+ }
+
+ $.setActionItem({
+ raw: data,
+ });
+ },
+});
diff --git a/packages/backend/src/apps/monday/actions/index.js b/packages/backend/src/apps/monday/actions/index.js
new file mode 100644
index 00000000..8f12938e
--- /dev/null
+++ b/packages/backend/src/apps/monday/actions/index.js
@@ -0,0 +1,5 @@
+import createBoard from './create-board/index.js';
+import createColumn from './create-column/index.js';
+import createItem from './create-item/index.js';
+
+export default [createBoard, createColumn, createItem];
diff --git a/packages/backend/src/apps/monday/assets/favicon.svg b/packages/backend/src/apps/monday/assets/favicon.svg
new file mode 100644
index 00000000..5d46afcf
--- /dev/null
+++ b/packages/backend/src/apps/monday/assets/favicon.svg
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file
diff --git a/packages/backend/src/apps/monday/auth/index.js b/packages/backend/src/apps/monday/auth/index.js
new file mode 100644
index 00000000..f1280da5
--- /dev/null
+++ b/packages/backend/src/apps/monday/auth/index.js
@@ -0,0 +1,21 @@
+import verifyCredentials from './verify-credentials.js';
+import isStillVerified from './is-still-verified.js';
+
+export default {
+ fields: [
+ {
+ key: 'apiToken',
+ label: 'API Token',
+ type: 'string',
+ required: true,
+ readOnly: false,
+ value: null,
+ placeholder: null,
+ description: 'Monday.com API token of your account.',
+ clickToCopy: false,
+ },
+ ],
+
+ verifyCredentials,
+ isStillVerified,
+};
diff --git a/packages/backend/src/apps/monday/auth/is-still-verified.js b/packages/backend/src/apps/monday/auth/is-still-verified.js
new file mode 100644
index 00000000..6663679a
--- /dev/null
+++ b/packages/backend/src/apps/monday/auth/is-still-verified.js
@@ -0,0 +1,8 @@
+import verifyCredentials from './verify-credentials.js';
+
+const isStillVerified = async ($) => {
+ await verifyCredentials($);
+ return true;
+};
+
+export default isStillVerified;
diff --git a/packages/backend/src/apps/monday/auth/verify-credentials.js b/packages/backend/src/apps/monday/auth/verify-credentials.js
new file mode 100644
index 00000000..7b3b05e4
--- /dev/null
+++ b/packages/backend/src/apps/monday/auth/verify-credentials.js
@@ -0,0 +1,18 @@
+const verifyCredentials = async ($) => {
+ const body = {
+ query: 'query { me { name, email } }',
+ };
+
+ const { data } = await $.http.post('/', body);
+
+ const screenName = [data.data.me.name, data.data.me.email]
+ .filter(Boolean)
+ .join(' @ ');
+
+ await $.auth.set({
+ screenName,
+ apiToken: $.auth.data.apiToken,
+ });
+};
+
+export default verifyCredentials;
diff --git a/packages/backend/src/apps/monday/common/add-auth-header.js b/packages/backend/src/apps/monday/common/add-auth-header.js
new file mode 100644
index 00000000..19f25cc5
--- /dev/null
+++ b/packages/backend/src/apps/monday/common/add-auth-header.js
@@ -0,0 +1,9 @@
+const addAuthHeader = ($, requestConfig) => {
+ if ($.auth.data?.apiToken) {
+ requestConfig.headers.Authorization = $.auth.data.apiToken;
+ }
+
+ return requestConfig;
+};
+
+export default addAuthHeader;
diff --git a/packages/backend/src/apps/monday/dynamic-data/index.js b/packages/backend/src/apps/monday/dynamic-data/index.js
new file mode 100644
index 00000000..c686d52a
--- /dev/null
+++ b/packages/backend/src/apps/monday/dynamic-data/index.js
@@ -0,0 +1,4 @@
+import listBoards from './list-boards/index.js';
+import listGroups from './list-groups/index.js';
+
+export default [listBoards, listGroups];
diff --git a/packages/backend/src/apps/monday/dynamic-data/list-boards/index.js b/packages/backend/src/apps/monday/dynamic-data/list-boards/index.js
new file mode 100644
index 00000000..b804d447
--- /dev/null
+++ b/packages/backend/src/apps/monday/dynamic-data/list-boards/index.js
@@ -0,0 +1,37 @@
+export default {
+ name: 'List boards',
+ key: 'listBoards',
+
+ async run($) {
+ const boards = {
+ data: [],
+ };
+
+ const body = {
+ query: `
+ query {
+ boards {
+ id
+ name
+ type
+ }
+ }
+ `,
+ };
+
+ const { data } = await $.http.post('/', body);
+
+ if (data.data.boards?.length) {
+ for (const board of data.data.boards) {
+ if (board.type === 'board') {
+ boards.data.push({
+ value: board.id,
+ name: board.name,
+ });
+ }
+ }
+ }
+
+ return boards;
+ },
+};
diff --git a/packages/backend/src/apps/monday/dynamic-data/list-groups/index.js b/packages/backend/src/apps/monday/dynamic-data/list-groups/index.js
new file mode 100644
index 00000000..19f4db46
--- /dev/null
+++ b/packages/backend/src/apps/monday/dynamic-data/list-groups/index.js
@@ -0,0 +1,40 @@
+export default {
+ name: 'List groups',
+ key: 'listGroups',
+
+ async run($) {
+ const groups = {
+ data: [],
+ };
+ const boardId = $.step.parameters.boardId;
+
+ if (!boardId) {
+ return groups;
+ }
+
+ const body = {
+ query: `query {
+ boards (ids: ${boardId}) {
+ groups {
+ title
+ id
+ }
+ }
+ }
+ `,
+ };
+
+ const { data } = await $.http.post('/', body);
+
+ if (data.data.boards[0]?.groups.length) {
+ for (const group of data.data.boards[0].groups) {
+ groups.data.push({
+ value: group.id,
+ name: group.title,
+ });
+ }
+ }
+
+ return groups;
+ },
+};
diff --git a/packages/backend/src/apps/monday/index.js b/packages/backend/src/apps/monday/index.js
new file mode 100644
index 00000000..2ee81ebb
--- /dev/null
+++ b/packages/backend/src/apps/monday/index.js
@@ -0,0 +1,22 @@
+import defineApp from '../../helpers/define-app.js';
+import addAuthHeader from './common/add-auth-header.js';
+import auth from './auth/index.js';
+import triggers from './triggers/index.js';
+import actions from './actions/index.js';
+import dynamicData from './dynamic-data/index.js';
+
+export default defineApp({
+ name: 'Monday',
+ key: 'monday',
+ iconUrl: '{BASE_URL}/apps/monday/assets/favicon.svg',
+ authDocUrl: '{DOCS_URL}/apps/monday/connection',
+ supportsConnections: true,
+ baseUrl: 'https://monday.com',
+ apiBaseUrl: 'https://api.monday.com/v2',
+ primaryColor: 'F62B54',
+ beforeRequest: [addAuthHeader],
+ auth,
+ triggers,
+ actions,
+ dynamicData,
+});
diff --git a/packages/backend/src/apps/monday/triggers/index.js b/packages/backend/src/apps/monday/triggers/index.js
new file mode 100644
index 00000000..6ffc8710
--- /dev/null
+++ b/packages/backend/src/apps/monday/triggers/index.js
@@ -0,0 +1,4 @@
+import newBoards from './new-boards/index.js';
+import newUsers from './new-users/index.js';
+
+export default [newBoards, newUsers];
diff --git a/packages/backend/src/apps/monday/triggers/new-boards/index.js b/packages/backend/src/apps/monday/triggers/new-boards/index.js
new file mode 100644
index 00000000..599d7d2e
--- /dev/null
+++ b/packages/backend/src/apps/monday/triggers/new-boards/index.js
@@ -0,0 +1,31 @@
+import defineTrigger from '../../../../helpers/define-trigger.js';
+
+export default defineTrigger({
+ name: 'New board',
+ key: 'newBoard',
+ pollInterval: 15,
+ description: 'Triggers when a new board is created.',
+
+ async run($) {
+ const body = {
+ query: 'query { boards { id, name, type } }',
+ };
+
+ const { data } = await $.http.post('/', body);
+
+ if (!data?.data?.boards?.length) {
+ return;
+ }
+
+ for (const board of data.data.boards) {
+ if (board.type === 'board') {
+ $.pushTriggerItem({
+ raw: board,
+ meta: {
+ internalId: board.id,
+ },
+ });
+ }
+ }
+ },
+});
diff --git a/packages/backend/src/apps/monday/triggers/new-users/index.js b/packages/backend/src/apps/monday/triggers/new-users/index.js
new file mode 100644
index 00000000..aa0e474b
--- /dev/null
+++ b/packages/backend/src/apps/monday/triggers/new-users/index.js
@@ -0,0 +1,29 @@
+import defineTrigger from '../../../../helpers/define-trigger.js';
+
+export default defineTrigger({
+ name: 'New users',
+ key: 'newUsers',
+ pollInterval: 15,
+ description: 'Triggers when a new user joins your account.',
+
+ async run($) {
+ const body = {
+ query: 'query { users { id name } }',
+ };
+
+ const { data } = await $.http.post('/', body);
+
+ if (!data.data?.users?.length) {
+ return;
+ }
+
+ for (const user of data.data.users.reverse()) {
+ $.pushTriggerItem({
+ raw: user,
+ meta: {
+ internalId: user.id,
+ },
+ });
+ }
+ },
+});
diff --git a/packages/backend/src/models/__snapshots__/app.test.js.snap b/packages/backend/src/models/__snapshots__/app.test.js.snap
index 624194aa..33000024 100644
--- a/packages/backend/src/models/__snapshots__/app.test.js.snap
+++ b/packages/backend/src/models/__snapshots__/app.test.js.snap
@@ -40,6 +40,7 @@ exports[`App model > list should have list of applications keys 1`] = `
"mattermost",
"miro",
"mistral-ai",
+ "monday",
"notion",
"ntfy",
"odoo",
diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js
index c842386e..dde76d78 100644
--- a/packages/docs/pages/.vitepress/config.js
+++ b/packages/docs/pages/.vitepress/config.js
@@ -340,6 +340,16 @@ export default defineConfig({
{ text: 'Connection', link: '/apps/mistral-ai/connection' },
],
},
+ {
+ text: 'Monday',
+ collapsible: true,
+ collapsed: true,
+ items: [
+ { text: 'Triggers', link: '/apps/monday/triggers' },
+ { text: 'Actions', link: '/apps/monday/actions' },
+ { text: 'Connection', link: '/apps/monday/connection' },
+ ],
+ },
{
text: 'Notion',
collapsible: true,
diff --git a/packages/docs/pages/apps/monday/actions.md b/packages/docs/pages/apps/monday/actions.md
new file mode 100644
index 00000000..f2034ed2
--- /dev/null
+++ b/packages/docs/pages/apps/monday/actions.md
@@ -0,0 +1,16 @@
+---
+favicon: /favicons/monday.svg
+items:
+ - name: Create board
+ desc: Creates a new board.
+ - name: Create column
+ desc: Creates a new column in a board.
+ - name: Create item
+ desc: Creates a new item in a board.
+---
+
+
+
+
diff --git a/packages/docs/pages/apps/monday/connection.md b/packages/docs/pages/apps/monday/connection.md
new file mode 100644
index 00000000..2087dcd9
--- /dev/null
+++ b/packages/docs/pages/apps/monday/connection.md
@@ -0,0 +1,12 @@
+# Monday
+
+:::info
+This page explains the steps you need to follow to set up the Monday
+connection in Automatisch. If any of the steps are outdated, please let us know!
+:::
+
+1. Login to your Monday account: [https://monday.com](https://monday.com).
+2. Click on the account logo and go to the **Developers** page.
+3. Click on the **My access tokens** tab on the left.
+4. Click on the **Show** button and copy the access token from the page to the `API Token` field on Automatisch.
+5. Now, you can start using the Monday connection with Automatisch.
diff --git a/packages/docs/pages/apps/monday/triggers.md b/packages/docs/pages/apps/monday/triggers.md
new file mode 100644
index 00000000..f1b79fb8
--- /dev/null
+++ b/packages/docs/pages/apps/monday/triggers.md
@@ -0,0 +1,14 @@
+---
+favicon: /favicons/monday.svg
+items:
+ - name: New board
+ desc: Triggers when a new board is created.
+ - name: New users
+ desc: Triggers when a new user joins your account.
+---
+
+
+
+
diff --git a/packages/docs/pages/guide/available-apps.md b/packages/docs/pages/guide/available-apps.md
index 81cb3c03..7bb4d57e 100644
--- a/packages/docs/pages/guide/available-apps.md
+++ b/packages/docs/pages/guide/available-apps.md
@@ -36,6 +36,7 @@ The following integrations are currently supported by Automatisch.
- [Mattermost](/apps/mattermost/actions)
- [Miro](/apps/miro/actions)
- [Mistral AI](/apps/mistral-ai/actions)
+- [Monday](/apps/monday/triggers)
- [Notion](/apps/notion/triggers)
- [Ntfy](/apps/ntfy/actions)
- [Odoo](/apps/odoo/actions)
diff --git a/packages/docs/pages/public/favicons/monday.svg b/packages/docs/pages/public/favicons/monday.svg
new file mode 100644
index 00000000..5d46afcf
--- /dev/null
+++ b/packages/docs/pages/public/favicons/monday.svg
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file