diff --git a/cypress/composables/executions.ts b/cypress/composables/executions.ts
new file mode 100644
index 00000000000..cd07eb7e6a2
--- /dev/null
+++ b/cypress/composables/executions.ts
@@ -0,0 +1,29 @@
+/**
+ * Getters
+ */
+
+export const getExecutionsSidebar = () => cy.getByTestId('executions-sidebar');
+
+export const getWorkflowExecutionPreviewIframe = () => cy.getByTestId('workflow-preview-iframe');
+
+export const getExecutionPreviewBody = () =>
+ getWorkflowExecutionPreviewIframe()
+ .its('0.contentDocument.body')
+ .then((el) => cy.wrap(el));
+
+export const getExecutionPreviewBodyNodes = () =>
+ getExecutionPreviewBody().findChildByTestId('canvas-node');
+
+export const getExecutionPreviewBodyNodesByName = (name: string) =>
+ getExecutionPreviewBody().findChildByTestId('canvas-node').filter(`[data-name="${name}"]`).eq(0);
+
+export function getExecutionPreviewOutputPanelRelatedExecutionLink() {
+ return getExecutionPreviewBody().findChildByTestId('related-execution-link');
+}
+
+/**
+ * Actions
+ */
+
+export const openExecutionPreviewNode = (name: string) =>
+ getExecutionPreviewBodyNodesByName(name).dblclick();
diff --git a/cypress/composables/ndv.ts b/cypress/composables/ndv.ts
index b7ea33cb69e..05d783ec5e8 100644
--- a/cypress/composables/ndv.ts
+++ b/cypress/composables/ndv.ts
@@ -48,10 +48,38 @@ export function getOutputTableRow(row: number) {
return getOutputTableRows().eq(row);
}
+export function getOutputTableHeaders() {
+ return getOutputPanelDataContainer().find('table thead th');
+}
+
+export function getOutputTableHeaderByText(text: string) {
+ return getOutputTableHeaders().contains(text);
+}
+
+export function getOutputTbodyCell(row: number, col: number) {
+ return getOutputTableRows().eq(row).find('td').eq(col);
+}
+
+export function getOutputRunSelector() {
+ return getOutputPanel().findChildByTestId('run-selector');
+}
+
+export function getOutputRunSelectorInput() {
+ return getOutputRunSelector().find('input');
+}
+
export function getOutputPanelTable() {
return getOutputPanelDataContainer().get('table');
}
+export function getOutputPanelItemsCount() {
+ return getOutputPanel().getByTestId('ndv-items-count');
+}
+
+export function getOutputPanelRelatedExecutionLink() {
+ return getOutputPanel().getByTestId('related-execution-link');
+}
+
/**
* Actions
*/
@@ -90,3 +118,8 @@ export function setParameterSelectByContent(name: string, content: string) {
getParameterInputByName(name).realClick();
getVisibleSelect().find('.option-headline').contains(content).click();
}
+
+export function changeOutputRunSelector(runName: string) {
+ getOutputRunSelector().click();
+ getVisibleSelect().find('.el-select-dropdown__item').contains(runName).click();
+}
diff --git a/cypress/composables/workflow.ts b/cypress/composables/workflow.ts
index bab18587e02..251db6e75d4 100644
--- a/cypress/composables/workflow.ts
+++ b/cypress/composables/workflow.ts
@@ -76,6 +76,14 @@ export function getCanvasNodes() {
);
}
+export function getSaveButton() {
+ return cy.getByTestId('workflow-save-button');
+}
+
+export function getZoomToFitButton() {
+ return cy.getByTestId('zoom-to-fit');
+}
+
/**
* Actions
*/
@@ -170,3 +178,19 @@ export function clickManualChatButton() {
export function openNode(nodeName: string) {
getNodeByName(nodeName).dblclick();
}
+
+export function saveWorkflowOnButtonClick() {
+ cy.intercept('POST', '/rest/workflows').as('createWorkflow');
+ getSaveButton().should('contain', 'Save');
+ getSaveButton().click();
+ getSaveButton().should('contain', 'Saved');
+ cy.url().should('not.have.string', '/new');
+}
+
+export function pasteWorkflow(workflow: object) {
+ cy.get('body').paste(JSON.stringify(workflow));
+}
+
+export function clickZoomToFit() {
+ getZoomToFitButton().click();
+}
diff --git a/cypress/e2e/1-workflows.cy.ts b/cypress/e2e/1-workflows.cy.ts
index eb93c07ee6c..a6683bbee48 100644
--- a/cypress/e2e/1-workflows.cy.ts
+++ b/cypress/e2e/1-workflows.cy.ts
@@ -23,7 +23,6 @@ describe('Workflows', () => {
});
it('should create multiple new workflows using add workflow button', () => {
- cy.viewport(1920, 1080);
[...Array(multipleWorkflowsCount).keys()].forEach(() => {
cy.visit(WorkflowsPage.url);
WorkflowsPage.getters.createWorkflowButton().click();
@@ -36,7 +35,6 @@ describe('Workflows', () => {
});
it('should search for a workflow', () => {
- cy.viewport(1920, 1080);
// One Result
WorkflowsPage.getters.searchBar().type('Empty State Card Workflow');
WorkflowsPage.getters.workflowCards().should('have.length', 1);
@@ -62,7 +60,6 @@ describe('Workflows', () => {
});
it('should delete all the workflows', () => {
- cy.viewport(1920, 1080);
WorkflowsPage.getters.workflowCards().should('have.length', multipleWorkflowsCount + 1);
WorkflowsPage.getters.workflowCards().each(($el) => {
@@ -78,7 +75,6 @@ describe('Workflows', () => {
});
it('should respect tag querystring filter when listing workflows', () => {
- cy.viewport(1920, 1080);
WorkflowsPage.getters.newWorkflowButtonCard().click();
cy.createFixtureWorkflow('Test_workflow_2.json', getUniqueWorkflowName('My New Workflow'));
diff --git a/cypress/e2e/17-workflow-tags.cy.ts b/cypress/e2e/17-workflow-tags.cy.ts
index e8dacca2b84..26ea7cbe2ce 100644
--- a/cypress/e2e/17-workflow-tags.cy.ts
+++ b/cypress/e2e/17-workflow-tags.cy.ts
@@ -53,7 +53,6 @@ describe('Workflow tags', () => {
});
it('should detach a tag inline by clicking on X on tag pill', () => {
- cy.viewport(1920, 1080);
wf.getters.createTagButton().click();
wf.actions.addTags(TEST_TAGS);
wf.getters.nthTagPill(1).click();
@@ -74,7 +73,6 @@ describe('Workflow tags', () => {
});
it('should not show non existing tag as a selectable option', () => {
- cy.viewport(1920, 1080);
const NON_EXISTING_TAG = 'My Test Tag';
wf.getters.createTagButton().click();
diff --git a/cypress/e2e/19-execution.cy.ts b/cypress/e2e/19-execution.cy.ts
index b8b30dd7a9f..5be2399253f 100644
--- a/cypress/e2e/19-execution.cy.ts
+++ b/cypress/e2e/19-execution.cy.ts
@@ -514,7 +514,6 @@ describe('Execution', () => {
});
it('should send proper payload for node rerun', () => {
- cy.viewport(1920, 1080);
cy.createFixtureWorkflow('Multiple_trigger_node_rerun.json', 'Multiple trigger node rerun');
workflowPage.getters.zoomToFitButton().click();
diff --git a/cypress/e2e/20-workflow-executions.cy.ts b/cypress/e2e/20-workflow-executions.cy.ts
index bfa9bca1d9b..5788af171cd 100644
--- a/cypress/e2e/20-workflow-executions.cy.ts
+++ b/cypress/e2e/20-workflow-executions.cy.ts
@@ -101,7 +101,6 @@ describe('Workflow Executions', () => {
});
it('should show workflow data in executions tab after hard reload and modify name and tags', () => {
- cy.viewport(1920, 1080);
executionsTab.actions.switchToExecutionsTab();
checkMainHeaderELements();
workflowPage.getters.saveButton().find('button').should('not.exist');
diff --git a/cypress/e2e/24-ndv-paired-item.cy.ts b/cypress/e2e/24-ndv-paired-item.cy.ts
index 1261a0fcd1b..49257a8a12f 100644
--- a/cypress/e2e/24-ndv-paired-item.cy.ts
+++ b/cypress/e2e/24-ndv-paired-item.cy.ts
@@ -31,29 +31,31 @@ describe('NDV', () => {
ndv.getters.inputTableRow(1).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
- ndv.getters.inputTableRow(1).realHover();
+ ndv.actions.dragMainPanelToRight();
+ ndv.getters.inputTableRow(1).realMouseMove(10, 1);
ndv.getters.outputTableRow(4).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
- ndv.getters.inputTableRow(2).realHover();
+ ndv.getters.inputTableRow(2).realMouseMove(10, 1);
ndv.getters.outputTableRow(2).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
- ndv.getters.inputTableRow(3).realHover();
+ ndv.getters.inputTableRow(3).realMouseMove(10, 1);
ndv.getters.outputTableRow(6).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
// output to input
- ndv.getters.outputTableRow(1).realHover();
+ ndv.actions.dragMainPanelToLeft();
+ ndv.getters.outputTableRow(1).realMouseMove(10, 1);
ndv.getters.inputTableRow(4).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
- ndv.getters.outputTableRow(4).realHover();
+ ndv.getters.outputTableRow(4).realMouseMove(10, 1);
ndv.getters.inputTableRow(1).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
- ndv.getters.outputTableRow(2).realHover();
+ ndv.getters.outputTableRow(2).realMouseMove(10, 1);
ndv.getters.inputTableRow(2).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
- ndv.getters.outputTableRow(6).realHover();
+ ndv.getters.outputTableRow(6).realMouseMove(10, 1);
ndv.getters.inputTableRow(3).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
- ndv.getters.outputTableRow(1).realHover();
+ ndv.getters.outputTableRow(1).realMouseMove(10, 1);
ndv.getters.inputTableRow(4).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
});
@@ -75,31 +77,32 @@ describe('NDV', () => {
ndv.actions.switchInputMode('Table');
ndv.actions.switchOutputMode('Table');
- ndv.getters.backToCanvas().realHover(); // reset to default hover
+ ndv.getters.backToCanvas().realMouseMove(10, 1); // reset to default hover
ndv.getters.outputHoveringItem().should('not.exist');
ndv.getters.parameterExpressionPreview('value').should('include.text', '1111');
ndv.actions.selectInputNode('Set1');
- ndv.getters.backToCanvas().realHover(); // reset to default hover
+ ndv.getters.backToCanvas().realMouseMove(10, 1); // reset to default hover
ndv.getters.inputTableRow(1).should('have.text', '1000');
ndv.getters.inputTableRow(1).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
- ndv.getters.inputTableRow(1).realHover();
- cy.wait(50);
+ ndv.actions.dragMainPanelToRight();
+ ndv.getters.inputTbodyCell(1, 0).realMouseMove(10, 1);
ndv.getters.outputHoveringItem().should('have.text', '1000');
ndv.getters.parameterExpressionPreview('value').should('include.text', '1000');
ndv.actions.selectInputNode('Sort');
+ ndv.actions.dragMainPanelToLeft();
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
- ndv.getters.backToCanvas().realHover(); // reset to default hover
+ ndv.getters.backToCanvas().realMouseMove(10, 1); // reset to default hover
ndv.getters.inputTableRow(1).should('have.text', '1111');
ndv.getters.inputTableRow(1).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
- ndv.getters.inputTableRow(1).realHover();
- cy.wait(50);
+ ndv.actions.dragMainPanelToRight();
+ ndv.getters.inputTbodyCell(1, 0).realMouseMove(10, 1);
ndv.getters.outputHoveringItem().should('have.text', '1111');
ndv.getters.parameterExpressionPreview('value').should('include.text', '1111');
});
@@ -132,20 +135,22 @@ describe('NDV', () => {
ndv.getters.inputTableRow(1).should('have.text', '1111');
ndv.getters.inputTableRow(1).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
+
+ ndv.actions.dragMainPanelToLeft();
ndv.getters.outputTableRow(1).should('have.text', '1111');
- ndv.getters.outputTableRow(1).realHover();
+ ndv.getters.outputTableRow(1).realMouseMove(10, 1);
ndv.getters.outputTableRow(3).should('have.text', '4444');
- ndv.getters.outputTableRow(3).realHover();
+ ndv.getters.outputTableRow(3).realMouseMove(10, 1);
ndv.getters.inputTableRow(3).should('have.text', '4444');
ndv.getters.inputTableRow(3).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
ndv.actions.changeOutputRunSelector('2 of 2 (6 items)');
- cy.wait(50);
ndv.getters.inputTableRow(1).should('have.text', '1000');
- ndv.getters.inputTableRow(1).realHover();
+ ndv.actions.dragMainPanelToRight();
+ ndv.getters.inputTableRow(1).realMouseMove(10, 1);
ndv.getters.outputTableRow(1).should('have.text', '1000');
ndv.getters
@@ -155,7 +160,8 @@ describe('NDV', () => {
.should('equal', 'hovering-item');
ndv.getters.outputTableRow(3).should('have.text', '2000');
- ndv.getters.outputTableRow(3).realHover();
+ ndv.actions.dragMainPanelToLeft();
+ ndv.getters.outputTableRow(3).realMouseMove(10, 1);
ndv.getters.inputTableRow(3).should('have.text', '2000');
@@ -175,14 +181,15 @@ describe('NDV', () => {
ndv.actions.switchOutputBranch('False Branch (2 items)');
ndv.getters.outputTableRow(1).should('have.text', '8888');
- ndv.getters.outputTableRow(1).realHover();
+ ndv.actions.dragMainPanelToLeft();
+ ndv.getters.outputTableRow(1).realMouseMove(10, 1);
ndv.getters.inputTableRow(5).should('have.text', '8888');
ndv.getters.inputTableRow(5).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
ndv.getters.outputTableRow(2).should('have.text', '9999');
- ndv.getters.outputTableRow(2).realHover();
+ ndv.getters.outputTableRow(2).realMouseMove(10, 1);
ndv.getters.inputTableRow(6).should('have.text', '9999');
@@ -192,29 +199,35 @@ describe('NDV', () => {
workflowPage.actions.openNode('Set5');
+ ndv.actions.dragMainPanelToRight();
ndv.actions.switchInputBranch('True Branch');
+
+ ndv.actions.dragMainPanelToLeft();
ndv.actions.changeOutputRunSelector('1 of 2 (2 items)');
ndv.getters.outputTableRow(1).should('have.text', '8888');
- ndv.getters.outputTableRow(1).realHover();
- cy.wait(100);
+ ndv.getters.outputTableRow(1).realMouseMove(10, 1);
ndv.getters.inputHoveringItem().should('not.exist');
ndv.getters.inputTableRow(1).should('have.text', '1111');
- ndv.getters.inputTableRow(1).realHover();
- cy.wait(100);
+
+ ndv.actions.dragMainPanelToRight();
+ ndv.getters.inputTableRow(1).realMouseMove(10, 1);
ndv.getters.outputHoveringItem().should('not.exist');
ndv.actions.switchInputBranch('False Branch');
ndv.getters.inputTableRow(1).should('have.text', '8888');
- ndv.getters.inputTableRow(1).realHover();
+ ndv.actions.dragMainPanelToRight();
+ ndv.getters.inputTableRow(1).realMouseMove(10, 1);
+ ndv.actions.dragMainPanelToLeft();
ndv.actions.changeOutputRunSelector('2 of 2 (4 items)');
ndv.getters.outputTableRow(1).should('have.text', '1111');
- ndv.getters.outputTableRow(1).realHover();
+ ndv.getters.outputTableRow(1).realMouseMove(10, 1);
ndv.actions.changeOutputRunSelector('1 of 2 (2 items)');
ndv.getters.inputTableRow(1).should('have.text', '8888');
- ndv.getters.inputTableRow(1).realHover();
+ ndv.actions.dragMainPanelToRight();
+ ndv.getters.inputTableRow(1).realMouseMove(10, 1);
ndv.getters.outputHoveringItem().should('have.text', '8888');
// todo there's a bug here need to fix ADO-534
// ndv.getters.outputHoveringItem().should('not.exist');
diff --git a/cypress/e2e/34-template-credentials-setup.cy.ts b/cypress/e2e/34-template-credentials-setup.cy.ts
index a2f9a019f79..386c83eb0ad 100644
--- a/cypress/e2e/34-template-credentials-setup.cy.ts
+++ b/cypress/e2e/34-template-credentials-setup.cy.ts
@@ -56,10 +56,10 @@ describe('Template credentials setup', () => {
it('can be opened from template collection page', () => {
visitTemplateCollectionPage(testData.ecommerceStarterPack);
templateCredentialsSetupPage.enableTemplateCredentialSetupFeatureFlag();
- clickUseWorkflowButtonByTitle('Promote new Shopify products on Twitter and Telegram');
+ clickUseWorkflowButtonByTitle('Promote new Shopify products');
templateCredentialsSetupPage.getters
- .title("Set up 'Promote new Shopify products on Twitter and Telegram' template")
+ .title("Set up 'Promote new Shopify products' template")
.should('be.visible');
});
@@ -67,7 +67,7 @@ describe('Template credentials setup', () => {
templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id);
templateCredentialsSetupPage.getters
- .title("Set up 'Promote new Shopify products on Twitter and Telegram' template")
+ .title("Set up 'Promote new Shopify products' template")
.should('be.visible');
templateCredentialsSetupPage.getters
@@ -182,7 +182,6 @@ describe('Template credentials setup', () => {
});
it('should fill credentials from workflow editor', () => {
- cy.viewport(1920, 1080);
templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id);
templateCredentialsSetupPage.getters.skipLink().click();
diff --git a/cypress/e2e/45-ai-assistant.cy.ts b/cypress/e2e/45-ai-assistant.cy.ts
index 7f9c79e0e65..9b50fef44a0 100644
--- a/cypress/e2e/45-ai-assistant.cy.ts
+++ b/cypress/e2e/45-ai-assistant.cy.ts
@@ -36,7 +36,6 @@ describe('AI Assistant::enabled', () => {
});
it('renders placeholder UI', () => {
- cy.viewport(1920, 1080);
aiAssistant.getters.askAssistantFloatingButton().should('be.visible');
aiAssistant.getters.askAssistantFloatingButton().click();
aiAssistant.getters.askAssistantChat().should('be.visible');
diff --git a/cypress/e2e/47-subworkflow-debugging.cy.ts b/cypress/e2e/47-subworkflow-debugging.cy.ts
new file mode 100644
index 00000000000..f808bdd044e
--- /dev/null
+++ b/cypress/e2e/47-subworkflow-debugging.cy.ts
@@ -0,0 +1,140 @@
+import {
+ getExecutionPreviewOutputPanelRelatedExecutionLink,
+ getExecutionsSidebar,
+ getWorkflowExecutionPreviewIframe,
+ openExecutionPreviewNode,
+} from '../composables/executions';
+import {
+ changeOutputRunSelector,
+ getOutputPanelItemsCount,
+ getOutputPanelRelatedExecutionLink,
+ getOutputRunSelectorInput,
+ getOutputTableHeaders,
+ getOutputTableRows,
+ getOutputTbodyCell,
+} from '../composables/ndv';
+import {
+ clickExecuteWorkflowButton,
+ clickZoomToFit,
+ getCanvasNodes,
+ navigateToNewWorkflowPage,
+ openNode,
+ pasteWorkflow,
+ saveWorkflowOnButtonClick,
+} from '../composables/workflow';
+import SUBWORKFLOW_DEBUGGING_EXAMPLE from '../fixtures/Subworkflow-debugging-execute-workflow.json';
+
+describe('Subworkflow debugging', () => {
+ beforeEach(() => {
+ navigateToNewWorkflowPage();
+ pasteWorkflow(SUBWORKFLOW_DEBUGGING_EXAMPLE);
+ saveWorkflowOnButtonClick();
+ getCanvasNodes().should('have.length', 11);
+ clickZoomToFit();
+
+ clickExecuteWorkflowButton();
+ });
+
+ describe('can inspect sub executed workflow', () => {
+ it('(Run once with all items/ Wait for Sub-workflow completion) (default behavior)', () => {
+ openNode('Execute Workflow with param');
+
+ getOutputPanelItemsCount().should('contain.text', '2 items, 1 sub-execution');
+ getOutputPanelRelatedExecutionLink().should('contain.text', 'Inspect Sub-Execution');
+ getOutputPanelRelatedExecutionLink().should('have.attr', 'href');
+
+ // ensure workflow executed and waited on output
+ getOutputTableHeaders().should('have.length', 2);
+ getOutputTbodyCell(1, 0).should('have.text', 'world Natalie Moore');
+ });
+
+ it('(Run once for each item/ Wait for Sub-workflow completion)', () => {
+ openNode('Execute Workflow with param1');
+
+ getOutputPanelItemsCount().should('contain.text', '2 items, 2 sub-execution');
+ getOutputPanelRelatedExecutionLink().should('not.exist');
+
+ // ensure workflow executed and waited on output
+ getOutputTableHeaders().should('have.length', 3);
+ getOutputTbodyCell(1, 0).find('a').should('have.attr', 'href');
+ getOutputTbodyCell(1, 1).should('have.text', 'world Natalie Moore');
+ });
+
+ it('(Run once with all items/ Wait for Sub-workflow completion)', () => {
+ openNode('Execute Workflow with param2');
+
+ getOutputPanelItemsCount().should('not.exist');
+ getOutputPanelRelatedExecutionLink().should('contain.text', 'Inspect Sub-Execution');
+ getOutputPanelRelatedExecutionLink().should('have.attr', 'href');
+
+ // ensure workflow executed but returned same data as input
+ getOutputRunSelectorInput().should('have.value', '2 of 2 (3 items, 1 sub-execution)');
+ getOutputTableHeaders().should('have.length', 6);
+ getOutputTableHeaders().eq(0).should('have.text', 'uid');
+ getOutputTableRows().should('have.length', 4);
+ getOutputTbodyCell(1, 1).should('include.text', 'Jon_Ebert@yahoo.com');
+
+ changeOutputRunSelector('1 of 2 (2 items, 1 sub-execution)');
+ getOutputRunSelectorInput().should('have.value', '1 of 2 (2 items, 1 sub-execution)');
+ getOutputTableHeaders().should('have.length', 6);
+ getOutputTableHeaders().eq(0).should('have.text', 'uid');
+ getOutputTableRows().should('have.length', 3);
+ getOutputTbodyCell(1, 1).should('include.text', 'Terry.Dach@hotmail.com');
+ });
+
+ it('(Run once for each item/ Wait for Sub-workflow completion)', () => {
+ openNode('Execute Workflow with param3');
+
+ // ensure workflow executed but returned same data as input
+ getOutputRunSelectorInput().should('have.value', '2 of 2 (3 items, 3 sub-executions)');
+ getOutputTableHeaders().should('have.length', 7);
+ getOutputTableHeaders().eq(1).should('have.text', 'uid');
+ getOutputTableRows().should('have.length', 4);
+ getOutputTbodyCell(1, 0).find('a').should('have.attr', 'href');
+ getOutputTbodyCell(1, 2).should('include.text', 'Jon_Ebert@yahoo.com');
+
+ changeOutputRunSelector('1 of 2 (2 items, 2 sub-executions)');
+ getOutputRunSelectorInput().should('have.value', '1 of 2 (2 items, 2 sub-executions)');
+ getOutputTableHeaders().should('have.length', 7);
+ getOutputTableHeaders().eq(1).should('have.text', 'uid');
+ getOutputTableRows().should('have.length', 3);
+
+ getOutputTbodyCell(1, 0).find('a').should('have.attr', 'href');
+ getOutputTbodyCell(1, 2).should('include.text', 'Terry.Dach@hotmail.com');
+ });
+ });
+
+ it('can inspect parent executions', () => {
+ cy.url().then((workflowUrl) => {
+ openNode('Execute Workflow with param');
+
+ getOutputPanelItemsCount().should('contain.text', '2 items, 1 sub-execution');
+ getOutputPanelRelatedExecutionLink().should('contain.text', 'Inspect Sub-Execution');
+ getOutputPanelRelatedExecutionLink().should('have.attr', 'href');
+
+ // ensure workflow executed and waited on output
+ getOutputTableHeaders().should('have.length', 2);
+ getOutputTbodyCell(1, 0).should('have.text', 'world Natalie Moore');
+
+ // cypress cannot handle new tabs so removing it
+ getOutputPanelRelatedExecutionLink().invoke('removeAttr', 'target').click();
+
+ getExecutionsSidebar().should('be.visible');
+ getWorkflowExecutionPreviewIframe().should('be.visible');
+ openExecutionPreviewNode('Execute Workflow Trigger');
+
+ getExecutionPreviewOutputPanelRelatedExecutionLink().should(
+ 'include.text',
+ 'Inspect Parent Execution',
+ );
+
+ getExecutionPreviewOutputPanelRelatedExecutionLink()
+ .invoke('removeAttr', 'target')
+ .click({ force: true });
+
+ cy.url().then((currentUrl) => {
+ expect(currentUrl === workflowUrl);
+ });
+ });
+ });
+});
diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts
index 9ad1fceae1b..96b917d4c3d 100644
--- a/cypress/e2e/5-ndv.cy.ts
+++ b/cypress/e2e/5-ndv.cy.ts
@@ -66,7 +66,6 @@ describe('NDV', () => {
});
it('should disconect Switch outputs if rules order was changed', () => {
- cy.viewport(1920, 1080);
cy.createFixtureWorkflow('NDV-test-switch_reorder.json', 'NDV test switch reorder');
workflowPage.actions.zoomToFit();
@@ -233,7 +232,6 @@ describe('NDV', () => {
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
});
it('should display large schema', () => {
- cy.viewport(1920, 1080);
cy.createFixtureWorkflow(
'Test_workflow_schema_test_pinned_data.json',
'NDV test schema view 2',
@@ -720,7 +718,6 @@ describe('NDV', () => {
});
it('Should open appropriate node creator after clicking on connection hint link', () => {
- cy.viewport(1920, 1080);
const nodeCreator = new NodeCreator();
const hintMapper = {
Memory: 'AI Nodes',
diff --git a/cypress/fixtures/Ecommerce_starter_pack_template_collection.json b/cypress/fixtures/Ecommerce_starter_pack_template_collection.json
index 1f908c587ba..d6235b358c9 100644
--- a/cypress/fixtures/Ecommerce_starter_pack_template_collection.json
+++ b/cypress/fixtures/Ecommerce_starter_pack_template_collection.json
@@ -1 +1,1555 @@
-{"collection":{"id":1,"name":"eCommerce Starter Pack","description":"eCommerce operations are complex — but there are many things that you can automate to make your life easier. This collection provides a few ideas to get started.\n\nReduce manual work and the risk of human error by automating processes such as social media promotion of products, updating customer databases, and get notifications for important events.","totalViews":0,"createdAt":"2022-02-17T12:40:50.498Z","nodes":[{"id":20,"name":"n8n-nodes-base.if","defaults":{"name":"IF","color":"#408000"},"displayName":"IF","icon":"fa:map-signs","iconData":{"icon":"map-signs","type":"icon"},"typeVersion":1,"categories":[{"id":9,"name":"Core Nodes"}]},{"id":49,"name":"n8n-nodes-base.telegram","defaults":{"name":"Telegram"},"displayName":"Telegram","icon":"file:telegram.svg","iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNjYgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiIGZpbGw9IiMzN2FlZTIiLz48cGF0aCBkPSJNMjEuNjYxIDM0LjMzOGwzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0yNi42OTUgMzcuMDM0bC0uNzI5IDcuNzQ2cy0uMzA1IDIuMzczIDIuMDY4IDBsNC42NDQtNC4yMDMiIGZpbGw9IiNhOWM2ZDgiLz48cGF0aCBkPSJNMjEuNzMgMzQuNzEybC03LjgwOS0yLjU0NXMtLjkzMi0uMzc4LS42MzMtMS4yMzdjLjA2Mi0uMTc3LjE4Ni0uMzI4LjU1OS0uNTg4IDEuNzMxLTEuMjA2IDMyLjAyOC0xMi4wOTYgMzIuMDI4LTEyLjA5NnMuODU2LS4yODggMS4zNjEtLjA5N2MuMjMxLjA4OC4zNzguMTg3LjUwMy41NDguMDQ1LjEzMi4wNzEuNDExLjA2OC42ODktLjAwMy4yMDEtLjAyNy4zODYtLjA0NS42NzgtLjE4NCAyLjk3OC01LjcwNiAyNS4xOTgtNS43MDYgMjUuMTk4cy0uMzMgMS4zLTEuNTE0IDEuMzQ1Yy0uNDMyLjAxNi0uOTU2LS4wNzEtMS41ODItLjYxLTIuMzIzLTEuOTk4LTEwLjM1Mi03LjM5NC0xMi4xMjYtOC41OGEuMzQuMzQgMCAwMS0uMTQ2LS4yMzljLS4wMjUtLjEyNS4xMDgtLjI4LjEwOC0uMjhzMTMuOTgtMTIuNDI3IDE0LjM1Mi0xMy43MzFjLjAyOS0uMTAxLS4wNzktLjE1MS0uMjI2LS4xMDctLjkyOS4zNDItMTcuMDI1IDEwLjUwNi0xOC44MDEgMTEuNjI5LS4xMDQuMDY2LS4zOTUuMDIzLS4zOTUuMDIzIi8+PC9nPjwvc3ltYm9sPjwvc3ZnPg=="},"typeVersion":1,"categories":[{"id":6,"name":"Communication"}]},{"id":107,"name":"n8n-nodes-base.shopifyTrigger","defaults":{"name":"Shopify Trigger"},"displayName":"Shopify Trigger","icon":"file:shopify.svg","iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTggNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTQ5LjI1NSAxMi40ODRhLjYzMy42MzMgMCAwMC0uNTY0LS41MjdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdjLS4zNC0uMzc2LTEuMDkyLS4yNjYtMS4zNzYtLjE4OC0uMDM3IDAtLjc1Mi4yMjUtMS45MjIuNjA1LTEuMTM3LTMuMy0zLjE1LTYuMzA2LTYuNjk2LTYuMzA2aC0uMzAzQzI4LjQzOC42MDUgMjcuMTk0IDAgMjYuMTQ0IDBjLTguMjU2LjAzNy0xMi4yIDEwLjMzMy0xMy40MzQgMTUuNTk0bC01Ljc3IDEuNzdjLTEuNzcuNTY0LTEuODM1LjYwNS0yLjA3MyAyLjI5M0wwIDU3LjE3NSAzNi40NjggNjRsMTkuNzYzLTQuMjZjMC0uMDM3LTYuOTQtNDYuODk3LTYuOTc2LTQ3LjI1NXpNMzQuNDMxIDguODZjLS45MTcuMzAzLTEuOTYzLjYwNS0zLjEuOTQ1di0uNjhhMTUuMDMgMTUuMDMgMCAwMC0uNzUyLTQuOTk5YzEuODQ4LjI4NCAzLjEgMi4zNTcgMy44NDMgNC43MzN6bS02LjA2OC00LjI5OGMuNjAzIDEuNzc4Ljg4MyAzLjY1LjgyNiA1LjUyN3YuMzRsLTYuMzc1IDEuOTYzYzEuMjQ4LTQuNjYgMy41NS02Ljk2MiA1LjU1LTcuODN6bS0yLjQ1LTIuMjkzYTEuOTQgMS45NCAwIDAxMS4wNTUuMzM5Yy0yLjY2IDEuMjM4LTUuNDcyIDQuMzY2LTYuNjc4IDEwLjYyN2wtNS4wNDUgMS41NDZDMTYuNjY4IDEwLjAzIDE5Ljk4OCAyLjI2IDI1LjkxIDIuMjZ6IiBmaWxsPSIjOTViZjQ3Ii8+PHBhdGggZD0iTTQ4LjY5MSAxMS45NTdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdhLjc1My43NTMgMCAwMC0uNTI3LS4yMjVMMzYuNDcyIDY0bDE5Ljc2My00LjI2LTYuOTgtNDcuMjE4YS42OC42OCAwIDAwLS41NjQtLjU2NHoiIGZpbGw9IiM1ZThlM2UiLz48cGF0aCBkPSJNMjkuNzU4IDIyLjlsLTIuNDU0IDcuMjQyYTExLjM2IDExLjM2IDAgMDAtNC43NTItMS4xMzNjLTMuODQ4IDAtNC4wMzYgMi40MTItNC4wMzYgMy4wMTggMCAzLjI5OCA4LjYzNiA0LjU2NCA4LjYzNiAxMi4zMzMgMCA2LjEtMy44ODUgMTAuMDMtOS4xIDEwLjAzLTYuMjYgMC05LjQ2Ny0zLjg4NS05LjQ2Ny0zLjg4NWwxLjY2NS01LjU1czMuMjggMi44MyA2LjA3MyAyLjgzYTIuNDcgMi40NyAwIDAwMi41NjQtMi40OWMwLTQuMzQtNy4xLTQuNTI3LTcuMS0xMS42MTggMC01Ljk2MiA0LjI5OC0xMS43NyAxMi45MzQtMTEuNzcgMy4zOTQuMDUgNS4wMTggMSA1LjAxOCAxeiIvPjwvZz48L3N5bWJvbD48L3N2Zz4="},"typeVersion":1,"categories":[{"id":2,"name":"Sales"}]},{"id":126,"name":"n8n-nodes-base.mautic","defaults":{"name":"Mautic"},"displayName":"Mautic","icon":"file:mautic.svg","iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMzQ5Ljc3OXB4IiBoZWlnaHQ9IjM0OS43NzlweCIgdmlld0JveD0iMCAwIDM0OS43NzkgMzQ5Ljc3OSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMzQ5Ljc3OSAzNDkuNzc5Ig0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xNzQuODksMzQ5Ljc3OUM3OC42MTIsMzQ5Ljc3OSwwLDI3MS40NjIsMCwxNzQuODlTNzguNjEyLDAsMTc0Ljg5LDBjMjMuMjYsMCw0NS45MzEsNC40MTcsNjcuMTI5LDEzLjU0Mw0KCWM1Ljg4OSwyLjY1LDguODMzLDkuNDIyLDYuNDc4LDE1LjYwNWMtMi42NDksNS44ODgtOS40MjEsOC44MzMtMTUuNjA0LDYuNDc3Yy0xOC41NDktNy42NTUtMzcuOTgtMTEuNDgyLTU4LjAwMi0xMS40ODINCgljLTgzLjMyMywwLTE1MS4wNDEsNjcuNzE4LTE1MS4wNDEsMTUxLjA0MVM5MS41NjcsMzI2LjIyNSwxNzQuODksMzI2LjIyNWM4My4zMjMsMCwxNTEuMDQxLTY3LjcxOCwxNTEuMDQxLTE1MS4wNDENCgljMC0xNy45Ni0yLjk0NC0zNS4zMzItOS4xMjctNTEuODE5Yy0yLjM1NS02LjE4MywwLjg4My0xMi45NTUsNy4wNjYtMTUuMzFjNi4xODMtMi4zNTUsMTIuOTU0LDAuODgzLDE1LjMxLDcuMDY2DQoJYzcuMDY2LDE5LjEzOCwxMC42LDM5LjQ1MywxMC42LDYwLjA2M0MzNDkuNzc5LDI3MS4xNjcsMjcxLjQ2MiwzNDkuNzc5LDE3NC44OSwzNDkuNzc5Ii8+DQo8Zz4NCgk8cG9seWdvbiBmaWxsPSIjRkRCOTMzIiBwb2ludHM9IjI1MS40NCwxNTYuOTMgMjI0LjM1NCwxODUuMTk0IDIzOS4zNjksMjQ4LjQ5NiAyNzMuNTIyLDI0OC40OTYgCSIvPg0KPC9nPg0KPHBvbHlnb24gZmlsbD0iI0ZEQjkzMyIgcG9pbnRzPSIyNDAuMjUzLDczLjMxMiAyNDkuNjc0LDgyLjczNCAxNzQuODksMTYxLjkzNSAxMTAuOTk5LDk2LjI3NyA3NC4xOTYsMjQ4LjQ5NiAxMDguMzUsMjQ4LjQ5NiANCgkxMjguNjY1LDE2My45OTYgMTc0Ljg5LDIxNC4zNDMgMjczLjgxNywxMDYuNTgzIDI4My4yMzksMTE2LjI5OSAyOTIuNjYsNjMuMDA3ICIvPg0KPC9zdmc+DQo="},"typeVersion":1,"categories":[{"id":1,"name":"Marketing & Content"},{"id":6,"name":"Communication"}]},{"id":235,"name":"n8n-nodes-base.wooCommerceTrigger","defaults":{"name":"WooCommerce Trigger"},"displayName":"WooCommerce Trigger","icon":"file:wooCommerce.svg","iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="},"typeVersion":1,"categories":[{"id":2,"name":"Sales"}]},{"id":325,"name":"n8n-nodes-base.twitter","defaults":{"name":"X"},"displayName":"X (Formerly Twitter)","icon":"file:x.svg","iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE4LjI0NCAyLjI1aDMuMzA4bC03LjIyNyA4LjI2IDguNTAyIDExLjI0SDE2LjE3bC01LjIxNC02LjgxN0w0Ljk5IDIxLjc1SDEuNjhsNy43My04LjgzNUwxLjI1NCAyLjI1SDguMDhsNC43MTMgNi4yMzF6bS0xLjE2MSAxNy41MmgxLjgzM0w3LjA4NCA0LjEyNkg1LjExN3oiPjwvcGF0aD48L3N2Zz4K"},"typeVersion":2,"categories":[{"id":1,"name":"Marketing & Content"}]}],"categories":[{"id":2,"name":"Sales"}],"workflows":[{"id":1205,"name":"Promote new Shopify products on Twitter and Telegram","views":485,"recentViews":9850,"totalViews":485,"createdAt":"2021-08-24T10:40:50.007Z","description":"This workflow automatically promotes your new Shopify products on Twitter and Telegram. This workflow is also featured in the blog post [*6 e-commerce workflows to power up your Shopify store*](https://n8n.io/blog/no-code-ecommerce-workflow-automations/#promote-your-new-products-on-social-media).\n\n## Prerequisites\n\n- A Shopify account and [credentials](https://docs.n8n.io/integrations/credentials/shopify/)\n- A Twitter account and [credentials](https://docs.n8n.io/integrations/credentials/twitter/)\n- A Telegram account and [credentials](https://docs.n8n.io/integrations/credentials/telegram/) for the channel you want to send messages to.\n\n## Nodes\n\n- [Shopify Trigger node](https://docs.n8n.io/integrations/trigger-nodes/n8n-nodes-base.shopifytrigger/) triggers the workflow when you create a new product in Shopify.\n- [Twitter node](https://docs.n8n.io/integrations/nodes/n8n-nodes-base.twitter/) posts a tweet with the text \"Hey there, my design is now on a new product! Visit my {shop name} to get this cool {product title} (and check out more {product type})\".\n- [Telegram node](https://docs.n8n.io/integrations/nodes/n8n-nodes-base.telegram/) posts a message with the same text as above in a Telegram channel.","workflow":{"nodes":[{"name":"Twitter","type":"n8n-nodes-base.twitter","position":[720,-220],"parameters":{"text":"=Hey there, my design is now on a new product ✨\nVisit my {{$json[\"vendor\"]}} shop to get this cool{{$json[\"title\"]}} (and check out more {{$json[\"product_type\"]}}) 🛍️","additionalFields":{}},"credentials":{"twitterOAuth1Api":"twitter"},"typeVersion":1},{"name":"Telegram","type":"n8n-nodes-base.telegram","position":[720,-20],"parameters":{"text":"=Hey there, my design is now on a new product!\nVisit my {{$json[\"vendor\"]}} shop to get this cool{{$json[\"title\"]}} (and check out more {{$json[\"product_type\"]}})","chatId":"123456","additionalFields":{}},"credentials":{"telegramApi":"telegram_habot"},"typeVersion":1},{"name":"product created","type":"n8n-nodes-base.shopifyTrigger","position":[540,-110],"webhookId":"2a7e0e50-8f09-4a2b-bf54-a849a6ac4fe0","parameters":{"topic":"products/create"},"credentials":{"shopifyApi":"shopify_nodeqa"},"typeVersion":1}],"connections":{"product created":{"main":[[{"node":"Twitter","type":"main","index":0},{"node":"Telegram","type":"main","index":0}]]}}},"workflowInfo":{"nodeCount":3,"nodeTypes":{"n8n-nodes-base.twitter":{"count":1},"n8n-nodes-base.telegram":{"count":1},"n8n-nodes-base.shopifyTrigger":{"count":1}}},"user":{"username":"lorenanda"},"nodes":[{"id":49,"icon":"file:telegram.svg","name":"n8n-nodes-base.telegram","defaults":{"name":"Telegram"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNjYgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiIGZpbGw9IiMzN2FlZTIiLz48cGF0aCBkPSJNMjEuNjYxIDM0LjMzOGwzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0yNi42OTUgMzcuMDM0bC0uNzI5IDcuNzQ2cy0uMzA1IDIuMzczIDIuMDY4IDBsNC42NDQtNC4yMDMiIGZpbGw9IiNhOWM2ZDgiLz48cGF0aCBkPSJNMjEuNzMgMzQuNzEybC03LjgwOS0yLjU0NXMtLjkzMi0uMzc4LS42MzMtMS4yMzdjLjA2Mi0uMTc3LjE4Ni0uMzI4LjU1OS0uNTg4IDEuNzMxLTEuMjA2IDMyLjAyOC0xMi4wOTYgMzIuMDI4LTEyLjA5NnMuODU2LS4yODggMS4zNjEtLjA5N2MuMjMxLjA4OC4zNzguMTg3LjUwMy41NDguMDQ1LjEzMi4wNzEuNDExLjA2OC42ODktLjAwMy4yMDEtLjAyNy4zODYtLjA0NS42NzgtLjE4NCAyLjk3OC01LjcwNiAyNS4xOTgtNS43MDYgMjUuMTk4cy0uMzMgMS4zLTEuNTE0IDEuMzQ1Yy0uNDMyLjAxNi0uOTU2LS4wNzEtMS41ODItLjYxLTIuMzIzLTEuOTk4LTEwLjM1Mi03LjM5NC0xMi4xMjYtOC41OGEuMzQuMzQgMCAwMS0uMTQ2LS4yMzljLS4wMjUtLjEyNS4xMDgtLjI4LjEwOC0uMjhzMTMuOTgtMTIuNDI3IDE0LjM1Mi0xMy43MzFjLjAyOS0uMTAxLS4wNzktLjE1MS0uMjI2LS4xMDctLjkyOS4zNDItMTcuMDI1IDEwLjUwNi0xOC44MDEgMTEuNjI5LS4xMDQuMDY2LS4zOTUuMDIzLS4zOTUuMDIzIi8+PC9nPjwvc3ltYm9sPjwvc3ZnPg=="},"categories":[{"id":6,"name":"Communication"}],"displayName":"Telegram","typeVersion":1},{"id":107,"icon":"file:shopify.svg","name":"n8n-nodes-base.shopifyTrigger","defaults":{"name":"Shopify Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTggNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTQ5LjI1NSAxMi40ODRhLjYzMy42MzMgMCAwMC0uNTY0LS41MjdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdjLS4zNC0uMzc2LTEuMDkyLS4yNjYtMS4zNzYtLjE4OC0uMDM3IDAtLjc1Mi4yMjUtMS45MjIuNjA1LTEuMTM3LTMuMy0zLjE1LTYuMzA2LTYuNjk2LTYuMzA2aC0uMzAzQzI4LjQzOC42MDUgMjcuMTk0IDAgMjYuMTQ0IDBjLTguMjU2LjAzNy0xMi4yIDEwLjMzMy0xMy40MzQgMTUuNTk0bC01Ljc3IDEuNzdjLTEuNzcuNTY0LTEuODM1LjYwNS0yLjA3MyAyLjI5M0wwIDU3LjE3NSAzNi40NjggNjRsMTkuNzYzLTQuMjZjMC0uMDM3LTYuOTQtNDYuODk3LTYuOTc2LTQ3LjI1NXpNMzQuNDMxIDguODZjLS45MTcuMzAzLTEuOTYzLjYwNS0zLjEuOTQ1di0uNjhhMTUuMDMgMTUuMDMgMCAwMC0uNzUyLTQuOTk5YzEuODQ4LjI4NCAzLjEgMi4zNTcgMy44NDMgNC43MzN6bS02LjA2OC00LjI5OGMuNjAzIDEuNzc4Ljg4MyAzLjY1LjgyNiA1LjUyN3YuMzRsLTYuMzc1IDEuOTYzYzEuMjQ4LTQuNjYgMy41NS02Ljk2MiA1LjU1LTcuODN6bS0yLjQ1LTIuMjkzYTEuOTQgMS45NCAwIDAxMS4wNTUuMzM5Yy0yLjY2IDEuMjM4LTUuNDcyIDQuMzY2LTYuNjc4IDEwLjYyN2wtNS4wNDUgMS41NDZDMTYuNjY4IDEwLjAzIDE5Ljk4OCAyLjI2IDI1LjkxIDIuMjZ6IiBmaWxsPSIjOTViZjQ3Ii8+PHBhdGggZD0iTTQ4LjY5MSAxMS45NTdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdhLjc1My43NTMgMCAwMC0uNTI3LS4yMjVMMzYuNDcyIDY0bDE5Ljc2My00LjI2LTYuOTgtNDcuMjE4YS42OC42OCAwIDAwLS41NjQtLjU2NHoiIGZpbGw9IiM1ZThlM2UiLz48cGF0aCBkPSJNMjkuNzU4IDIyLjlsLTIuNDU0IDcuMjQyYTExLjM2IDExLjM2IDAgMDAtNC43NTItMS4xMzNjLTMuODQ4IDAtNC4wMzYgMi40MTItNC4wMzYgMy4wMTggMCAzLjI5OCA4LjYzNiA0LjU2NCA4LjYzNiAxMi4zMzMgMCA2LjEtMy44ODUgMTAuMDMtOS4xIDEwLjAzLTYuMjYgMC05LjQ2Ny0zLjg4NS05LjQ2Ny0zLjg4NWwxLjY2NS01LjU1czMuMjggMi44MyA2LjA3MyAyLjgzYTIuNDcgMi40NyAwIDAwMi41NjQtMi40OWMwLTQuMzQtNy4xLTQuNTI3LTcuMS0xMS42MTggMC01Ljk2MiA0LjI5OC0xMS43NyAxMi45MzQtMTEuNzcgMy4zOTQuMDUgNS4wMTggMSA1LjAxOCAxeiIvPjwvZz48L3N5bWJvbD48L3N2Zz4="},"categories":[{"id":2,"name":"Sales"}],"displayName":"Shopify Trigger","typeVersion":1},{"id":325,"icon":"file:x.svg","name":"n8n-nodes-base.twitter","defaults":{"name":"X"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE4LjI0NCAyLjI1aDMuMzA4bC03LjIyNyA4LjI2IDguNTAyIDExLjI0SDE2LjE3bC01LjIxNC02LjgxN0w0Ljk5IDIxLjc1SDEuNjhsNy43My04LjgzNUwxLjI1NCAyLjI1SDguMDhsNC43MTMgNi4yMzF6bS0xLjE2MSAxNy41MmgxLjgzM0w3LjA4NCA0LjEyNkg1LjExN3oiPjwvcGF0aD48L3N2Zz4K"},"categories":[{"id":1,"name":"Marketing & Content"}],"displayName":"X (Formerly Twitter)","typeVersion":2}],"categories":[{"id":2,"name":"Sales"},{"id":19,"name":"Marketing & Growth"}],"image":[{"id":527,"url":"https://n8niostorageaccount.blob.core.windows.net/n8nio-strapi-blobs-prod/assets/89a078b208fe4c6181902608b1cd1332.png"}]},{"id":1456,"name":"Add new customers from WooCommerce to Mautic","views":333,"recentViews":9833,"totalViews":333,"createdAt":"2022-02-17T15:00:40.748Z","description":"This workflow uses a WooCommerce trigger that will run when a new customer has been added, It will then add the customer to Mautic.\n\nTo use this workflow you will need to set the credentials to use for the WooCommerce and Mautic nodes.","workflow":{"id":83,"name":"New WooCommerce Customer to Mautic","nodes":[{"name":"Check for Existing","type":"n8n-nodes-base.mautic","position":[280,480],"parameters":{"options":{"search":"={{$json[\"email\"]}}"},"operation":"getAll","authentication":"oAuth2"},"credentials":{"mauticOAuth2Api":{"id":"54","name":"Mautic account"}},"typeVersion":1,"alwaysOutputData":true},{"name":"If New","type":"n8n-nodes-base.if","position":[460,480],"parameters":{"conditions":{"string":[{"value1":"={{$json[\"id\"]}}","operation":"isEmpty"}]}},"typeVersion":1},{"name":"Create Contact","type":"n8n-nodes-base.mautic","position":[680,320],"parameters":{"email":"={{$node[\"Customer Created\"].json[\"email\"]}}","company":"={{$node[\"Customer Created\"].json[\"billing\"][\"company\"]}}","options":{},"lastName":"={{$node[\"Customer Created\"].json[\"last_name\"]}}","firstName":"={{$node[\"Customer Created\"].json[\"first_name\"]}}","authentication":"oAuth2","additionalFields":{}},"credentials":{"mauticOAuth2Api":{"id":"54","name":"Mautic account"}},"typeVersion":1},{"name":"Update Contact","type":"n8n-nodes-base.mautic","position":[680,580],"parameters":{"options":{},"contactId":"={{$json[\"id\"]}}","operation":"update","updateFields":{"lastName":"={{$node[\"Customer Created or Updated\"].json[\"last_name\"]}}","firstName":"={{$node[\"Customer Created or Updated\"].json[\"first_name\"]}}"},"authentication":"oAuth2"},"credentials":{"mauticOAuth2Api":{"id":"54","name":"Mautic account"}},"typeVersion":1},{"name":"Customer Created or Updated","type":"n8n-nodes-base.wooCommerceTrigger","position":[100,480],"webhookId":"5d89e322-a5e0-4cce-9eab-185e8375175b","parameters":{"event":"customer.updated"},"credentials":{"wooCommerceApi":{"id":"48","name":"WooCommerce account"}},"typeVersion":1}],"active":false,"settings":{"saveManualExecutions":true,"saveExecutionProgress":true,"saveDataSuccessExecution":"all"},"connections":{"If New":{"main":[[{"node":"Create Contact","type":"main","index":0}],[{"node":"Update Contact","type":"main","index":0}]]},"Check for Existing":{"main":[[{"node":"If New","type":"main","index":0}]]},"Customer Created or Updated":{"main":[[{"node":"Check for Existing","type":"main","index":0}]]}}},"workflowInfo":{"nodeCount":6,"nodeTypes":{"n8n-nodes-base.if":{"count":1},"n8n-nodes-base.mautic":{"count":3},"n8n-nodes-base.wooCommerceTrigger":{"count":1}}},"user":{"username":"jon-n8n"},"nodes":[{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","defaults":{"name":"IF","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"IF","typeVersion":1},{"id":42,"icon":"fa:play","name":"n8n-nodes-base.start","defaults":{"name":"Start","color":"#00e000"},"iconData":{"icon":"play","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"Start","typeVersion":1},{"id":126,"icon":"file:mautic.svg","name":"n8n-nodes-base.mautic","defaults":{"name":"Mautic"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMzQ5Ljc3OXB4IiBoZWlnaHQ9IjM0OS43NzlweCIgdmlld0JveD0iMCAwIDM0OS43NzkgMzQ5Ljc3OSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMzQ5Ljc3OSAzNDkuNzc5Ig0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xNzQuODksMzQ5Ljc3OUM3OC42MTIsMzQ5Ljc3OSwwLDI3MS40NjIsMCwxNzQuODlTNzguNjEyLDAsMTc0Ljg5LDBjMjMuMjYsMCw0NS45MzEsNC40MTcsNjcuMTI5LDEzLjU0Mw0KCWM1Ljg4OSwyLjY1LDguODMzLDkuNDIyLDYuNDc4LDE1LjYwNWMtMi42NDksNS44ODgtOS40MjEsOC44MzMtMTUuNjA0LDYuNDc3Yy0xOC41NDktNy42NTUtMzcuOTgtMTEuNDgyLTU4LjAwMi0xMS40ODINCgljLTgzLjMyMywwLTE1MS4wNDEsNjcuNzE4LTE1MS4wNDEsMTUxLjA0MVM5MS41NjcsMzI2LjIyNSwxNzQuODksMzI2LjIyNWM4My4zMjMsMCwxNTEuMDQxLTY3LjcxOCwxNTEuMDQxLTE1MS4wNDENCgljMC0xNy45Ni0yLjk0NC0zNS4zMzItOS4xMjctNTEuODE5Yy0yLjM1NS02LjE4MywwLjg4My0xMi45NTUsNy4wNjYtMTUuMzFjNi4xODMtMi4zNTUsMTIuOTU0LDAuODgzLDE1LjMxLDcuMDY2DQoJYzcuMDY2LDE5LjEzOCwxMC42LDM5LjQ1MywxMC42LDYwLjA2M0MzNDkuNzc5LDI3MS4xNjcsMjcxLjQ2MiwzNDkuNzc5LDE3NC44OSwzNDkuNzc5Ii8+DQo8Zz4NCgk8cG9seWdvbiBmaWxsPSIjRkRCOTMzIiBwb2ludHM9IjI1MS40NCwxNTYuOTMgMjI0LjM1NCwxODUuMTk0IDIzOS4zNjksMjQ4LjQ5NiAyNzMuNTIyLDI0OC40OTYgCSIvPg0KPC9nPg0KPHBvbHlnb24gZmlsbD0iI0ZEQjkzMyIgcG9pbnRzPSIyNDAuMjUzLDczLjMxMiAyNDkuNjc0LDgyLjczNCAxNzQuODksMTYxLjkzNSAxMTAuOTk5LDk2LjI3NyA3NC4xOTYsMjQ4LjQ5NiAxMDguMzUsMjQ4LjQ5NiANCgkxMjguNjY1LDE2My45OTYgMTc0Ljg5LDIxNC4zNDMgMjczLjgxNywxMDYuNTgzIDI4My4yMzksMTE2LjI5OSAyOTIuNjYsNjMuMDA3ICIvPg0KPC9zdmc+DQo="},"categories":[{"id":1,"name":"Marketing & Content"},{"id":6,"name":"Communication"}],"displayName":"Mautic","typeVersion":1},{"id":235,"icon":"file:wooCommerce.svg","name":"n8n-nodes-base.wooCommerceTrigger","defaults":{"name":"WooCommerce Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="},"categories":[{"id":2,"name":"Sales"}],"displayName":"WooCommerce Trigger","typeVersion":1}],"categories":[{"id":2,"name":"Sales"}],"image":[]},{"id":1459,"name":"Notify on Telegram and Twitter when new order is added in WooCommerce","views":620,"recentViews":9823,"totalViews":620,"createdAt":"2022-02-17T15:02:14.961Z","description":"This workflow uses a WooCommerce trigger that will run a new product has been added, It will then post the product to Telegram and Twitter.\n\nTo use this workflow you will need to set the credentials to use for the WooCommerce, Twitter and Telegram nodes.","workflow":{"id":85,"name":"New WooCommerce Product to Twitter and Telegram","nodes":[{"name":"Twitter","type":"n8n-nodes-base.twitter","position":[720,300],"parameters":{"text":"=✨ New Product Announcement ✨\nWe have just added {{$json[\"name\"]}}, Head to {{$json[\"permalink\"]}} to find out more.","additionalFields":{}},"credentials":{"twitterOAuth1Api":{"id":"37","name":"joffcom"}},"typeVersion":1},{"name":"Telegram","type":"n8n-nodes-base.telegram","position":[720,500],"parameters":{"text":"=✨ New Product Announcement ✨\nWe have just added {{$json[\"name\"]}}, Head to {{$json[\"permalink\"]}} to find out more.","chatId":"123456","additionalFields":{}},"credentials":{"telegramApi":{"id":"56","name":"Telegram account"}},"typeVersion":1},{"name":"WooCommerce Trigger","type":"n8n-nodes-base.wooCommerceTrigger","position":[540,400],"webhookId":"ab7b134b-9b2d-4e0d-b496-1aee30db0808","parameters":{"event":"product.created"},"credentials":{"wooCommerceApi":{"id":"48","name":"WooCommerce account"}},"typeVersion":1}],"active":false,"settings":{},"connections":{"WooCommerce Trigger":{"main":[[{"node":"Twitter","type":"main","index":0},{"node":"Telegram","type":"main","index":0}]]}}},"workflowInfo":{"nodeCount":4,"nodeTypes":{"n8n-nodes-base.twitter":{"count":1},"n8n-nodes-base.telegram":{"count":1},"n8n-nodes-base.wooCommerceTrigger":{"count":1}}},"user":{"username":"jon-n8n"},"nodes":[{"id":42,"icon":"fa:play","name":"n8n-nodes-base.start","defaults":{"name":"Start","color":"#00e000"},"iconData":{"icon":"play","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"Start","typeVersion":1},{"id":49,"icon":"file:telegram.svg","name":"n8n-nodes-base.telegram","defaults":{"name":"Telegram"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNjYgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiIGZpbGw9IiMzN2FlZTIiLz48cGF0aCBkPSJNMjEuNjYxIDM0LjMzOGwzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0yNi42OTUgMzcuMDM0bC0uNzI5IDcuNzQ2cy0uMzA1IDIuMzczIDIuMDY4IDBsNC42NDQtNC4yMDMiIGZpbGw9IiNhOWM2ZDgiLz48cGF0aCBkPSJNMjEuNzMgMzQuNzEybC03LjgwOS0yLjU0NXMtLjkzMi0uMzc4LS42MzMtMS4yMzdjLjA2Mi0uMTc3LjE4Ni0uMzI4LjU1OS0uNTg4IDEuNzMxLTEuMjA2IDMyLjAyOC0xMi4wOTYgMzIuMDI4LTEyLjA5NnMuODU2LS4yODggMS4zNjEtLjA5N2MuMjMxLjA4OC4zNzguMTg3LjUwMy41NDguMDQ1LjEzMi4wNzEuNDExLjA2OC42ODktLjAwMy4yMDEtLjAyNy4zODYtLjA0NS42NzgtLjE4NCAyLjk3OC01LjcwNiAyNS4xOTgtNS43MDYgMjUuMTk4cy0uMzMgMS4zLTEuNTE0IDEuMzQ1Yy0uNDMyLjAxNi0uOTU2LS4wNzEtMS41ODItLjYxLTIuMzIzLTEuOTk4LTEwLjM1Mi03LjM5NC0xMi4xMjYtOC41OGEuMzQuMzQgMCAwMS0uMTQ2LS4yMzljLS4wMjUtLjEyNS4xMDgtLjI4LjEwOC0uMjhzMTMuOTgtMTIuNDI3IDE0LjM1Mi0xMy43MzFjLjAyOS0uMTAxLS4wNzktLjE1MS0uMjI2LS4xMDctLjkyOS4zNDItMTcuMDI1IDEwLjUwNi0xOC44MDEgMTEuNjI5LS4xMDQuMDY2LS4zOTUuMDIzLS4zOTUuMDIzIi8+PC9nPjwvc3ltYm9sPjwvc3ZnPg=="},"categories":[{"id":6,"name":"Communication"}],"displayName":"Telegram","typeVersion":1},{"id":235,"icon":"file:wooCommerce.svg","name":"n8n-nodes-base.wooCommerceTrigger","defaults":{"name":"WooCommerce Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="},"categories":[{"id":2,"name":"Sales"}],"displayName":"WooCommerce Trigger","typeVersion":1},{"id":325,"icon":"file:x.svg","name":"n8n-nodes-base.twitter","defaults":{"name":"X"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE4LjI0NCAyLjI1aDMuMzA4bC03LjIyNyA4LjI2IDguNTAyIDExLjI0SDE2LjE3bC01LjIxNC02LjgxN0w0Ljk5IDIxLjc1SDEuNjhsNy43My04LjgzNUwxLjI1NCAyLjI1SDguMDhsNC43MTMgNi4yMzF6bS0xLjE2MSAxNy41MmgxLjgzM0w3LjA4NCA0LjEyNkg1LjExN3oiPjwvcGF0aD48L3N2Zz4K"},"categories":[{"id":1,"name":"Marketing & Content"}],"displayName":"X (Formerly Twitter)","typeVersion":2}],"categories":[{"id":2,"name":"Sales"},{"id":19,"name":"Marketing & Growth"}],"image":[]},{"id":1457,"name":"Notify on Slack when new order is registered in WooCommerce","views":178,"recentViews":9787,"totalViews":178,"createdAt":"2022-02-17T15:01:13.489Z","description":"This workflow uses a WooCommerce trigger that will run when an order has been placed.\n\nIf the value of this is over 100 it will post it to a Slack channel.\n\nTo use this workflow you will need to set the credentials to use for the WooCommerce and Slack nodes, You will also need to pick a channel to post the message to.","workflow":{"id":81,"name":"New WooCommerce order to Slack","nodes":[{"name":"Order Created","type":"n8n-nodes-base.wooCommerceTrigger","position":[340,500],"webhookId":"287b4bf4-67ec-4c97-85d9-c0d3e6f59e6b","parameters":{"event":"order.created"},"credentials":{"wooCommerceApi":{"id":"48","name":"WooCommerce account"}},"typeVersion":1},{"name":"Send to Slack","type":"n8n-nodes-base.slack","position":[780,480],"parameters":{"text":":sparkles: There is a new order :sparkles:","channel":"woo-commerce","blocksUi":{"blocksValues":[]},"attachments":[{"color":"#66FF00","fields":{"item":[{"short":true,"title":"Order ID","value":"={{$json[\"id\"]}}"},{"short":true,"title":"Status","value":"={{$json[\"status\"]}}"},{"short":true,"title":"Total","value":"={{$json[\"currency_symbol\"]}}{{$json[\"total\"]}}"},{"short":false,"title":"Link","value":"={{$node[\"Order Created\"].json[\"_links\"][\"self\"][0][\"href\"]}}"}]},"footer":"=*Ordered:* {{$json[\"date_created\"]}} | *Transaction ID:* {{$json[\"transaction_id\"]}}"}],"otherOptions":{}},"credentials":{"slackApi":{"id":"53","name":"Slack Access Token"}},"typeVersion":1},{"name":"Price over 100","type":"n8n-nodes-base.if","position":[540,500],"parameters":{"conditions":{"number":[{"value1":"={{$json[\"total\"]}}","value2":100,"operation":"largerEqual"}]}},"typeVersion":1}],"active":false,"settings":{"saveManualExecutions":true,"saveExecutionProgress":true,"saveDataSuccessExecution":"all"},"connections":{"Order Created":{"main":[[{"node":"Price over 100","type":"main","index":0}]]},"Price over 100":{"main":[[{"node":"Send to Slack","type":"main","index":0}],[]]}}},"workflowInfo":{"nodeCount":4,"nodeTypes":{"n8n-nodes-base.if":{"count":1},"n8n-nodes-base.slack":{"count":1},"n8n-nodes-base.wooCommerceTrigger":{"count":1}}},"user":{"username":"jon-n8n"},"nodes":[{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","defaults":{"name":"IF","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"IF","typeVersion":1},{"id":40,"icon":"file:slack.svg","name":"n8n-nodes-base.slack","defaults":{"name":"Slack"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTUwLjg1MiAxNTAuODUyIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48dXNlIHhsaW5rOmhyZWY9IiNhIiB4PSIuOTI2IiB5PSIuOTI2Ii8+PHN5bWJvbCBpZD0iYSIgb3ZlcmZsb3c9InZpc2libGUiPjxnIHN0cm9rZS13aWR0aD0iMS44NTIiPjxwYXRoIGQ9Ik00MC43NDEgOTMuNTVjMC04LjczNSA2LjYwNy0xNS43NzIgMTQuODE1LTE1Ljc3MnMxNC44MTUgNy4wMzcgMTQuODE1IDE1Ljc3MnYzOC44MjRjMCA4LjczNy02LjYwNyAxNS43NzQtMTQuODE1IDE1Ljc3NHMtMTQuODE1LTcuMDM3LTE0LjgxNS0xNS43NzJ6IiBmaWxsPSIjZTAxZTVhIiBzdHJva2U9IiNlMDFlNWEiLz48cGF0aCBkPSJNOTMuNTUgMTA3LjQwOGMtOC43MzUgMC0xNS43NzItNi42MDctMTUuNzcyLTE0LjgxNXM3LjAzNy0xNC44MTUgMTUuNzcyLTE0LjgxNWgzOC44MjZjOC43MzUgMCAxNS43NzIgNi42MDcgMTUuNzcyIDE0LjgxNXMtNy4wMzcgMTQuODE1LTE1Ljc3MiAxNC44MTV6IiBmaWxsPSIjZWNiMjJkIiBzdHJva2U9IiNlY2IyMmQiLz48cGF0aCBkPSJNNzcuNzc4IDE1Ljc3MkM3Ny43NzggNy4wMzcgODQuMzg1IDAgOTIuNTkzIDBzMTQuODE1IDcuMDM3IDE0LjgxNSAxNS43NzJ2MzguODI2YzAgOC43MzUtNi42MDcgMTUuNzcyLTE0LjgxNSAxNS43NzJzLTE0LjgxNS03LjAzNy0xNC44MTUtMTUuNzcyeiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE1Ljc3MiA3MC4zNzFDNy4wMzcgNzAuMzcxIDAgNjMuNzYzIDAgNTUuNTU2czcuMDM3LTE0LjgxNSAxNS43NzItMTQuODE1aDM4LjgyNmM4LjczNSAwIDE1Ljc3MiA2LjYwNyAxNS43NzIgMTQuODE1cy03LjAzNyAxNC44MTUtMTUuNzcyIDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjxnIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiPjxwYXRoIGQ9Ik03Ny43NzggMTMzLjMzM2MwIDguMjA4IDYuNjA3IDE0LjgxNSAxNC44MTUgMTQuODE1czE0LjgxNS02LjYwNyAxNC44MTUtMTQuODE1LTYuNjA3LTE0LjgxNS0xNC44MTUtMTQuODE1SDc3Ljc3OHoiIGZpbGw9IiNlY2IyMmQiIHN0cm9rZT0iI2VjYjIyZCIvPjxwYXRoIGQ9Ik0xMzMuMzM0IDcwLjM3MWgtMTQuODE1VjU1LjU1NmMwLTguMjA3IDYuNjA3LTE0LjgxNSAxNC44MTUtMTQuODE1czE0LjgxNSA2LjYwNyAxNC44MTUgMTQuODE1LTYuNjA3IDE0LjgxNS0xNC44MTUgMTQuODE1eiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE0LjgxNSA3Ny43NzhIMjkuNjN2MTQuODE1YzAgOC4yMDctNi42MDcgMTQuODE1LTE0LjgxNSAxNC44MTVTMCAxMDAuOCAwIDkyLjU5M3M2LjYwNy0xNC44MTUgMTQuODE1LTE0LjgxNXoiIGZpbGw9IiNlMDFlNWEiIHN0cm9rZT0iI2UwMWU1YSIvPjxwYXRoIGQ9Ik03MC4zNzEgMTQuODE1VjI5LjYzSDU1LjU1NmMtOC4yMDcgMC0xNC44MTUtNi42MDctMTQuODE1LTE0LjgxNVM0Ny4zNDggMCA1NS41NTYgMHMxNC44MTUgNi42MDcgMTQuODE1IDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjwvZz48L2c+PC9zeW1ib2w+PC9zdmc+"},"categories":[{"id":6,"name":"Communication"}],"displayName":"Slack","typeVersion":2},{"id":42,"icon":"fa:play","name":"n8n-nodes-base.start","defaults":{"name":"Start","color":"#00e000"},"iconData":{"icon":"play","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"Start","typeVersion":1},{"id":235,"icon":"file:wooCommerce.svg","name":"n8n-nodes-base.wooCommerceTrigger","defaults":{"name":"WooCommerce Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="},"categories":[{"id":2,"name":"Sales"}],"displayName":"WooCommerce Trigger","typeVersion":1}],"categories":[{"id":2,"name":"Sales"}],"image":[]},{"id":1765,"name":"Get Slack notifications when new product published on WooCommerce","views":79,"recentViews":9577,"totalViews":79,"createdAt":"2022-08-12T12:36:53.409Z","description":"This workflow let's a bot in Slack notify a specific channel when a new product in WooCommerce is published and live on the site. \n\n## Prerequisites\n\n[WooCommerce](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.woocommercetrigger/) account\n[Slack](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.slack/) and a [Slack bot](https://slack.com/help/articles/115005265703-Create-a-bot-for-your-workspace)\n\n## How it works\n\n1. Listen for WooCommerce product creation\n2. If permalink starts with https://[your-url-here].com/product/\n3. Slack bot notifies channel that a new product has been added. \n\nPlease note, you must update the URL in the IF node to match your url. If your WooCommerce doesn't use the slug /product/, that will need to be updated too. \n","workflow":{"id":1016,"name":"Woocommerce to slack: notify new product created","tags":[{"id":"5","name":"FVF","createdAt":"2022-07-30T07:43:44.795Z","updatedAt":"2022-07-30T07:43:44.795Z"}],"nodes":[{"name":"If URL has /product/","type":"n8n-nodes-base.if","position":[640,300],"parameters":{"conditions":{"string":[{"value1":"={{$json[\"permalink\"]}}","value2":"https://[add-your-url-here]/product/","operation":"startsWith"}]}},"typeVersion":1},{"name":"Send message to slack","type":"n8n-nodes-base.slack","position":[920,260],"parameters":{"text":":new: A new product has been added! :new:","channel":"newproducts","blocksUi":{"blocksValues":[]},"attachments":[{"color":"#66FF00","fields":{"item":[{"short":false,"title":"Name","value":"={{$json[\"name\"]}}"},{"short":true,"title":"Price","value":"={{$json[\"regular_price\"]}}"},{"short":true,"title":"Sale Price","value":"={{$json[\"sale_price\"]}}"},{"short":false,"title":"Link","value":"={{$json[\"permalink\"]}}"}]},"footer":"=Added: {{$json[\"date_created\"]}}"}],"otherOptions":{}},"credentials":{"slackApi":{"id":"21","name":"FVF bot"}},"typeVersion":1},{"name":"On product creation","type":"n8n-nodes-base.wooCommerceTrigger","position":[460,300],"webhookId":"267c4855-6227-4d33-867e-74600097473e","parameters":{"event":"product.created"},"credentials":{"wooCommerceApi":{"id":"20","name":"WooCommerce account FVF"}},"typeVersion":1}],"active":true,"settings":{},"connections":{"On product creation":{"main":[[{"node":"If URL has /product/","type":"main","index":0}]]},"If URL has /product/":{"main":[[{"node":"Send message to slack","type":"main","index":0}]]}}},"workflowInfo":{"nodeCount":4,"nodeTypes":{"n8n-nodes-base.if":{"count":1},"n8n-nodes-base.slack":{"count":1},"n8n-nodes-base.wooCommerceTrigger":{"count":1}}},"user":{"username":"n8n-team"},"nodes":[{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","defaults":{"name":"IF","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"IF","typeVersion":1},{"id":40,"icon":"file:slack.svg","name":"n8n-nodes-base.slack","defaults":{"name":"Slack"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTUwLjg1MiAxNTAuODUyIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48dXNlIHhsaW5rOmhyZWY9IiNhIiB4PSIuOTI2IiB5PSIuOTI2Ii8+PHN5bWJvbCBpZD0iYSIgb3ZlcmZsb3c9InZpc2libGUiPjxnIHN0cm9rZS13aWR0aD0iMS44NTIiPjxwYXRoIGQ9Ik00MC43NDEgOTMuNTVjMC04LjczNSA2LjYwNy0xNS43NzIgMTQuODE1LTE1Ljc3MnMxNC44MTUgNy4wMzcgMTQuODE1IDE1Ljc3MnYzOC44MjRjMCA4LjczNy02LjYwNyAxNS43NzQtMTQuODE1IDE1Ljc3NHMtMTQuODE1LTcuMDM3LTE0LjgxNS0xNS43NzJ6IiBmaWxsPSIjZTAxZTVhIiBzdHJva2U9IiNlMDFlNWEiLz48cGF0aCBkPSJNOTMuNTUgMTA3LjQwOGMtOC43MzUgMC0xNS43NzItNi42MDctMTUuNzcyLTE0LjgxNXM3LjAzNy0xNC44MTUgMTUuNzcyLTE0LjgxNWgzOC44MjZjOC43MzUgMCAxNS43NzIgNi42MDcgMTUuNzcyIDE0LjgxNXMtNy4wMzcgMTQuODE1LTE1Ljc3MiAxNC44MTV6IiBmaWxsPSIjZWNiMjJkIiBzdHJva2U9IiNlY2IyMmQiLz48cGF0aCBkPSJNNzcuNzc4IDE1Ljc3MkM3Ny43NzggNy4wMzcgODQuMzg1IDAgOTIuNTkzIDBzMTQuODE1IDcuMDM3IDE0LjgxNSAxNS43NzJ2MzguODI2YzAgOC43MzUtNi42MDcgMTUuNzcyLTE0LjgxNSAxNS43NzJzLTE0LjgxNS03LjAzNy0xNC44MTUtMTUuNzcyeiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE1Ljc3MiA3MC4zNzFDNy4wMzcgNzAuMzcxIDAgNjMuNzYzIDAgNTUuNTU2czcuMDM3LTE0LjgxNSAxNS43NzItMTQuODE1aDM4LjgyNmM4LjczNSAwIDE1Ljc3MiA2LjYwNyAxNS43NzIgMTQuODE1cy03LjAzNyAxNC44MTUtMTUuNzcyIDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjxnIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiPjxwYXRoIGQ9Ik03Ny43NzggMTMzLjMzM2MwIDguMjA4IDYuNjA3IDE0LjgxNSAxNC44MTUgMTQuODE1czE0LjgxNS02LjYwNyAxNC44MTUtMTQuODE1LTYuNjA3LTE0LjgxNS0xNC44MTUtMTQuODE1SDc3Ljc3OHoiIGZpbGw9IiNlY2IyMmQiIHN0cm9rZT0iI2VjYjIyZCIvPjxwYXRoIGQ9Ik0xMzMuMzM0IDcwLjM3MWgtMTQuODE1VjU1LjU1NmMwLTguMjA3IDYuNjA3LTE0LjgxNSAxNC44MTUtMTQuODE1czE0LjgxNSA2LjYwNyAxNC44MTUgMTQuODE1LTYuNjA3IDE0LjgxNS0xNC44MTUgMTQuODE1eiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE0LjgxNSA3Ny43NzhIMjkuNjN2MTQuODE1YzAgOC4yMDctNi42MDcgMTQuODE1LTE0LjgxNSAxNC44MTVTMCAxMDAuOCAwIDkyLjU5M3M2LjYwNy0xNC44MTUgMTQuODE1LTE0LjgxNXoiIGZpbGw9IiNlMDFlNWEiIHN0cm9rZT0iI2UwMWU1YSIvPjxwYXRoIGQ9Ik03MC4zNzEgMTQuODE1VjI5LjYzSDU1LjU1NmMtOC4yMDcgMC0xNC44MTUtNi42MDctMTQuODE1LTE0LjgxNVM0Ny4zNDggMCA1NS41NTYgMHMxNC44MTUgNi42MDcgMTQuODE1IDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjwvZz48L2c+PC9zeW1ib2w+PC9zdmc+"},"categories":[{"id":6,"name":"Communication"}],"displayName":"Slack","typeVersion":2},{"id":42,"icon":"fa:play","name":"n8n-nodes-base.start","defaults":{"name":"Start","color":"#00e000"},"iconData":{"icon":"play","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"Start","typeVersion":1},{"id":235,"icon":"file:wooCommerce.svg","name":"n8n-nodes-base.wooCommerceTrigger","defaults":{"name":"WooCommerce Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="},"categories":[{"id":2,"name":"Sales"}],"displayName":"WooCommerce Trigger","typeVersion":1}],"categories":[{"id":2,"name":"Sales"}],"image":[]},{"id":1460,"name":"Notify on Slack when refund is registered in WooCommerce","views":85,"recentViews":9541,"totalViews":85,"createdAt":"2022-02-17T15:02:58.662Z","description":"This workflow uses a WooCommerce trigger that will run when an order has been updated and the status is refunded.\n\nIf the value of this is over 100 it will post it to a Slack channel.\n\nTo use this workflow you will need to set the credentials to use for the WooCommerce and Slack nodes, You will also need to pick a channel to post the message to.","workflow":{"id":82,"name":"New WooCommerce refund to Slack","nodes":[{"name":"Order Updated","type":"n8n-nodes-base.wooCommerceTrigger","position":[320,500],"webhookId":"f7736be3-e978-4a17-b936-7ce9f8ccdb72","parameters":{"event":"order.updated"},"credentials":{"wooCommerceApi":{"id":"48","name":"WooCommerce account"}},"typeVersion":1},{"name":"If Refund and Over 100","type":"n8n-nodes-base.if","position":[540,500],"parameters":{"conditions":{"number":[{"value1":"={{$json[\"total\"]}}","value2":100,"operation":"largerEqual"}],"string":[{"value1":"={{$json[\"status\"]}}","value2":"refunded"}]}},"typeVersion":1},{"name":"Send to Slack","type":"n8n-nodes-base.slack","position":[780,480],"parameters":{"text":":x: A refund has been issued :x:","channel":"woo-commerce","blocksUi":{"blocksValues":[]},"attachments":[{"color":"#FF0000","fields":{"item":[{"short":true,"title":"Order ID","value":"={{$json[\"id\"]}}"},{"short":true,"title":"Status","value":"={{$json[\"status\"]}}"},{"short":true,"title":"Total","value":"={{$json[\"currency_symbol\"]}}{{$json[\"total\"]}}"}]},"footer":"=*Order updated:* {{$json[\"date_modified\"]}}"}],"otherOptions":{}},"credentials":{"slackApi":{"id":"53","name":"Slack Access Token"}},"typeVersion":1}],"active":false,"settings":{"saveManualExecutions":true,"saveExecutionProgress":true,"saveDataSuccessExecution":"all"},"connections":{"Order Updated":{"main":[[{"node":"If Refund and Over 100","type":"main","index":0}]]},"If Refund and Over 100":{"main":[[{"node":"Send to Slack","type":"main","index":0}],[]]}}},"workflowInfo":{"nodeCount":4,"nodeTypes":{"n8n-nodes-base.if":{"count":1},"n8n-nodes-base.slack":{"count":1},"n8n-nodes-base.wooCommerceTrigger":{"count":1}}},"user":{"username":"jon-n8n"},"nodes":[{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","defaults":{"name":"IF","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"IF","typeVersion":1},{"id":40,"icon":"file:slack.svg","name":"n8n-nodes-base.slack","defaults":{"name":"Slack"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTUwLjg1MiAxNTAuODUyIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48dXNlIHhsaW5rOmhyZWY9IiNhIiB4PSIuOTI2IiB5PSIuOTI2Ii8+PHN5bWJvbCBpZD0iYSIgb3ZlcmZsb3c9InZpc2libGUiPjxnIHN0cm9rZS13aWR0aD0iMS44NTIiPjxwYXRoIGQ9Ik00MC43NDEgOTMuNTVjMC04LjczNSA2LjYwNy0xNS43NzIgMTQuODE1LTE1Ljc3MnMxNC44MTUgNy4wMzcgMTQuODE1IDE1Ljc3MnYzOC44MjRjMCA4LjczNy02LjYwNyAxNS43NzQtMTQuODE1IDE1Ljc3NHMtMTQuODE1LTcuMDM3LTE0LjgxNS0xNS43NzJ6IiBmaWxsPSIjZTAxZTVhIiBzdHJva2U9IiNlMDFlNWEiLz48cGF0aCBkPSJNOTMuNTUgMTA3LjQwOGMtOC43MzUgMC0xNS43NzItNi42MDctMTUuNzcyLTE0LjgxNXM3LjAzNy0xNC44MTUgMTUuNzcyLTE0LjgxNWgzOC44MjZjOC43MzUgMCAxNS43NzIgNi42MDcgMTUuNzcyIDE0LjgxNXMtNy4wMzcgMTQuODE1LTE1Ljc3MiAxNC44MTV6IiBmaWxsPSIjZWNiMjJkIiBzdHJva2U9IiNlY2IyMmQiLz48cGF0aCBkPSJNNzcuNzc4IDE1Ljc3MkM3Ny43NzggNy4wMzcgODQuMzg1IDAgOTIuNTkzIDBzMTQuODE1IDcuMDM3IDE0LjgxNSAxNS43NzJ2MzguODI2YzAgOC43MzUtNi42MDcgMTUuNzcyLTE0LjgxNSAxNS43NzJzLTE0LjgxNS03LjAzNy0xNC44MTUtMTUuNzcyeiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE1Ljc3MiA3MC4zNzFDNy4wMzcgNzAuMzcxIDAgNjMuNzYzIDAgNTUuNTU2czcuMDM3LTE0LjgxNSAxNS43NzItMTQuODE1aDM4LjgyNmM4LjczNSAwIDE1Ljc3MiA2LjYwNyAxNS43NzIgMTQuODE1cy03LjAzNyAxNC44MTUtMTUuNzcyIDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjxnIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiPjxwYXRoIGQ9Ik03Ny43NzggMTMzLjMzM2MwIDguMjA4IDYuNjA3IDE0LjgxNSAxNC44MTUgMTQuODE1czE0LjgxNS02LjYwNyAxNC44MTUtMTQuODE1LTYuNjA3LTE0LjgxNS0xNC44MTUtMTQuODE1SDc3Ljc3OHoiIGZpbGw9IiNlY2IyMmQiIHN0cm9rZT0iI2VjYjIyZCIvPjxwYXRoIGQ9Ik0xMzMuMzM0IDcwLjM3MWgtMTQuODE1VjU1LjU1NmMwLTguMjA3IDYuNjA3LTE0LjgxNSAxNC44MTUtMTQuODE1czE0LjgxNSA2LjYwNyAxNC44MTUgMTQuODE1LTYuNjA3IDE0LjgxNS0xNC44MTUgMTQuODE1eiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE0LjgxNSA3Ny43NzhIMjkuNjN2MTQuODE1YzAgOC4yMDctNi42MDcgMTQuODE1LTE0LjgxNSAxNC44MTVTMCAxMDAuOCAwIDkyLjU5M3M2LjYwNy0xNC44MTUgMTQuODE1LTE0LjgxNXoiIGZpbGw9IiNlMDFlNWEiIHN0cm9rZT0iI2UwMWU1YSIvPjxwYXRoIGQ9Ik03MC4zNzEgMTQuODE1VjI5LjYzSDU1LjU1NmMtOC4yMDcgMC0xNC44MTUtNi42MDctMTQuODE1LTE0LjgxNVM0Ny4zNDggMCA1NS41NTYgMHMxNC44MTUgNi42MDcgMTQuODE1IDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjwvZz48L2c+PC9zeW1ib2w+PC9zdmc+"},"categories":[{"id":6,"name":"Communication"}],"displayName":"Slack","typeVersion":2},{"id":42,"icon":"fa:play","name":"n8n-nodes-base.start","defaults":{"name":"Start","color":"#00e000"},"iconData":{"icon":"play","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"Start","typeVersion":1},{"id":235,"icon":"file:wooCommerce.svg","name":"n8n-nodes-base.wooCommerceTrigger","defaults":{"name":"WooCommerce Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="},"categories":[{"id":2,"name":"Sales"}],"displayName":"WooCommerce Trigger","typeVersion":1}],"categories":[{"id":2,"name":"Sales"},{"id":8,"name":"Finance & Accounting"}],"image":[]}],"image":[]}}
+{
+ "collection": {
+ "id": 1,
+ "name": "eCommerce Starter Pack",
+ "description": "eCommerce operations are complex — but there are many things that you can automate to make your life easier. This collection provides a few ideas to get started.\n\nReduce manual work and the risk of human error by automating processes such as social media promotion of products, updating customer databases, and get notifications for important events.",
+ "totalViews": 0,
+ "createdAt": "2022-02-17T12:40:50.498Z",
+ "nodes": [
+ {
+ "id": 20,
+ "name": "n8n-nodes-base.if",
+ "defaults": {
+ "name": "IF",
+ "color": "#408000"
+ },
+ "displayName": "IF",
+ "icon": "fa:map-signs",
+ "iconData": {
+ "icon": "map-signs",
+ "type": "icon"
+ },
+ "typeVersion": 1,
+ "categories": [
+ {
+ "id": 9,
+ "name": "Core Nodes"
+ }
+ ]
+ },
+ {
+ "id": 49,
+ "name": "n8n-nodes-base.telegram",
+ "defaults": {
+ "name": "Telegram"
+ },
+ "displayName": "Telegram",
+ "icon": "file:telegram.svg",
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNjYgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiIGZpbGw9IiMzN2FlZTIiLz48cGF0aCBkPSJNMjEuNjYxIDM0LjMzOGwzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0yNi42OTUgMzcuMDM0bC0uNzI5IDcuNzQ2cy0uMzA1IDIuMzczIDIuMDY4IDBsNC42NDQtNC4yMDMiIGZpbGw9IiNhOWM2ZDgiLz48cGF0aCBkPSJNMjEuNzMgMzQuNzEybC03LjgwOS0yLjU0NXMtLjkzMi0uMzc4LS42MzMtMS4yMzdjLjA2Mi0uMTc3LjE4Ni0uMzI4LjU1OS0uNTg4IDEuNzMxLTEuMjA2IDMyLjAyOC0xMi4wOTYgMzIuMDI4LTEyLjA5NnMuODU2LS4yODggMS4zNjEtLjA5N2MuMjMxLjA4OC4zNzguMTg3LjUwMy41NDguMDQ1LjEzMi4wNzEuNDExLjA2OC42ODktLjAwMy4yMDEtLjAyNy4zODYtLjA0NS42NzgtLjE4NCAyLjk3OC01LjcwNiAyNS4xOTgtNS43MDYgMjUuMTk4cy0uMzMgMS4zLTEuNTE0IDEuMzQ1Yy0uNDMyLjAxNi0uOTU2LS4wNzEtMS41ODItLjYxLTIuMzIzLTEuOTk4LTEwLjM1Mi03LjM5NC0xMi4xMjYtOC41OGEuMzQuMzQgMCAwMS0uMTQ2LS4yMzljLS4wMjUtLjEyNS4xMDgtLjI4LjEwOC0uMjhzMTMuOTgtMTIuNDI3IDE0LjM1Mi0xMy43MzFjLjAyOS0uMTAxLS4wNzktLjE1MS0uMjI2LS4xMDctLjkyOS4zNDItMTcuMDI1IDEwLjUwNi0xOC44MDEgMTEuNjI5LS4xMDQuMDY2LS4zOTUuMDIzLS4zOTUuMDIzIi8+PC9nPjwvc3ltYm9sPjwvc3ZnPg=="
+ },
+ "typeVersion": 1,
+ "categories": [
+ {
+ "id": 6,
+ "name": "Communication"
+ }
+ ]
+ },
+ {
+ "id": 107,
+ "name": "n8n-nodes-base.shopifyTrigger",
+ "defaults": {
+ "name": "Shopify Trigger"
+ },
+ "displayName": "Shopify Trigger",
+ "icon": "file:shopify.svg",
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTggNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTQ5LjI1NSAxMi40ODRhLjYzMy42MzMgMCAwMC0uNTY0LS41MjdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdjLS4zNC0uMzc2LTEuMDkyLS4yNjYtMS4zNzYtLjE4OC0uMDM3IDAtLjc1Mi4yMjUtMS45MjIuNjA1LTEuMTM3LTMuMy0zLjE1LTYuMzA2LTYuNjk2LTYuMzA2aC0uMzAzQzI4LjQzOC42MDUgMjcuMTk0IDAgMjYuMTQ0IDBjLTguMjU2LjAzNy0xMi4yIDEwLjMzMy0xMy40MzQgMTUuNTk0bC01Ljc3IDEuNzdjLTEuNzcuNTY0LTEuODM1LjYwNS0yLjA3MyAyLjI5M0wwIDU3LjE3NSAzNi40NjggNjRsMTkuNzYzLTQuMjZjMC0uMDM3LTYuOTQtNDYuODk3LTYuOTc2LTQ3LjI1NXpNMzQuNDMxIDguODZjLS45MTcuMzAzLTEuOTYzLjYwNS0zLjEuOTQ1di0uNjhhMTUuMDMgMTUuMDMgMCAwMC0uNzUyLTQuOTk5YzEuODQ4LjI4NCAzLjEgMi4zNTcgMy44NDMgNC43MzN6bS02LjA2OC00LjI5OGMuNjAzIDEuNzc4Ljg4MyAzLjY1LjgyNiA1LjUyN3YuMzRsLTYuMzc1IDEuOTYzYzEuMjQ4LTQuNjYgMy41NS02Ljk2MiA1LjU1LTcuODN6bS0yLjQ1LTIuMjkzYTEuOTQgMS45NCAwIDAxMS4wNTUuMzM5Yy0yLjY2IDEuMjM4LTUuNDcyIDQuMzY2LTYuNjc4IDEwLjYyN2wtNS4wNDUgMS41NDZDMTYuNjY4IDEwLjAzIDE5Ljk4OCAyLjI2IDI1LjkxIDIuMjZ6IiBmaWxsPSIjOTViZjQ3Ii8+PHBhdGggZD0iTTQ4LjY5MSAxMS45NTdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdhLjc1My43NTMgMCAwMC0uNTI3LS4yMjVMMzYuNDcyIDY0bDE5Ljc2My00LjI2LTYuOTgtNDcuMjE4YS42OC42OCAwIDAwLS41NjQtLjU2NHoiIGZpbGw9IiM1ZThlM2UiLz48cGF0aCBkPSJNMjkuNzU4IDIyLjlsLTIuNDU0IDcuMjQyYTExLjM2IDExLjM2IDAgMDAtNC43NTItMS4xMzNjLTMuODQ4IDAtNC4wMzYgMi40MTItNC4wMzYgMy4wMTggMCAzLjI5OCA4LjYzNiA0LjU2NCA4LjYzNiAxMi4zMzMgMCA2LjEtMy44ODUgMTAuMDMtOS4xIDEwLjAzLTYuMjYgMC05LjQ2Ny0zLjg4NS05LjQ2Ny0zLjg4NWwxLjY2NS01LjU1czMuMjggMi44MyA2LjA3MyAyLjgzYTIuNDcgMi40NyAwIDAwMi41NjQtMi40OWMwLTQuMzQtNy4xLTQuNTI3LTcuMS0xMS42MTggMC01Ljk2MiA0LjI5OC0xMS43NyAxMi45MzQtMTEuNzcgMy4zOTQuMDUgNS4wMTggMSA1LjAxOCAxeiIvPjwvZz48L3N5bWJvbD48L3N2Zz4="
+ },
+ "typeVersion": 1,
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ }
+ ]
+ },
+ {
+ "id": 126,
+ "name": "n8n-nodes-base.mautic",
+ "defaults": {
+ "name": "Mautic"
+ },
+ "displayName": "Mautic",
+ "icon": "file:mautic.svg",
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMzQ5Ljc3OXB4IiBoZWlnaHQ9IjM0OS43NzlweCIgdmlld0JveD0iMCAwIDM0OS43NzkgMzQ5Ljc3OSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMzQ5Ljc3OSAzNDkuNzc5Ig0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xNzQuODksMzQ5Ljc3OUM3OC42MTIsMzQ5Ljc3OSwwLDI3MS40NjIsMCwxNzQuODlTNzguNjEyLDAsMTc0Ljg5LDBjMjMuMjYsMCw0NS45MzEsNC40MTcsNjcuMTI5LDEzLjU0Mw0KCWM1Ljg4OSwyLjY1LDguODMzLDkuNDIyLDYuNDc4LDE1LjYwNWMtMi42NDksNS44ODgtOS40MjEsOC44MzMtMTUuNjA0LDYuNDc3Yy0xOC41NDktNy42NTUtMzcuOTgtMTEuNDgyLTU4LjAwMi0xMS40ODINCgljLTgzLjMyMywwLTE1MS4wNDEsNjcuNzE4LTE1MS4wNDEsMTUxLjA0MVM5MS41NjcsMzI2LjIyNSwxNzQuODksMzI2LjIyNWM4My4zMjMsMCwxNTEuMDQxLTY3LjcxOCwxNTEuMDQxLTE1MS4wNDENCgljMC0xNy45Ni0yLjk0NC0zNS4zMzItOS4xMjctNTEuODE5Yy0yLjM1NS02LjE4MywwLjg4My0xMi45NTUsNy4wNjYtMTUuMzFjNi4xODMtMi4zNTUsMTIuOTU0LDAuODgzLDE1LjMxLDcuMDY2DQoJYzcuMDY2LDE5LjEzOCwxMC42LDM5LjQ1MywxMC42LDYwLjA2M0MzNDkuNzc5LDI3MS4xNjcsMjcxLjQ2MiwzNDkuNzc5LDE3NC44OSwzNDkuNzc5Ii8+DQo8Zz4NCgk8cG9seWdvbiBmaWxsPSIjRkRCOTMzIiBwb2ludHM9IjI1MS40NCwxNTYuOTMgMjI0LjM1NCwxODUuMTk0IDIzOS4zNjksMjQ4LjQ5NiAyNzMuNTIyLDI0OC40OTYgCSIvPg0KPC9nPg0KPHBvbHlnb24gZmlsbD0iI0ZEQjkzMyIgcG9pbnRzPSIyNDAuMjUzLDczLjMxMiAyNDkuNjc0LDgyLjczNCAxNzQuODksMTYxLjkzNSAxMTAuOTk5LDk2LjI3NyA3NC4xOTYsMjQ4LjQ5NiAxMDguMzUsMjQ4LjQ5NiANCgkxMjguNjY1LDE2My45OTYgMTc0Ljg5LDIxNC4zNDMgMjczLjgxNywxMDYuNTgzIDI4My4yMzksMTE2LjI5OSAyOTIuNjYsNjMuMDA3ICIvPg0KPC9zdmc+DQo="
+ },
+ "typeVersion": 1,
+ "categories": [
+ {
+ "id": 1,
+ "name": "Marketing & Content"
+ },
+ {
+ "id": 6,
+ "name": "Communication"
+ }
+ ]
+ },
+ {
+ "id": 235,
+ "name": "n8n-nodes-base.wooCommerceTrigger",
+ "defaults": {
+ "name": "WooCommerce Trigger"
+ },
+ "displayName": "WooCommerce Trigger",
+ "icon": "file:wooCommerce.svg",
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="
+ },
+ "typeVersion": 1,
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ }
+ ]
+ },
+ {
+ "id": 325,
+ "name": "n8n-nodes-base.twitter",
+ "defaults": {
+ "name": "X"
+ },
+ "displayName": "X (Formerly Twitter)",
+ "icon": "file:x.svg",
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE4LjI0NCAyLjI1aDMuMzA4bC03LjIyNyA4LjI2IDguNTAyIDExLjI0SDE2LjE3bC01LjIxNC02LjgxN0w0Ljk5IDIxLjc1SDEuNjhsNy43My04LjgzNUwxLjI1NCAyLjI1SDguMDhsNC43MTMgNi4yMzF6bS0xLjE2MSAxNy41MmgxLjgzM0w3LjA4NCA0LjEyNkg1LjExN3oiPjwvcGF0aD48L3N2Zz4K"
+ },
+ "typeVersion": 2,
+ "categories": [
+ {
+ "id": 1,
+ "name": "Marketing & Content"
+ }
+ ]
+ }
+ ],
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ }
+ ],
+ "workflows": [
+ {
+ "id": 1205,
+ "name": "Promote new Shopify products",
+ "views": 485,
+ "recentViews": 9850,
+ "totalViews": 485,
+ "createdAt": "2021-08-24T10:40:50.007Z",
+ "description": "This workflow automatically promotes your new Shopify products on Twitter and Telegram. This workflow is also featured in the blog post [*6 e-commerce workflows to power up your Shopify store*](https://n8n.io/blog/no-code-ecommerce-workflow-automations/#promote-your-new-products-on-social-media).\n\n## Prerequisites\n\n- A Shopify account and [credentials](https://docs.n8n.io/integrations/credentials/shopify/)\n- A Twitter account and [credentials](https://docs.n8n.io/integrations/credentials/twitter/)\n- A Telegram account and [credentials](https://docs.n8n.io/integrations/credentials/telegram/) for the channel you want to send messages to.\n\n## Nodes\n\n- [Shopify Trigger node](https://docs.n8n.io/integrations/trigger-nodes/n8n-nodes-base.shopifytrigger/) triggers the workflow when you create a new product in Shopify.\n- [Twitter node](https://docs.n8n.io/integrations/nodes/n8n-nodes-base.twitter/) posts a tweet with the text \"Hey there, my design is now on a new product! Visit my {shop name} to get this cool {product title} (and check out more {product type})\".\n- [Telegram node](https://docs.n8n.io/integrations/nodes/n8n-nodes-base.telegram/) posts a message with the same text as above in a Telegram channel.",
+ "workflow": {
+ "nodes": [
+ {
+ "name": "Twitter",
+ "type": "n8n-nodes-base.twitter",
+ "position": [
+ 720,
+ -220
+ ],
+ "parameters": {
+ "text": "=Hey there, my design is now on a new product ✨\nVisit my {{$json[\"vendor\"]}} shop to get this cool{{$json[\"title\"]}} (and check out more {{$json[\"product_type\"]}}) 🛍️",
+ "additionalFields": {}
+ },
+ "credentials": {
+ "twitterOAuth1Api": "twitter"
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "Telegram",
+ "type": "n8n-nodes-base.telegram",
+ "position": [
+ 720,
+ -20
+ ],
+ "parameters": {
+ "text": "=Hey there, my design is now on a new product!\nVisit my {{$json[\"vendor\"]}} shop to get this cool{{$json[\"title\"]}} (and check out more {{$json[\"product_type\"]}})",
+ "chatId": "123456",
+ "additionalFields": {}
+ },
+ "credentials": {
+ "telegramApi": "telegram_habot"
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "product created",
+ "type": "n8n-nodes-base.shopifyTrigger",
+ "position": [
+ 540,
+ -110
+ ],
+ "webhookId": "2a7e0e50-8f09-4a2b-bf54-a849a6ac4fe0",
+ "parameters": {
+ "topic": "products/create"
+ },
+ "credentials": {
+ "shopifyApi": "shopify_nodeqa"
+ },
+ "typeVersion": 1
+ }
+ ],
+ "connections": {
+ "product created": {
+ "main": [
+ [
+ {
+ "node": "Twitter",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Telegram",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ }
+ },
+ "workflowInfo": {
+ "nodeCount": 3,
+ "nodeTypes": {
+ "n8n-nodes-base.twitter": {
+ "count": 1
+ },
+ "n8n-nodes-base.telegram": {
+ "count": 1
+ },
+ "n8n-nodes-base.shopifyTrigger": {
+ "count": 1
+ }
+ }
+ },
+ "user": {
+ "username": "lorenanda"
+ },
+ "nodes": [
+ {
+ "id": 49,
+ "icon": "file:telegram.svg",
+ "name": "n8n-nodes-base.telegram",
+ "defaults": {
+ "name": "Telegram"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNjYgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiIGZpbGw9IiMzN2FlZTIiLz48cGF0aCBkPSJNMjEuNjYxIDM0LjMzOGwzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0yNi42OTUgMzcuMDM0bC0uNzI5IDcuNzQ2cy0uMzA1IDIuMzczIDIuMDY4IDBsNC42NDQtNC4yMDMiIGZpbGw9IiNhOWM2ZDgiLz48cGF0aCBkPSJNMjEuNzMgMzQuNzEybC03LjgwOS0yLjU0NXMtLjkzMi0uMzc4LS42MzMtMS4yMzdjLjA2Mi0uMTc3LjE4Ni0uMzI4LjU1OS0uNTg4IDEuNzMxLTEuMjA2IDMyLjAyOC0xMi4wOTYgMzIuMDI4LTEyLjA5NnMuODU2LS4yODggMS4zNjEtLjA5N2MuMjMxLjA4OC4zNzguMTg3LjUwMy41NDguMDQ1LjEzMi4wNzEuNDExLjA2OC42ODktLjAwMy4yMDEtLjAyNy4zODYtLjA0NS42NzgtLjE4NCAyLjk3OC01LjcwNiAyNS4xOTgtNS43MDYgMjUuMTk4cy0uMzMgMS4zLTEuNTE0IDEuMzQ1Yy0uNDMyLjAxNi0uOTU2LS4wNzEtMS41ODItLjYxLTIuMzIzLTEuOTk4LTEwLjM1Mi03LjM5NC0xMi4xMjYtOC41OGEuMzQuMzQgMCAwMS0uMTQ2LS4yMzljLS4wMjUtLjEyNS4xMDgtLjI4LjEwOC0uMjhzMTMuOTgtMTIuNDI3IDE0LjM1Mi0xMy43MzFjLjAyOS0uMTAxLS4wNzktLjE1MS0uMjI2LS4xMDctLjkyOS4zNDItMTcuMDI1IDEwLjUwNi0xOC44MDEgMTEuNjI5LS4xMDQuMDY2LS4zOTUuMDIzLS4zOTUuMDIzIi8+PC9nPjwvc3ltYm9sPjwvc3ZnPg=="
+ },
+ "categories": [
+ {
+ "id": 6,
+ "name": "Communication"
+ }
+ ],
+ "displayName": "Telegram",
+ "typeVersion": 1
+ },
+ {
+ "id": 107,
+ "icon": "file:shopify.svg",
+ "name": "n8n-nodes-base.shopifyTrigger",
+ "defaults": {
+ "name": "Shopify Trigger"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTggNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTQ5LjI1NSAxMi40ODRhLjYzMy42MzMgMCAwMC0uNTY0LS41MjdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdjLS4zNC0uMzc2LTEuMDkyLS4yNjYtMS4zNzYtLjE4OC0uMDM3IDAtLjc1Mi4yMjUtMS45MjIuNjA1LTEuMTM3LTMuMy0zLjE1LTYuMzA2LTYuNjk2LTYuMzA2aC0uMzAzQzI4LjQzOC42MDUgMjcuMTk0IDAgMjYuMTQ0IDBjLTguMjU2LjAzNy0xMi4yIDEwLjMzMy0xMy40MzQgMTUuNTk0bC01Ljc3IDEuNzdjLTEuNzcuNTY0LTEuODM1LjYwNS0yLjA3MyAyLjI5M0wwIDU3LjE3NSAzNi40NjggNjRsMTkuNzYzLTQuMjZjMC0uMDM3LTYuOTQtNDYuODk3LTYuOTc2LTQ3LjI1NXpNMzQuNDMxIDguODZjLS45MTcuMzAzLTEuOTYzLjYwNS0zLjEuOTQ1di0uNjhhMTUuMDMgMTUuMDMgMCAwMC0uNzUyLTQuOTk5YzEuODQ4LjI4NCAzLjEgMi4zNTcgMy44NDMgNC43MzN6bS02LjA2OC00LjI5OGMuNjAzIDEuNzc4Ljg4MyAzLjY1LjgyNiA1LjUyN3YuMzRsLTYuMzc1IDEuOTYzYzEuMjQ4LTQuNjYgMy41NS02Ljk2MiA1LjU1LTcuODN6bS0yLjQ1LTIuMjkzYTEuOTQgMS45NCAwIDAxMS4wNTUuMzM5Yy0yLjY2IDEuMjM4LTUuNDcyIDQuMzY2LTYuNjc4IDEwLjYyN2wtNS4wNDUgMS41NDZDMTYuNjY4IDEwLjAzIDE5Ljk4OCAyLjI2IDI1LjkxIDIuMjZ6IiBmaWxsPSIjOTViZjQ3Ii8+PHBhdGggZD0iTTQ4LjY5MSAxMS45NTdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdhLjc1My43NTMgMCAwMC0uNTI3LS4yMjVMMzYuNDcyIDY0bDE5Ljc2My00LjI2LTYuOTgtNDcuMjE4YS42OC42OCAwIDAwLS41NjQtLjU2NHoiIGZpbGw9IiM1ZThlM2UiLz48cGF0aCBkPSJNMjkuNzU4IDIyLjlsLTIuNDU0IDcuMjQyYTExLjM2IDExLjM2IDAgMDAtNC43NTItMS4xMzNjLTMuODQ4IDAtNC4wMzYgMi40MTItNC4wMzYgMy4wMTggMCAzLjI5OCA4LjYzNiA0LjU2NCA4LjYzNiAxMi4zMzMgMCA2LjEtMy44ODUgMTAuMDMtOS4xIDEwLjAzLTYuMjYgMC05LjQ2Ny0zLjg4NS05LjQ2Ny0zLjg4NWwxLjY2NS01LjU1czMuMjggMi44MyA2LjA3MyAyLjgzYTIuNDcgMi40NyAwIDAwMi41NjQtMi40OWMwLTQuMzQtNy4xLTQuNTI3LTcuMS0xMS42MTggMC01Ljk2MiA0LjI5OC0xMS43NyAxMi45MzQtMTEuNzcgMy4zOTQuMDUgNS4wMTggMSA1LjAxOCAxeiIvPjwvZz48L3N5bWJvbD48L3N2Zz4="
+ },
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ }
+ ],
+ "displayName": "Shopify Trigger",
+ "typeVersion": 1
+ },
+ {
+ "id": 325,
+ "icon": "file:x.svg",
+ "name": "n8n-nodes-base.twitter",
+ "defaults": {
+ "name": "X"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE4LjI0NCAyLjI1aDMuMzA4bC03LjIyNyA4LjI2IDguNTAyIDExLjI0SDE2LjE3bC01LjIxNC02LjgxN0w0Ljk5IDIxLjc1SDEuNjhsNy43My04LjgzNUwxLjI1NCAyLjI1SDguMDhsNC43MTMgNi4yMzF6bS0xLjE2MSAxNy41MmgxLjgzM0w3LjA4NCA0LjEyNkg1LjExN3oiPjwvcGF0aD48L3N2Zz4K"
+ },
+ "categories": [
+ {
+ "id": 1,
+ "name": "Marketing & Content"
+ }
+ ],
+ "displayName": "X (Formerly Twitter)",
+ "typeVersion": 2
+ }
+ ],
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ },
+ {
+ "id": 19,
+ "name": "Marketing & Growth"
+ }
+ ],
+ "image": [
+ {
+ "id": 527,
+ "url": "https://n8niostorageaccount.blob.core.windows.net/n8nio-strapi-blobs-prod/assets/89a078b208fe4c6181902608b1cd1332.png"
+ }
+ ]
+ },
+ {
+ "id": 1456,
+ "name": "Add new customers from WooCommerce to Mautic",
+ "views": 333,
+ "recentViews": 9833,
+ "totalViews": 333,
+ "createdAt": "2022-02-17T15:00:40.748Z",
+ "description": "This workflow uses a WooCommerce trigger that will run when a new customer has been added, It will then add the customer to Mautic.\n\nTo use this workflow you will need to set the credentials to use for the WooCommerce and Mautic nodes.",
+ "workflow": {
+ "id": 83,
+ "name": "New WooCommerce Customer to Mautic",
+ "nodes": [
+ {
+ "name": "Check for Existing",
+ "type": "n8n-nodes-base.mautic",
+ "position": [
+ 280,
+ 480
+ ],
+ "parameters": {
+ "options": {
+ "search": "={{$json[\"email\"]}}"
+ },
+ "operation": "getAll",
+ "authentication": "oAuth2"
+ },
+ "credentials": {
+ "mauticOAuth2Api": {
+ "id": "54",
+ "name": "Mautic account"
+ }
+ },
+ "typeVersion": 1,
+ "alwaysOutputData": true
+ },
+ {
+ "name": "If New",
+ "type": "n8n-nodes-base.if",
+ "position": [
+ 460,
+ 480
+ ],
+ "parameters": {
+ "conditions": {
+ "string": [
+ {
+ "value1": "={{$json[\"id\"]}}",
+ "operation": "isEmpty"
+ }
+ ]
+ }
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "Create Contact",
+ "type": "n8n-nodes-base.mautic",
+ "position": [
+ 680,
+ 320
+ ],
+ "parameters": {
+ "email": "={{$node[\"Customer Created\"].json[\"email\"]}}",
+ "company": "={{$node[\"Customer Created\"].json[\"billing\"][\"company\"]}}",
+ "options": {},
+ "lastName": "={{$node[\"Customer Created\"].json[\"last_name\"]}}",
+ "firstName": "={{$node[\"Customer Created\"].json[\"first_name\"]}}",
+ "authentication": "oAuth2",
+ "additionalFields": {}
+ },
+ "credentials": {
+ "mauticOAuth2Api": {
+ "id": "54",
+ "name": "Mautic account"
+ }
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "Update Contact",
+ "type": "n8n-nodes-base.mautic",
+ "position": [
+ 680,
+ 580
+ ],
+ "parameters": {
+ "options": {},
+ "contactId": "={{$json[\"id\"]}}",
+ "operation": "update",
+ "updateFields": {
+ "lastName": "={{$node[\"Customer Created or Updated\"].json[\"last_name\"]}}",
+ "firstName": "={{$node[\"Customer Created or Updated\"].json[\"first_name\"]}}"
+ },
+ "authentication": "oAuth2"
+ },
+ "credentials": {
+ "mauticOAuth2Api": {
+ "id": "54",
+ "name": "Mautic account"
+ }
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "Customer Created or Updated",
+ "type": "n8n-nodes-base.wooCommerceTrigger",
+ "position": [
+ 100,
+ 480
+ ],
+ "webhookId": "5d89e322-a5e0-4cce-9eab-185e8375175b",
+ "parameters": {
+ "event": "customer.updated"
+ },
+ "credentials": {
+ "wooCommerceApi": {
+ "id": "48",
+ "name": "WooCommerce account"
+ }
+ },
+ "typeVersion": 1
+ }
+ ],
+ "active": false,
+ "settings": {
+ "saveManualExecutions": true,
+ "saveExecutionProgress": true,
+ "saveDataSuccessExecution": "all"
+ },
+ "connections": {
+ "If New": {
+ "main": [
+ [
+ {
+ "node": "Create Contact",
+ "type": "main",
+ "index": 0
+ }
+ ],
+ [
+ {
+ "node": "Update Contact",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Check for Existing": {
+ "main": [
+ [
+ {
+ "node": "If New",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Customer Created or Updated": {
+ "main": [
+ [
+ {
+ "node": "Check for Existing",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ }
+ },
+ "workflowInfo": {
+ "nodeCount": 6,
+ "nodeTypes": {
+ "n8n-nodes-base.if": {
+ "count": 1
+ },
+ "n8n-nodes-base.mautic": {
+ "count": 3
+ },
+ "n8n-nodes-base.wooCommerceTrigger": {
+ "count": 1
+ }
+ }
+ },
+ "user": {
+ "username": "jon-n8n"
+ },
+ "nodes": [
+ {
+ "id": 20,
+ "icon": "fa:map-signs",
+ "name": "n8n-nodes-base.if",
+ "defaults": {
+ "name": "IF",
+ "color": "#408000"
+ },
+ "iconData": {
+ "icon": "map-signs",
+ "type": "icon"
+ },
+ "categories": [
+ {
+ "id": 9,
+ "name": "Core Nodes"
+ }
+ ],
+ "displayName": "IF",
+ "typeVersion": 1
+ },
+ {
+ "id": 42,
+ "icon": "fa:play",
+ "name": "n8n-nodes-base.start",
+ "defaults": {
+ "name": "Start",
+ "color": "#00e000"
+ },
+ "iconData": {
+ "icon": "play",
+ "type": "icon"
+ },
+ "categories": [
+ {
+ "id": 9,
+ "name": "Core Nodes"
+ }
+ ],
+ "displayName": "Start",
+ "typeVersion": 1
+ },
+ {
+ "id": 126,
+ "icon": "file:mautic.svg",
+ "name": "n8n-nodes-base.mautic",
+ "defaults": {
+ "name": "Mautic"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMzQ5Ljc3OXB4IiBoZWlnaHQ9IjM0OS43NzlweCIgdmlld0JveD0iMCAwIDM0OS43NzkgMzQ5Ljc3OSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMzQ5Ljc3OSAzNDkuNzc5Ig0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xNzQuODksMzQ5Ljc3OUM3OC42MTIsMzQ5Ljc3OSwwLDI3MS40NjIsMCwxNzQuODlTNzguNjEyLDAsMTc0Ljg5LDBjMjMuMjYsMCw0NS45MzEsNC40MTcsNjcuMTI5LDEzLjU0Mw0KCWM1Ljg4OSwyLjY1LDguODMzLDkuNDIyLDYuNDc4LDE1LjYwNWMtMi42NDksNS44ODgtOS40MjEsOC44MzMtMTUuNjA0LDYuNDc3Yy0xOC41NDktNy42NTUtMzcuOTgtMTEuNDgyLTU4LjAwMi0xMS40ODINCgljLTgzLjMyMywwLTE1MS4wNDEsNjcuNzE4LTE1MS4wNDEsMTUxLjA0MVM5MS41NjcsMzI2LjIyNSwxNzQuODksMzI2LjIyNWM4My4zMjMsMCwxNTEuMDQxLTY3LjcxOCwxNTEuMDQxLTE1MS4wNDENCgljMC0xNy45Ni0yLjk0NC0zNS4zMzItOS4xMjctNTEuODE5Yy0yLjM1NS02LjE4MywwLjg4My0xMi45NTUsNy4wNjYtMTUuMzFjNi4xODMtMi4zNTUsMTIuOTU0LDAuODgzLDE1LjMxLDcuMDY2DQoJYzcuMDY2LDE5LjEzOCwxMC42LDM5LjQ1MywxMC42LDYwLjA2M0MzNDkuNzc5LDI3MS4xNjcsMjcxLjQ2MiwzNDkuNzc5LDE3NC44OSwzNDkuNzc5Ii8+DQo8Zz4NCgk8cG9seWdvbiBmaWxsPSIjRkRCOTMzIiBwb2ludHM9IjI1MS40NCwxNTYuOTMgMjI0LjM1NCwxODUuMTk0IDIzOS4zNjksMjQ4LjQ5NiAyNzMuNTIyLDI0OC40OTYgCSIvPg0KPC9nPg0KPHBvbHlnb24gZmlsbD0iI0ZEQjkzMyIgcG9pbnRzPSIyNDAuMjUzLDczLjMxMiAyNDkuNjc0LDgyLjczNCAxNzQuODksMTYxLjkzNSAxMTAuOTk5LDk2LjI3NyA3NC4xOTYsMjQ4LjQ5NiAxMDguMzUsMjQ4LjQ5NiANCgkxMjguNjY1LDE2My45OTYgMTc0Ljg5LDIxNC4zNDMgMjczLjgxNywxMDYuNTgzIDI4My4yMzksMTE2LjI5OSAyOTIuNjYsNjMuMDA3ICIvPg0KPC9zdmc+DQo="
+ },
+ "categories": [
+ {
+ "id": 1,
+ "name": "Marketing & Content"
+ },
+ {
+ "id": 6,
+ "name": "Communication"
+ }
+ ],
+ "displayName": "Mautic",
+ "typeVersion": 1
+ },
+ {
+ "id": 235,
+ "icon": "file:wooCommerce.svg",
+ "name": "n8n-nodes-base.wooCommerceTrigger",
+ "defaults": {
+ "name": "WooCommerce Trigger"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="
+ },
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ }
+ ],
+ "displayName": "WooCommerce Trigger",
+ "typeVersion": 1
+ }
+ ],
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ }
+ ],
+ "image": []
+ },
+ {
+ "id": 1459,
+ "name": "Notify on Telegram and Twitter when new order is added in WooCommerce",
+ "views": 620,
+ "recentViews": 9823,
+ "totalViews": 620,
+ "createdAt": "2022-02-17T15:02:14.961Z",
+ "description": "This workflow uses a WooCommerce trigger that will run a new product has been added, It will then post the product to Telegram and Twitter.\n\nTo use this workflow you will need to set the credentials to use for the WooCommerce, Twitter and Telegram nodes.",
+ "workflow": {
+ "id": 85,
+ "name": "New WooCommerce Product to Twitter and Telegram",
+ "nodes": [
+ {
+ "name": "Twitter",
+ "type": "n8n-nodes-base.twitter",
+ "position": [
+ 720,
+ 300
+ ],
+ "parameters": {
+ "text": "=✨ New Product Announcement ✨\nWe have just added {{$json[\"name\"]}}, Head to {{$json[\"permalink\"]}} to find out more.",
+ "additionalFields": {}
+ },
+ "credentials": {
+ "twitterOAuth1Api": {
+ "id": "37",
+ "name": "joffcom"
+ }
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "Telegram",
+ "type": "n8n-nodes-base.telegram",
+ "position": [
+ 720,
+ 500
+ ],
+ "parameters": {
+ "text": "=✨ New Product Announcement ✨\nWe have just added {{$json[\"name\"]}}, Head to {{$json[\"permalink\"]}} to find out more.",
+ "chatId": "123456",
+ "additionalFields": {}
+ },
+ "credentials": {
+ "telegramApi": {
+ "id": "56",
+ "name": "Telegram account"
+ }
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "WooCommerce Trigger",
+ "type": "n8n-nodes-base.wooCommerceTrigger",
+ "position": [
+ 540,
+ 400
+ ],
+ "webhookId": "ab7b134b-9b2d-4e0d-b496-1aee30db0808",
+ "parameters": {
+ "event": "product.created"
+ },
+ "credentials": {
+ "wooCommerceApi": {
+ "id": "48",
+ "name": "WooCommerce account"
+ }
+ },
+ "typeVersion": 1
+ }
+ ],
+ "active": false,
+ "settings": {},
+ "connections": {
+ "WooCommerce Trigger": {
+ "main": [
+ [
+ {
+ "node": "Twitter",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Telegram",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ }
+ },
+ "workflowInfo": {
+ "nodeCount": 4,
+ "nodeTypes": {
+ "n8n-nodes-base.twitter": {
+ "count": 1
+ },
+ "n8n-nodes-base.telegram": {
+ "count": 1
+ },
+ "n8n-nodes-base.wooCommerceTrigger": {
+ "count": 1
+ }
+ }
+ },
+ "user": {
+ "username": "jon-n8n"
+ },
+ "nodes": [
+ {
+ "id": 42,
+ "icon": "fa:play",
+ "name": "n8n-nodes-base.start",
+ "defaults": {
+ "name": "Start",
+ "color": "#00e000"
+ },
+ "iconData": {
+ "icon": "play",
+ "type": "icon"
+ },
+ "categories": [
+ {
+ "id": 9,
+ "name": "Core Nodes"
+ }
+ ],
+ "displayName": "Start",
+ "typeVersion": 1
+ },
+ {
+ "id": 49,
+ "icon": "file:telegram.svg",
+ "name": "n8n-nodes-base.telegram",
+ "defaults": {
+ "name": "Telegram"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNjYgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiIGZpbGw9IiMzN2FlZTIiLz48cGF0aCBkPSJNMjEuNjYxIDM0LjMzOGwzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0yNi42OTUgMzcuMDM0bC0uNzI5IDcuNzQ2cy0uMzA1IDIuMzczIDIuMDY4IDBsNC42NDQtNC4yMDMiIGZpbGw9IiNhOWM2ZDgiLz48cGF0aCBkPSJNMjEuNzMgMzQuNzEybC03LjgwOS0yLjU0NXMtLjkzMi0uMzc4LS42MzMtMS4yMzdjLjA2Mi0uMTc3LjE4Ni0uMzI4LjU1OS0uNTg4IDEuNzMxLTEuMjA2IDMyLjAyOC0xMi4wOTYgMzIuMDI4LTEyLjA5NnMuODU2LS4yODggMS4zNjEtLjA5N2MuMjMxLjA4OC4zNzguMTg3LjUwMy41NDguMDQ1LjEzMi4wNzEuNDExLjA2OC42ODktLjAwMy4yMDEtLjAyNy4zODYtLjA0NS42NzgtLjE4NCAyLjk3OC01LjcwNiAyNS4xOTgtNS43MDYgMjUuMTk4cy0uMzMgMS4zLTEuNTE0IDEuMzQ1Yy0uNDMyLjAxNi0uOTU2LS4wNzEtMS41ODItLjYxLTIuMzIzLTEuOTk4LTEwLjM1Mi03LjM5NC0xMi4xMjYtOC41OGEuMzQuMzQgMCAwMS0uMTQ2LS4yMzljLS4wMjUtLjEyNS4xMDgtLjI4LjEwOC0uMjhzMTMuOTgtMTIuNDI3IDE0LjM1Mi0xMy43MzFjLjAyOS0uMTAxLS4wNzktLjE1MS0uMjI2LS4xMDctLjkyOS4zNDItMTcuMDI1IDEwLjUwNi0xOC44MDEgMTEuNjI5LS4xMDQuMDY2LS4zOTUuMDIzLS4zOTUuMDIzIi8+PC9nPjwvc3ltYm9sPjwvc3ZnPg=="
+ },
+ "categories": [
+ {
+ "id": 6,
+ "name": "Communication"
+ }
+ ],
+ "displayName": "Telegram",
+ "typeVersion": 1
+ },
+ {
+ "id": 235,
+ "icon": "file:wooCommerce.svg",
+ "name": "n8n-nodes-base.wooCommerceTrigger",
+ "defaults": {
+ "name": "WooCommerce Trigger"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="
+ },
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ }
+ ],
+ "displayName": "WooCommerce Trigger",
+ "typeVersion": 1
+ },
+ {
+ "id": 325,
+ "icon": "file:x.svg",
+ "name": "n8n-nodes-base.twitter",
+ "defaults": {
+ "name": "X"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE4LjI0NCAyLjI1aDMuMzA4bC03LjIyNyA4LjI2IDguNTAyIDExLjI0SDE2LjE3bC01LjIxNC02LjgxN0w0Ljk5IDIxLjc1SDEuNjhsNy43My04LjgzNUwxLjI1NCAyLjI1SDguMDhsNC43MTMgNi4yMzF6bS0xLjE2MSAxNy41MmgxLjgzM0w3LjA4NCA0LjEyNkg1LjExN3oiPjwvcGF0aD48L3N2Zz4K"
+ },
+ "categories": [
+ {
+ "id": 1,
+ "name": "Marketing & Content"
+ }
+ ],
+ "displayName": "X (Formerly Twitter)",
+ "typeVersion": 2
+ }
+ ],
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ },
+ {
+ "id": 19,
+ "name": "Marketing & Growth"
+ }
+ ],
+ "image": []
+ },
+ {
+ "id": 1457,
+ "name": "Notify on Slack when new order is registered in WooCommerce",
+ "views": 178,
+ "recentViews": 9787,
+ "totalViews": 178,
+ "createdAt": "2022-02-17T15:01:13.489Z",
+ "description": "This workflow uses a WooCommerce trigger that will run when an order has been placed.\n\nIf the value of this is over 100 it will post it to a Slack channel.\n\nTo use this workflow you will need to set the credentials to use for the WooCommerce and Slack nodes, You will also need to pick a channel to post the message to.",
+ "workflow": {
+ "id": 81,
+ "name": "New WooCommerce order to Slack",
+ "nodes": [
+ {
+ "name": "Order Created",
+ "type": "n8n-nodes-base.wooCommerceTrigger",
+ "position": [
+ 340,
+ 500
+ ],
+ "webhookId": "287b4bf4-67ec-4c97-85d9-c0d3e6f59e6b",
+ "parameters": {
+ "event": "order.created"
+ },
+ "credentials": {
+ "wooCommerceApi": {
+ "id": "48",
+ "name": "WooCommerce account"
+ }
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "Send to Slack",
+ "type": "n8n-nodes-base.slack",
+ "position": [
+ 780,
+ 480
+ ],
+ "parameters": {
+ "text": ":sparkles: There is a new order :sparkles:",
+ "channel": "woo-commerce",
+ "blocksUi": {
+ "blocksValues": []
+ },
+ "attachments": [
+ {
+ "color": "#66FF00",
+ "fields": {
+ "item": [
+ {
+ "short": true,
+ "title": "Order ID",
+ "value": "={{$json[\"id\"]}}"
+ },
+ {
+ "short": true,
+ "title": "Status",
+ "value": "={{$json[\"status\"]}}"
+ },
+ {
+ "short": true,
+ "title": "Total",
+ "value": "={{$json[\"currency_symbol\"]}}{{$json[\"total\"]}}"
+ },
+ {
+ "short": false,
+ "title": "Link",
+ "value": "={{$node[\"Order Created\"].json[\"_links\"][\"self\"][0][\"href\"]}}"
+ }
+ ]
+ },
+ "footer": "=*Ordered:* {{$json[\"date_created\"]}} | *Transaction ID:* {{$json[\"transaction_id\"]}}"
+ }
+ ],
+ "otherOptions": {}
+ },
+ "credentials": {
+ "slackApi": {
+ "id": "53",
+ "name": "Slack Access Token"
+ }
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "Price over 100",
+ "type": "n8n-nodes-base.if",
+ "position": [
+ 540,
+ 500
+ ],
+ "parameters": {
+ "conditions": {
+ "number": [
+ {
+ "value1": "={{$json[\"total\"]}}",
+ "value2": 100,
+ "operation": "largerEqual"
+ }
+ ]
+ }
+ },
+ "typeVersion": 1
+ }
+ ],
+ "active": false,
+ "settings": {
+ "saveManualExecutions": true,
+ "saveExecutionProgress": true,
+ "saveDataSuccessExecution": "all"
+ },
+ "connections": {
+ "Order Created": {
+ "main": [
+ [
+ {
+ "node": "Price over 100",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Price over 100": {
+ "main": [
+ [
+ {
+ "node": "Send to Slack",
+ "type": "main",
+ "index": 0
+ }
+ ],
+ []
+ ]
+ }
+ }
+ },
+ "workflowInfo": {
+ "nodeCount": 4,
+ "nodeTypes": {
+ "n8n-nodes-base.if": {
+ "count": 1
+ },
+ "n8n-nodes-base.slack": {
+ "count": 1
+ },
+ "n8n-nodes-base.wooCommerceTrigger": {
+ "count": 1
+ }
+ }
+ },
+ "user": {
+ "username": "jon-n8n"
+ },
+ "nodes": [
+ {
+ "id": 20,
+ "icon": "fa:map-signs",
+ "name": "n8n-nodes-base.if",
+ "defaults": {
+ "name": "IF",
+ "color": "#408000"
+ },
+ "iconData": {
+ "icon": "map-signs",
+ "type": "icon"
+ },
+ "categories": [
+ {
+ "id": 9,
+ "name": "Core Nodes"
+ }
+ ],
+ "displayName": "IF",
+ "typeVersion": 1
+ },
+ {
+ "id": 40,
+ "icon": "file:slack.svg",
+ "name": "n8n-nodes-base.slack",
+ "defaults": {
+ "name": "Slack"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTUwLjg1MiAxNTAuODUyIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48dXNlIHhsaW5rOmhyZWY9IiNhIiB4PSIuOTI2IiB5PSIuOTI2Ii8+PHN5bWJvbCBpZD0iYSIgb3ZlcmZsb3c9InZpc2libGUiPjxnIHN0cm9rZS13aWR0aD0iMS44NTIiPjxwYXRoIGQ9Ik00MC43NDEgOTMuNTVjMC04LjczNSA2LjYwNy0xNS43NzIgMTQuODE1LTE1Ljc3MnMxNC44MTUgNy4wMzcgMTQuODE1IDE1Ljc3MnYzOC44MjRjMCA4LjczNy02LjYwNyAxNS43NzQtMTQuODE1IDE1Ljc3NHMtMTQuODE1LTcuMDM3LTE0LjgxNS0xNS43NzJ6IiBmaWxsPSIjZTAxZTVhIiBzdHJva2U9IiNlMDFlNWEiLz48cGF0aCBkPSJNOTMuNTUgMTA3LjQwOGMtOC43MzUgMC0xNS43NzItNi42MDctMTUuNzcyLTE0LjgxNXM3LjAzNy0xNC44MTUgMTUuNzcyLTE0LjgxNWgzOC44MjZjOC43MzUgMCAxNS43NzIgNi42MDcgMTUuNzcyIDE0LjgxNXMtNy4wMzcgMTQuODE1LTE1Ljc3MiAxNC44MTV6IiBmaWxsPSIjZWNiMjJkIiBzdHJva2U9IiNlY2IyMmQiLz48cGF0aCBkPSJNNzcuNzc4IDE1Ljc3MkM3Ny43NzggNy4wMzcgODQuMzg1IDAgOTIuNTkzIDBzMTQuODE1IDcuMDM3IDE0LjgxNSAxNS43NzJ2MzguODI2YzAgOC43MzUtNi42MDcgMTUuNzcyLTE0LjgxNSAxNS43NzJzLTE0LjgxNS03LjAzNy0xNC44MTUtMTUuNzcyeiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE1Ljc3MiA3MC4zNzFDNy4wMzcgNzAuMzcxIDAgNjMuNzYzIDAgNTUuNTU2czcuMDM3LTE0LjgxNSAxNS43NzItMTQuODE1aDM4LjgyNmM4LjczNSAwIDE1Ljc3MiA2LjYwNyAxNS43NzIgMTQuODE1cy03LjAzNyAxNC44MTUtMTUuNzcyIDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjxnIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiPjxwYXRoIGQ9Ik03Ny43NzggMTMzLjMzM2MwIDguMjA4IDYuNjA3IDE0LjgxNSAxNC44MTUgMTQuODE1czE0LjgxNS02LjYwNyAxNC44MTUtMTQuODE1LTYuNjA3LTE0LjgxNS0xNC44MTUtMTQuODE1SDc3Ljc3OHoiIGZpbGw9IiNlY2IyMmQiIHN0cm9rZT0iI2VjYjIyZCIvPjxwYXRoIGQ9Ik0xMzMuMzM0IDcwLjM3MWgtMTQuODE1VjU1LjU1NmMwLTguMjA3IDYuNjA3LTE0LjgxNSAxNC44MTUtMTQuODE1czE0LjgxNSA2LjYwNyAxNC44MTUgMTQuODE1LTYuNjA3IDE0LjgxNS0xNC44MTUgMTQuODE1eiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE0LjgxNSA3Ny43NzhIMjkuNjN2MTQuODE1YzAgOC4yMDctNi42MDcgMTQuODE1LTE0LjgxNSAxNC44MTVTMCAxMDAuOCAwIDkyLjU5M3M2LjYwNy0xNC44MTUgMTQuODE1LTE0LjgxNXoiIGZpbGw9IiNlMDFlNWEiIHN0cm9rZT0iI2UwMWU1YSIvPjxwYXRoIGQ9Ik03MC4zNzEgMTQuODE1VjI5LjYzSDU1LjU1NmMtOC4yMDcgMC0xNC44MTUtNi42MDctMTQuODE1LTE0LjgxNVM0Ny4zNDggMCA1NS41NTYgMHMxNC44MTUgNi42MDcgMTQuODE1IDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjwvZz48L2c+PC9zeW1ib2w+PC9zdmc+"
+ },
+ "categories": [
+ {
+ "id": 6,
+ "name": "Communication"
+ }
+ ],
+ "displayName": "Slack",
+ "typeVersion": 2
+ },
+ {
+ "id": 42,
+ "icon": "fa:play",
+ "name": "n8n-nodes-base.start",
+ "defaults": {
+ "name": "Start",
+ "color": "#00e000"
+ },
+ "iconData": {
+ "icon": "play",
+ "type": "icon"
+ },
+ "categories": [
+ {
+ "id": 9,
+ "name": "Core Nodes"
+ }
+ ],
+ "displayName": "Start",
+ "typeVersion": 1
+ },
+ {
+ "id": 235,
+ "icon": "file:wooCommerce.svg",
+ "name": "n8n-nodes-base.wooCommerceTrigger",
+ "defaults": {
+ "name": "WooCommerce Trigger"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="
+ },
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ }
+ ],
+ "displayName": "WooCommerce Trigger",
+ "typeVersion": 1
+ }
+ ],
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ }
+ ],
+ "image": []
+ },
+ {
+ "id": 1765,
+ "name": "Get Slack notifications when new product published on WooCommerce",
+ "views": 79,
+ "recentViews": 9577,
+ "totalViews": 79,
+ "createdAt": "2022-08-12T12:36:53.409Z",
+ "description": "This workflow let's a bot in Slack notify a specific channel when a new product in WooCommerce is published and live on the site. \n\n## Prerequisites\n\n[WooCommerce](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.woocommercetrigger/) account\n[Slack](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.slack/) and a [Slack bot](https://slack.com/help/articles/115005265703-Create-a-bot-for-your-workspace)\n\n## How it works\n\n1. Listen for WooCommerce product creation\n2. If permalink starts with https://[your-url-here].com/product/\n3. Slack bot notifies channel that a new product has been added. \n\nPlease note, you must update the URL in the IF node to match your url. If your WooCommerce doesn't use the slug /product/, that will need to be updated too. \n",
+ "workflow": {
+ "id": 1016,
+ "name": "Woocommerce to slack: notify new product created",
+ "tags": [
+ {
+ "id": "5",
+ "name": "FVF",
+ "createdAt": "2022-07-30T07:43:44.795Z",
+ "updatedAt": "2022-07-30T07:43:44.795Z"
+ }
+ ],
+ "nodes": [
+ {
+ "name": "If URL has /product/",
+ "type": "n8n-nodes-base.if",
+ "position": [
+ 640,
+ 300
+ ],
+ "parameters": {
+ "conditions": {
+ "string": [
+ {
+ "value1": "={{$json[\"permalink\"]}}",
+ "value2": "https://[add-your-url-here]/product/",
+ "operation": "startsWith"
+ }
+ ]
+ }
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "Send message to slack",
+ "type": "n8n-nodes-base.slack",
+ "position": [
+ 920,
+ 260
+ ],
+ "parameters": {
+ "text": ":new: A new product has been added! :new:",
+ "channel": "newproducts",
+ "blocksUi": {
+ "blocksValues": []
+ },
+ "attachments": [
+ {
+ "color": "#66FF00",
+ "fields": {
+ "item": [
+ {
+ "short": false,
+ "title": "Name",
+ "value": "={{$json[\"name\"]}}"
+ },
+ {
+ "short": true,
+ "title": "Price",
+ "value": "={{$json[\"regular_price\"]}}"
+ },
+ {
+ "short": true,
+ "title": "Sale Price",
+ "value": "={{$json[\"sale_price\"]}}"
+ },
+ {
+ "short": false,
+ "title": "Link",
+ "value": "={{$json[\"permalink\"]}}"
+ }
+ ]
+ },
+ "footer": "=Added: {{$json[\"date_created\"]}}"
+ }
+ ],
+ "otherOptions": {}
+ },
+ "credentials": {
+ "slackApi": {
+ "id": "21",
+ "name": "FVF bot"
+ }
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "On product creation",
+ "type": "n8n-nodes-base.wooCommerceTrigger",
+ "position": [
+ 460,
+ 300
+ ],
+ "webhookId": "267c4855-6227-4d33-867e-74600097473e",
+ "parameters": {
+ "event": "product.created"
+ },
+ "credentials": {
+ "wooCommerceApi": {
+ "id": "20",
+ "name": "WooCommerce account FVF"
+ }
+ },
+ "typeVersion": 1
+ }
+ ],
+ "active": true,
+ "settings": {},
+ "connections": {
+ "On product creation": {
+ "main": [
+ [
+ {
+ "node": "If URL has /product/",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "If URL has /product/": {
+ "main": [
+ [
+ {
+ "node": "Send message to slack",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ }
+ },
+ "workflowInfo": {
+ "nodeCount": 4,
+ "nodeTypes": {
+ "n8n-nodes-base.if": {
+ "count": 1
+ },
+ "n8n-nodes-base.slack": {
+ "count": 1
+ },
+ "n8n-nodes-base.wooCommerceTrigger": {
+ "count": 1
+ }
+ }
+ },
+ "user": {
+ "username": "n8n-team"
+ },
+ "nodes": [
+ {
+ "id": 20,
+ "icon": "fa:map-signs",
+ "name": "n8n-nodes-base.if",
+ "defaults": {
+ "name": "IF",
+ "color": "#408000"
+ },
+ "iconData": {
+ "icon": "map-signs",
+ "type": "icon"
+ },
+ "categories": [
+ {
+ "id": 9,
+ "name": "Core Nodes"
+ }
+ ],
+ "displayName": "IF",
+ "typeVersion": 1
+ },
+ {
+ "id": 40,
+ "icon": "file:slack.svg",
+ "name": "n8n-nodes-base.slack",
+ "defaults": {
+ "name": "Slack"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTUwLjg1MiAxNTAuODUyIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48dXNlIHhsaW5rOmhyZWY9IiNhIiB4PSIuOTI2IiB5PSIuOTI2Ii8+PHN5bWJvbCBpZD0iYSIgb3ZlcmZsb3c9InZpc2libGUiPjxnIHN0cm9rZS13aWR0aD0iMS44NTIiPjxwYXRoIGQ9Ik00MC43NDEgOTMuNTVjMC04LjczNSA2LjYwNy0xNS43NzIgMTQuODE1LTE1Ljc3MnMxNC44MTUgNy4wMzcgMTQuODE1IDE1Ljc3MnYzOC44MjRjMCA4LjczNy02LjYwNyAxNS43NzQtMTQuODE1IDE1Ljc3NHMtMTQuODE1LTcuMDM3LTE0LjgxNS0xNS43NzJ6IiBmaWxsPSIjZTAxZTVhIiBzdHJva2U9IiNlMDFlNWEiLz48cGF0aCBkPSJNOTMuNTUgMTA3LjQwOGMtOC43MzUgMC0xNS43NzItNi42MDctMTUuNzcyLTE0LjgxNXM3LjAzNy0xNC44MTUgMTUuNzcyLTE0LjgxNWgzOC44MjZjOC43MzUgMCAxNS43NzIgNi42MDcgMTUuNzcyIDE0LjgxNXMtNy4wMzcgMTQuODE1LTE1Ljc3MiAxNC44MTV6IiBmaWxsPSIjZWNiMjJkIiBzdHJva2U9IiNlY2IyMmQiLz48cGF0aCBkPSJNNzcuNzc4IDE1Ljc3MkM3Ny43NzggNy4wMzcgODQuMzg1IDAgOTIuNTkzIDBzMTQuODE1IDcuMDM3IDE0LjgxNSAxNS43NzJ2MzguODI2YzAgOC43MzUtNi42MDcgMTUuNzcyLTE0LjgxNSAxNS43NzJzLTE0LjgxNS03LjAzNy0xNC44MTUtMTUuNzcyeiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE1Ljc3MiA3MC4zNzFDNy4wMzcgNzAuMzcxIDAgNjMuNzYzIDAgNTUuNTU2czcuMDM3LTE0LjgxNSAxNS43NzItMTQuODE1aDM4LjgyNmM4LjczNSAwIDE1Ljc3MiA2LjYwNyAxNS43NzIgMTQuODE1cy03LjAzNyAxNC44MTUtMTUuNzcyIDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjxnIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiPjxwYXRoIGQ9Ik03Ny43NzggMTMzLjMzM2MwIDguMjA4IDYuNjA3IDE0LjgxNSAxNC44MTUgMTQuODE1czE0LjgxNS02LjYwNyAxNC44MTUtMTQuODE1LTYuNjA3LTE0LjgxNS0xNC44MTUtMTQuODE1SDc3Ljc3OHoiIGZpbGw9IiNlY2IyMmQiIHN0cm9rZT0iI2VjYjIyZCIvPjxwYXRoIGQ9Ik0xMzMuMzM0IDcwLjM3MWgtMTQuODE1VjU1LjU1NmMwLTguMjA3IDYuNjA3LTE0LjgxNSAxNC44MTUtMTQuODE1czE0LjgxNSA2LjYwNyAxNC44MTUgMTQuODE1LTYuNjA3IDE0LjgxNS0xNC44MTUgMTQuODE1eiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE0LjgxNSA3Ny43NzhIMjkuNjN2MTQuODE1YzAgOC4yMDctNi42MDcgMTQuODE1LTE0LjgxNSAxNC44MTVTMCAxMDAuOCAwIDkyLjU5M3M2LjYwNy0xNC44MTUgMTQuODE1LTE0LjgxNXoiIGZpbGw9IiNlMDFlNWEiIHN0cm9rZT0iI2UwMWU1YSIvPjxwYXRoIGQ9Ik03MC4zNzEgMTQuODE1VjI5LjYzSDU1LjU1NmMtOC4yMDcgMC0xNC44MTUtNi42MDctMTQuODE1LTE0LjgxNVM0Ny4zNDggMCA1NS41NTYgMHMxNC44MTUgNi42MDcgMTQuODE1IDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjwvZz48L2c+PC9zeW1ib2w+PC9zdmc+"
+ },
+ "categories": [
+ {
+ "id": 6,
+ "name": "Communication"
+ }
+ ],
+ "displayName": "Slack",
+ "typeVersion": 2
+ },
+ {
+ "id": 42,
+ "icon": "fa:play",
+ "name": "n8n-nodes-base.start",
+ "defaults": {
+ "name": "Start",
+ "color": "#00e000"
+ },
+ "iconData": {
+ "icon": "play",
+ "type": "icon"
+ },
+ "categories": [
+ {
+ "id": 9,
+ "name": "Core Nodes"
+ }
+ ],
+ "displayName": "Start",
+ "typeVersion": 1
+ },
+ {
+ "id": 235,
+ "icon": "file:wooCommerce.svg",
+ "name": "n8n-nodes-base.wooCommerceTrigger",
+ "defaults": {
+ "name": "WooCommerce Trigger"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="
+ },
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ }
+ ],
+ "displayName": "WooCommerce Trigger",
+ "typeVersion": 1
+ }
+ ],
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ }
+ ],
+ "image": []
+ },
+ {
+ "id": 1460,
+ "name": "Notify on Slack when refund is registered in WooCommerce",
+ "views": 85,
+ "recentViews": 9541,
+ "totalViews": 85,
+ "createdAt": "2022-02-17T15:02:58.662Z",
+ "description": "This workflow uses a WooCommerce trigger that will run when an order has been updated and the status is refunded.\n\nIf the value of this is over 100 it will post it to a Slack channel.\n\nTo use this workflow you will need to set the credentials to use for the WooCommerce and Slack nodes, You will also need to pick a channel to post the message to.",
+ "workflow": {
+ "id": 82,
+ "name": "New WooCommerce refund to Slack",
+ "nodes": [
+ {
+ "name": "Order Updated",
+ "type": "n8n-nodes-base.wooCommerceTrigger",
+ "position": [
+ 320,
+ 500
+ ],
+ "webhookId": "f7736be3-e978-4a17-b936-7ce9f8ccdb72",
+ "parameters": {
+ "event": "order.updated"
+ },
+ "credentials": {
+ "wooCommerceApi": {
+ "id": "48",
+ "name": "WooCommerce account"
+ }
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "If Refund and Over 100",
+ "type": "n8n-nodes-base.if",
+ "position": [
+ 540,
+ 500
+ ],
+ "parameters": {
+ "conditions": {
+ "number": [
+ {
+ "value1": "={{$json[\"total\"]}}",
+ "value2": 100,
+ "operation": "largerEqual"
+ }
+ ],
+ "string": [
+ {
+ "value1": "={{$json[\"status\"]}}",
+ "value2": "refunded"
+ }
+ ]
+ }
+ },
+ "typeVersion": 1
+ },
+ {
+ "name": "Send to Slack",
+ "type": "n8n-nodes-base.slack",
+ "position": [
+ 780,
+ 480
+ ],
+ "parameters": {
+ "text": ":x: A refund has been issued :x:",
+ "channel": "woo-commerce",
+ "blocksUi": {
+ "blocksValues": []
+ },
+ "attachments": [
+ {
+ "color": "#FF0000",
+ "fields": {
+ "item": [
+ {
+ "short": true,
+ "title": "Order ID",
+ "value": "={{$json[\"id\"]}}"
+ },
+ {
+ "short": true,
+ "title": "Status",
+ "value": "={{$json[\"status\"]}}"
+ },
+ {
+ "short": true,
+ "title": "Total",
+ "value": "={{$json[\"currency_symbol\"]}}{{$json[\"total\"]}}"
+ }
+ ]
+ },
+ "footer": "=*Order updated:* {{$json[\"date_modified\"]}}"
+ }
+ ],
+ "otherOptions": {}
+ },
+ "credentials": {
+ "slackApi": {
+ "id": "53",
+ "name": "Slack Access Token"
+ }
+ },
+ "typeVersion": 1
+ }
+ ],
+ "active": false,
+ "settings": {
+ "saveManualExecutions": true,
+ "saveExecutionProgress": true,
+ "saveDataSuccessExecution": "all"
+ },
+ "connections": {
+ "Order Updated": {
+ "main": [
+ [
+ {
+ "node": "If Refund and Over 100",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "If Refund and Over 100": {
+ "main": [
+ [
+ {
+ "node": "Send to Slack",
+ "type": "main",
+ "index": 0
+ }
+ ],
+ []
+ ]
+ }
+ }
+ },
+ "workflowInfo": {
+ "nodeCount": 4,
+ "nodeTypes": {
+ "n8n-nodes-base.if": {
+ "count": 1
+ },
+ "n8n-nodes-base.slack": {
+ "count": 1
+ },
+ "n8n-nodes-base.wooCommerceTrigger": {
+ "count": 1
+ }
+ }
+ },
+ "user": {
+ "username": "jon-n8n"
+ },
+ "nodes": [
+ {
+ "id": 20,
+ "icon": "fa:map-signs",
+ "name": "n8n-nodes-base.if",
+ "defaults": {
+ "name": "IF",
+ "color": "#408000"
+ },
+ "iconData": {
+ "icon": "map-signs",
+ "type": "icon"
+ },
+ "categories": [
+ {
+ "id": 9,
+ "name": "Core Nodes"
+ }
+ ],
+ "displayName": "IF",
+ "typeVersion": 1
+ },
+ {
+ "id": 40,
+ "icon": "file:slack.svg",
+ "name": "n8n-nodes-base.slack",
+ "defaults": {
+ "name": "Slack"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTUwLjg1MiAxNTAuODUyIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48dXNlIHhsaW5rOmhyZWY9IiNhIiB4PSIuOTI2IiB5PSIuOTI2Ii8+PHN5bWJvbCBpZD0iYSIgb3ZlcmZsb3c9InZpc2libGUiPjxnIHN0cm9rZS13aWR0aD0iMS44NTIiPjxwYXRoIGQ9Ik00MC43NDEgOTMuNTVjMC04LjczNSA2LjYwNy0xNS43NzIgMTQuODE1LTE1Ljc3MnMxNC44MTUgNy4wMzcgMTQuODE1IDE1Ljc3MnYzOC44MjRjMCA4LjczNy02LjYwNyAxNS43NzQtMTQuODE1IDE1Ljc3NHMtMTQuODE1LTcuMDM3LTE0LjgxNS0xNS43NzJ6IiBmaWxsPSIjZTAxZTVhIiBzdHJva2U9IiNlMDFlNWEiLz48cGF0aCBkPSJNOTMuNTUgMTA3LjQwOGMtOC43MzUgMC0xNS43NzItNi42MDctMTUuNzcyLTE0LjgxNXM3LjAzNy0xNC44MTUgMTUuNzcyLTE0LjgxNWgzOC44MjZjOC43MzUgMCAxNS43NzIgNi42MDcgMTUuNzcyIDE0LjgxNXMtNy4wMzcgMTQuODE1LTE1Ljc3MiAxNC44MTV6IiBmaWxsPSIjZWNiMjJkIiBzdHJva2U9IiNlY2IyMmQiLz48cGF0aCBkPSJNNzcuNzc4IDE1Ljc3MkM3Ny43NzggNy4wMzcgODQuMzg1IDAgOTIuNTkzIDBzMTQuODE1IDcuMDM3IDE0LjgxNSAxNS43NzJ2MzguODI2YzAgOC43MzUtNi42MDcgMTUuNzcyLTE0LjgxNSAxNS43NzJzLTE0LjgxNS03LjAzNy0xNC44MTUtMTUuNzcyeiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE1Ljc3MiA3MC4zNzFDNy4wMzcgNzAuMzcxIDAgNjMuNzYzIDAgNTUuNTU2czcuMDM3LTE0LjgxNSAxNS43NzItMTQuODE1aDM4LjgyNmM4LjczNSAwIDE1Ljc3MiA2LjYwNyAxNS43NzIgMTQuODE1cy03LjAzNyAxNC44MTUtMTUuNzcyIDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjxnIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiPjxwYXRoIGQ9Ik03Ny43NzggMTMzLjMzM2MwIDguMjA4IDYuNjA3IDE0LjgxNSAxNC44MTUgMTQuODE1czE0LjgxNS02LjYwNyAxNC44MTUtMTQuODE1LTYuNjA3LTE0LjgxNS0xNC44MTUtMTQuODE1SDc3Ljc3OHoiIGZpbGw9IiNlY2IyMmQiIHN0cm9rZT0iI2VjYjIyZCIvPjxwYXRoIGQ9Ik0xMzMuMzM0IDcwLjM3MWgtMTQuODE1VjU1LjU1NmMwLTguMjA3IDYuNjA3LTE0LjgxNSAxNC44MTUtMTQuODE1czE0LjgxNSA2LjYwNyAxNC44MTUgMTQuODE1LTYuNjA3IDE0LjgxNS0xNC44MTUgMTQuODE1eiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE0LjgxNSA3Ny43NzhIMjkuNjN2MTQuODE1YzAgOC4yMDctNi42MDcgMTQuODE1LTE0LjgxNSAxNC44MTVTMCAxMDAuOCAwIDkyLjU5M3M2LjYwNy0xNC44MTUgMTQuODE1LTE0LjgxNXoiIGZpbGw9IiNlMDFlNWEiIHN0cm9rZT0iI2UwMWU1YSIvPjxwYXRoIGQ9Ik03MC4zNzEgMTQuODE1VjI5LjYzSDU1LjU1NmMtOC4yMDcgMC0xNC44MTUtNi42MDctMTQuODE1LTE0LjgxNVM0Ny4zNDggMCA1NS41NTYgMHMxNC44MTUgNi42MDcgMTQuODE1IDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjwvZz48L2c+PC9zeW1ib2w+PC9zdmc+"
+ },
+ "categories": [
+ {
+ "id": 6,
+ "name": "Communication"
+ }
+ ],
+ "displayName": "Slack",
+ "typeVersion": 2
+ },
+ {
+ "id": 42,
+ "icon": "fa:play",
+ "name": "n8n-nodes-base.start",
+ "defaults": {
+ "name": "Start",
+ "color": "#00e000"
+ },
+ "iconData": {
+ "icon": "play",
+ "type": "icon"
+ },
+ "categories": [
+ {
+ "id": 9,
+ "name": "Core Nodes"
+ }
+ ],
+ "displayName": "Start",
+ "typeVersion": 1
+ },
+ {
+ "id": 235,
+ "icon": "file:wooCommerce.svg",
+ "name": "n8n-nodes-base.wooCommerceTrigger",
+ "defaults": {
+ "name": "WooCommerce Trigger"
+ },
+ "iconData": {
+ "type": "file",
+ "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="
+ },
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ }
+ ],
+ "displayName": "WooCommerce Trigger",
+ "typeVersion": 1
+ }
+ ],
+ "categories": [
+ {
+ "id": 2,
+ "name": "Sales"
+ },
+ {
+ "id": 8,
+ "name": "Finance & Accounting"
+ }
+ ],
+ "image": []
+ }
+ ],
+ "image": []
+ }
+}
diff --git a/cypress/fixtures/Subworkflow-debugging-execute-workflow.json b/cypress/fixtures/Subworkflow-debugging-execute-workflow.json
new file mode 100644
index 00000000000..c336a80b419
--- /dev/null
+++ b/cypress/fixtures/Subworkflow-debugging-execute-workflow.json
@@ -0,0 +1,354 @@
+{
+ "meta": {
+ "instanceId": "08ce71ad998aeaade0abedb8dd96153d8eaa03fcb84cfccc1530095bf9ee478e"
+ },
+ "nodes": [
+ {
+ "parameters": {},
+ "id": "4535ce3e-280e-49b0-8854-373472ec86d1",
+ "name": "When clicking ‘Test workflow’",
+ "type": "n8n-nodes-base.manualTrigger",
+ "typeVersion": 1,
+ "position": [80, 860]
+ },
+ {
+ "parameters": {
+ "category": "randomData",
+ "randomDataSeed": "0",
+ "randomDataCount": 2
+ },
+ "id": "d7fba18a-d51f-4509-af45-68cd9425ac6b",
+ "name": "DebugHelper1",
+ "type": "n8n-nodes-base.debugHelper",
+ "typeVersion": 1,
+ "position": [280, 860]
+ },
+ {
+ "parameters": {
+ "source": "parameter",
+ "workflowJson": "{\n \"meta\": {\n \"instanceId\": \"a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0\"\n },\n \"nodes\": [\n {\n \"parameters\": {},\n \"type\": \"n8n-nodes-base.executeWorkflowTrigger\",\n \"typeVersion\": 1,\n \"position\": [\n 0,\n 0\n ],\n \"id\": \"00600a51-e63a-4b6e-93f5-f01d50a21e0c\",\n \"name\": \"Execute Workflow Trigger\"\n },\n {\n \"parameters\": {\n \"assignments\": {\n \"assignments\": [\n {\n \"id\": \"87ff01af-2e28-48da-ae6c-304040200b15\",\n \"name\": \"hello\",\n \"value\": \"=world {{ $json.firstname }} {{ $json.lastname }}\",\n \"type\": \"string\"\n }\n ]\n },\n \"includeOtherFields\": false,\n \"options\": {}\n },\n \"type\": \"n8n-nodes-base.set\",\n \"typeVersion\": 3.4,\n \"position\": [\n 280,\n 0\n ],\n \"id\": \"642219a1-d655-4a30-af5c-fcccbb690322\",\n \"name\": \"Edit Fields\"\n }\n ],\n \"connections\": {\n \"Execute Workflow Trigger\": {\n \"main\": [\n [\n {\n \"node\": \"Edit Fields\",\n \"type\": \"main\",\n \"index\": 0\n }\n ]\n ]\n }\n },\n \"pinData\": {}\n}",
+ "mode": "each",
+ "options": {
+ "waitForSubWorkflow": false
+ }
+ },
+ "type": "n8n-nodes-base.executeWorkflow",
+ "typeVersion": 1.1,
+ "position": [680, 1540],
+ "id": "f90a25da-dd89-4bf8-8f5b-bf8ee1de0b70",
+ "name": "Execute Workflow with param3"
+ },
+ {
+ "parameters": {
+ "assignments": {
+ "assignments": [
+ {
+ "id": "c93f26bd-3489-467b-909e-6462e1463707",
+ "name": "uid",
+ "value": "={{ $json.uid }}",
+ "type": "string"
+ },
+ {
+ "id": "3dd706ce-d925-4219-8531-ad12369972fe",
+ "name": "email",
+ "value": "={{ $json.email }}",
+ "type": "string"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.4,
+ "position": [900, 1540],
+ "id": "3be57648-3be8-4b0f-abfa-8fdcafee804d",
+ "name": "Edit Fields8"
+ },
+ {
+ "parameters": {
+ "source": "parameter",
+ "workflowJson": "{\n \"meta\": {\n \"instanceId\": \"a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0\"\n },\n \"nodes\": [\n {\n \"parameters\": {},\n \"type\": \"n8n-nodes-base.executeWorkflowTrigger\",\n \"typeVersion\": 1,\n \"position\": [\n 0,\n 0\n ],\n \"id\": \"00600a51-e63a-4b6e-93f5-f01d50a21e0c\",\n \"name\": \"Execute Workflow Trigger\"\n },\n {\n \"parameters\": {\n \"assignments\": {\n \"assignments\": [\n {\n \"id\": \"87ff01af-2e28-48da-ae6c-304040200b15\",\n \"name\": \"hello\",\n \"value\": \"=world {{ $json.firstname }} {{ $json.lastname }}\",\n \"type\": \"string\"\n }\n ]\n },\n \"includeOtherFields\": false,\n \"options\": {}\n },\n \"type\": \"n8n-nodes-base.set\",\n \"typeVersion\": 3.4,\n \"position\": [\n 280,\n 0\n ],\n \"id\": \"642219a1-d655-4a30-af5c-fcccbb690322\",\n \"name\": \"Edit Fields\"\n }\n ],\n \"connections\": {\n \"Execute Workflow Trigger\": {\n \"main\": [\n [\n {\n \"node\": \"Edit Fields\",\n \"type\": \"main\",\n \"index\": 0\n }\n ]\n ]\n }\n },\n \"pinData\": {}\n}",
+ "options": {
+ "waitForSubWorkflow": false
+ }
+ },
+ "type": "n8n-nodes-base.executeWorkflow",
+ "typeVersion": 1.1,
+ "position": [620, 1220],
+ "id": "dabc2356-3660-4d17-b305-936a002029ba",
+ "name": "Execute Workflow with param2"
+ },
+ {
+ "parameters": {
+ "assignments": {
+ "assignments": [
+ {
+ "id": "c93f26bd-3489-467b-909e-6462e1463707",
+ "name": "uid",
+ "value": "={{ $json.uid }}",
+ "type": "string"
+ },
+ {
+ "id": "3dd706ce-d925-4219-8531-ad12369972fe",
+ "name": "email",
+ "value": "={{ $json.email }}",
+ "type": "string"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.4,
+ "position": [840, 1220],
+ "id": "9d2a9dda-e2a1-43e8-a66f-a8a555692e5f",
+ "name": "Edit Fields7"
+ },
+ {
+ "parameters": {
+ "source": "parameter",
+ "workflowJson": "{\n \"meta\": {\n \"instanceId\": \"a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0\"\n },\n \"nodes\": [\n {\n \"parameters\": {},\n \"type\": \"n8n-nodes-base.executeWorkflowTrigger\",\n \"typeVersion\": 1,\n \"position\": [\n 0,\n 0\n ],\n \"id\": \"00600a51-e63a-4b6e-93f5-f01d50a21e0c\",\n \"name\": \"Execute Workflow Trigger\"\n },\n {\n \"parameters\": {\n \"assignments\": {\n \"assignments\": [\n {\n \"id\": \"87ff01af-2e28-48da-ae6c-304040200b15\",\n \"name\": \"hello\",\n \"value\": \"=world {{ $json.firstname }} {{ $json.lastname }}\",\n \"type\": \"string\"\n }\n ]\n },\n \"includeOtherFields\": false,\n \"options\": {}\n },\n \"type\": \"n8n-nodes-base.set\",\n \"typeVersion\": 3.4,\n \"position\": [\n 280,\n 0\n ],\n \"id\": \"642219a1-d655-4a30-af5c-fcccbb690322\",\n \"name\": \"Edit Fields\"\n }\n ],\n \"connections\": {\n \"Execute Workflow Trigger\": {\n \"main\": [\n [\n {\n \"node\": \"Edit Fields\",\n \"type\": \"main\",\n \"index\": 0\n }\n ]\n ]\n }\n },\n \"pinData\": {}\n}",
+ "mode": "each",
+ "options": {
+ "waitForSubWorkflow": true
+ }
+ },
+ "type": "n8n-nodes-base.executeWorkflow",
+ "typeVersion": 1.1,
+ "position": [560, 900],
+ "id": "07e47f60-622a-484c-ab24-35f6f2280595",
+ "name": "Execute Workflow with param1"
+ },
+ {
+ "parameters": {
+ "assignments": {
+ "assignments": [
+ {
+ "id": "c93f26bd-3489-467b-909e-6462e1463707",
+ "name": "uid",
+ "value": "={{ $json.uid }}",
+ "type": "string"
+ },
+ {
+ "id": "3dd706ce-d925-4219-8531-ad12369972fe",
+ "name": "email",
+ "value": "={{ $json.email }}",
+ "type": "string"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.4,
+ "position": [760, 900],
+ "id": "80563d0a-0bab-444f-a04c-4041a505d78b",
+ "name": "Edit Fields6"
+ },
+ {
+ "parameters": {
+ "source": "parameter",
+ "workflowJson": "{\n \"meta\": {\n \"instanceId\": \"a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0\"\n },\n \"nodes\": [\n {\n \"parameters\": {},\n \"type\": \"n8n-nodes-base.executeWorkflowTrigger\",\n \"typeVersion\": 1,\n \"position\": [\n 0,\n 0\n ],\n \"id\": \"00600a51-e63a-4b6e-93f5-f01d50a21e0c\",\n \"name\": \"Execute Workflow Trigger\"\n },\n {\n \"parameters\": {\n \"assignments\": {\n \"assignments\": [\n {\n \"id\": \"87ff01af-2e28-48da-ae6c-304040200b15\",\n \"name\": \"hello\",\n \"value\": \"=world {{ $json.firstname }} {{ $json.lastname }}\",\n \"type\": \"string\"\n }\n ]\n },\n \"includeOtherFields\": false,\n \"options\": {}\n },\n \"type\": \"n8n-nodes-base.set\",\n \"typeVersion\": 3.4,\n \"position\": [\n 280,\n 0\n ],\n \"id\": \"642219a1-d655-4a30-af5c-fcccbb690322\",\n \"name\": \"Edit Fields\"\n }\n ],\n \"connections\": {\n \"Execute Workflow Trigger\": {\n \"main\": [\n [\n {\n \"node\": \"Edit Fields\",\n \"type\": \"main\",\n \"index\": 0\n }\n ]\n ]\n }\n },\n \"pinData\": {}\n}",
+ "options": {
+ "waitForSubWorkflow": true
+ }
+ },
+ "type": "n8n-nodes-base.executeWorkflow",
+ "typeVersion": 1.1,
+ "position": [560, 580],
+ "id": "f04af481-f4d9-4d91-a60a-a377580e8393",
+ "name": "Execute Workflow with param"
+ },
+ {
+ "parameters": {
+ "assignments": {
+ "assignments": [
+ {
+ "id": "c93f26bd-3489-467b-909e-6462e1463707",
+ "name": "uid",
+ "value": "={{ $json.uid }}",
+ "type": "string"
+ },
+ {
+ "id": "3dd706ce-d925-4219-8531-ad12369972fe",
+ "name": "email",
+ "value": "={{ $json.email }}",
+ "type": "string"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.4,
+ "position": [760, 580],
+ "id": "80c10607-a0ac-4090-86a1-890da0a2aa52",
+ "name": "Edit Fields2"
+ },
+ {
+ "parameters": {
+ "content": "## Execute Workflow (Run once with all items/ DONT Wait for Sub-workflow completion)",
+ "height": 254.84308966329985,
+ "width": 457.58120569815793
+ },
+ "id": "534ef523-3453-4a16-9ff0-8ac9f025d47d",
+ "name": "Sticky Note5",
+ "type": "n8n-nodes-base.stickyNote",
+ "typeVersion": 1,
+ "position": [500, 1080]
+ },
+ {
+ "parameters": {
+ "content": "## Execute Workflow (Run once with for each item/ DONT Wait for Sub-workflow completion) ",
+ "height": 284.59778445962905,
+ "width": 457.58120569815793
+ },
+ "id": "838f0fa3-5ee4-4d1a-afb8-42e009f1aa9e",
+ "name": "Sticky Note4",
+ "type": "n8n-nodes-base.stickyNote",
+ "typeVersion": 1,
+ "position": [580, 1400]
+ },
+ {
+ "parameters": {
+ "category": "randomData",
+ "randomDataSeed": "1",
+ "randomDataCount": 3
+ },
+ "id": "86699a49-2aa7-488e-8ea9-828404c98f08",
+ "name": "DebugHelper",
+ "type": "n8n-nodes-base.debugHelper",
+ "typeVersion": 1,
+ "position": [320, 1120]
+ },
+ {
+ "parameters": {
+ "content": "## Execute Workflow (Run once with for each item/ Wait for Sub-workflow completion) ",
+ "height": 284.59778445962905,
+ "width": 457.58120569815793
+ },
+ "id": "885d35f0-8ae6-45ec-821b-a82c27e7577a",
+ "name": "Sticky Note3",
+ "type": "n8n-nodes-base.stickyNote",
+ "typeVersion": 1,
+ "position": [480, 760]
+ },
+ {
+ "parameters": {
+ "content": "## Execute Workflow (Run once with all items/ Wait for Sub-workflow completion) (default behavior)",
+ "height": 254.84308966329985,
+ "width": 457.58120569815793
+ },
+ "id": "505bd7f2-767e-41b8-9325-77300aed5883",
+ "name": "Sticky Note2",
+ "type": "n8n-nodes-base.stickyNote",
+ "typeVersion": 1,
+ "position": [460, 460]
+ }
+ ],
+ "connections": {
+ "When clicking ‘Test workflow’": {
+ "main": [
+ [
+ {
+ "node": "DebugHelper1",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "DebugHelper",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "DebugHelper1": {
+ "main": [
+ [
+ {
+ "node": "Execute Workflow with param3",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Execute Workflow with param2",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Execute Workflow with param1",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Execute Workflow with param",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Execute Workflow with param3": {
+ "main": [
+ [
+ {
+ "node": "Edit Fields8",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Execute Workflow with param2": {
+ "main": [
+ [
+ {
+ "node": "Edit Fields7",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Execute Workflow with param1": {
+ "main": [
+ [
+ {
+ "node": "Edit Fields6",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Execute Workflow with param": {
+ "main": [
+ [
+ {
+ "node": "Edit Fields2",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "DebugHelper": {
+ "main": [
+ [
+ {
+ "node": "Execute Workflow with param2",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Execute Workflow with param3",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ },
+ "pinData": {}
+}
diff --git a/cypress/fixtures/Test_Template_1.json b/cypress/fixtures/Test_Template_1.json
index f15970677e1..1995beca529 100644
--- a/cypress/fixtures/Test_Template_1.json
+++ b/cypress/fixtures/Test_Template_1.json
@@ -1,7 +1,7 @@
{
"workflow": {
"id": 1205,
- "name": "Promote new Shopify products on Twitter and Telegram",
+ "name": "Promote new Shopify products",
"views": 478,
"recentViews": 9880,
"totalViews": 478,
diff --git a/cypress/fixtures/templates_search/sales_templates_search_response.json b/cypress/fixtures/templates_search/sales_templates_search_response.json
index 4efbb3585b5..d4f90991b31 100644
--- a/cypress/fixtures/templates_search/sales_templates_search_response.json
+++ b/cypress/fixtures/templates_search/sales_templates_search_response.json
@@ -1202,7 +1202,7 @@
},
{
"id": 1205,
- "name": "Promote New Shopify Products on Social Media (Twitter and Telegram)",
+ "name": "Promote New Shopify Products",
"totalViews": 219,
"recentViews": 0,
"user": {
diff --git a/cypress/package.json b/cypress/package.json
index 02e2a74c032..832abd9ef58 100644
--- a/cypress/package.json
+++ b/cypress/package.json
@@ -26,6 +26,7 @@
"cypress": "^13.14.2",
"cypress-otp": "^1.0.3",
"cypress-real-events": "^1.13.0",
+ "flatted": "catalog:",
"lodash": "catalog:",
"nanoid": "catalog:",
"start-server-and-test": "^2.0.8"
diff --git a/cypress/pages/ndv.ts b/cypress/pages/ndv.ts
index 4504552e262..516a0a1ea86 100644
--- a/cypress/pages/ndv.ts
+++ b/cypress/pages/ndv.ts
@@ -323,6 +323,12 @@ export class NDV extends BasePage {
addItemToFixedCollection: (paramName: string) => {
this.getters.fixedCollectionParameter(paramName).getByTestId('fixed-collection-add').click();
},
+ dragMainPanelToLeft: () => {
+ cy.drag('[data-test-id=panel-drag-button]', [-1000, 0], { moveTwice: true });
+ },
+ dragMainPanelToRight: () => {
+ cy.drag('[data-test-id=panel-drag-button]', [1000, 0], { moveTwice: true });
+ },
};
}
diff --git a/cypress/pages/workflow.ts b/cypress/pages/workflow.ts
index cd1e7d94623..ee90fa55e80 100644
--- a/cypress/pages/workflow.ts
+++ b/cypress/pages/workflow.ts
@@ -17,7 +17,8 @@ export class WorkflowPage extends BasePage {
workflowTagsContainer: () => cy.getByTestId('workflow-tags-container'),
workflowTagsInput: () =>
this.getters.workflowTagsContainer().then(($el) => cy.wrap($el.find('input').first())),
- tagPills: () => cy.get('[data-test-id="workflow-tags-container"] span.el-tag'),
+ tagPills: () =>
+ cy.get('[data-test-id="workflow-tags-container"] span.el-tag:not(.count-container)'),
nthTagPill: (n: number) =>
cy.get(`[data-test-id="workflow-tags-container"] span.el-tag:nth-child(${n})`),
tagsDropdown: () => cy.getByTestId('workflow-tags-dropdown'),
diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts
index 6cad68b34fd..bc5a18a34f6 100644
--- a/cypress/support/commands.ts
+++ b/cypress/support/commands.ts
@@ -177,6 +177,16 @@ Cypress.Commands.add('drag', (selector, pos, options) => {
pageY: newPosition.y,
force: true,
});
+ if (options?.moveTwice) {
+ // first move like hover to trigger object to be visible
+ // like in main panel in ndv
+ element.trigger('mousemove', {
+ which: 1,
+ pageX: newPosition.x,
+ pageY: newPosition.y,
+ force: true,
+ });
+ }
if (options?.clickToFinish) {
// Click to finish the drag
// For some reason, mouseup isn't working when moving nodes
diff --git a/cypress/support/index.ts b/cypress/support/index.ts
index 2fd1faeb22a..864e64f1c4d 100644
--- a/cypress/support/index.ts
+++ b/cypress/support/index.ts
@@ -1,7 +1,7 @@
// Load type definitions that come with Cypress module
///
-import type { FrontendSettings } from '@n8n/api-types';
+import type { FrontendSettings, PushPayload, PushType } from '@n8n/api-types';
Cypress.Keyboard.defaults({
keystrokeDelay: 0,
@@ -59,14 +59,20 @@ declare global {
drag(
selector: string | Chainable>,
target: [number, number],
- options?: { abs?: boolean; index?: number; realMouse?: boolean; clickToFinish?: boolean },
+ options?: {
+ abs?: boolean;
+ index?: number;
+ realMouse?: boolean;
+ clickToFinish?: boolean;
+ moveTwice?: boolean;
+ },
): void;
draganddrop(
draggableSelector: string,
droppableSelector: string,
options?: Partial,
): void;
- push(type: string, data: unknown): void;
+ push(type: Type, data: PushPayload): void;
shouldNotHaveConsoleErrors(): void;
window(): Chainable<
AUTWindow & {
diff --git a/cypress/utils/executions.ts b/cypress/utils/executions.ts
index 0f429728560..12f4d2454a4 100644
--- a/cypress/utils/executions.ts
+++ b/cypress/utils/executions.ts
@@ -1,5 +1,5 @@
+import { stringify } from 'flatted';
import type { IDataObject, IPinData, ITaskData, ITaskDataConnections } from 'n8n-workflow';
-import { nanoid } from 'nanoid';
import { clickExecuteWorkflowButton } from '../composables/workflow';
@@ -39,41 +39,35 @@ export function createMockNodeExecutionData(
};
}
-export function createMockWorkflowExecutionData({
- executionId,
+function createMockWorkflowExecutionData({
runData,
- pinData = {},
lastNodeExecuted,
}: {
- executionId: string;
runData: Record;
pinData?: IPinData;
lastNodeExecuted: string;
}) {
return {
- executionId,
- data: {
- data: {
- startData: {},
- resultData: {
- runData,
- pinData,
- lastNodeExecuted,
- },
- executionData: {
- contextData: {},
- nodeExecutionStack: [],
- metadata: {},
- waitingExecution: {},
- waitingExecutionSource: {},
- },
+ data: stringify({
+ startData: {},
+ resultData: {
+ runData,
+ pinData: {},
+ lastNodeExecuted,
},
- mode: 'manual',
- startedAt: new Date().toISOString(),
- stoppedAt: new Date().toISOString(),
- status: 'success',
- finished: true,
- },
+ executionData: {
+ contextData: {},
+ nodeExecutionStack: [],
+ metadata: {},
+ waitingExecution: {},
+ waitingExecutionSource: {},
+ },
+ }),
+ mode: 'manual',
+ startedAt: new Date().toISOString(),
+ stoppedAt: new Date().toISOString(),
+ status: 'success',
+ finished: true,
};
}
@@ -81,14 +75,12 @@ export function runMockWorkflowExecution({
trigger,
lastNodeExecuted,
runData,
- workflowExecutionData,
}: {
trigger?: () => void;
lastNodeExecuted: string;
runData: Array>;
- workflowExecutionData?: ReturnType;
}) {
- const executionId = nanoid(8);
+ const executionId = Math.floor(Math.random() * 1_000_000).toString();
cy.intercept('POST', '/rest/workflows/**/run?**', {
statusCode: 201,
@@ -125,13 +117,17 @@ export function runMockWorkflowExecution({
resolvedRunData[nodeName] = nodeExecution[nodeName];
});
- cy.push(
- 'executionFinished',
- createMockWorkflowExecutionData({
- executionId,
- lastNodeExecuted,
- runData: resolvedRunData,
- ...workflowExecutionData,
- }),
- );
+ cy.intercept('GET', `/rest/executions/${executionId}`, {
+ statusCode: 200,
+ body: {
+ data: createMockWorkflowExecutionData({
+ lastNodeExecuted,
+ runData: resolvedRunData,
+ }),
+ },
+ }).as('getExecution');
+
+ cy.push('executionFinished', { executionId });
+
+ cy.wait('@getExecution');
}
diff --git a/docker/images/n8n/n8n-task-runners.json b/docker/images/n8n/n8n-task-runners.json
index 699794d5041..9eab58d91b3 100644
--- a/docker/images/n8n/n8n-task-runners.json
+++ b/docker/images/n8n/n8n-task-runners.json
@@ -13,7 +13,11 @@
"N8N_RUNNERS_MAX_CONCURRENCY",
"NODE_FUNCTION_ALLOW_BUILTIN",
"NODE_FUNCTION_ALLOW_EXTERNAL",
- "NODE_OPTIONS"
+ "NODE_OPTIONS",
+ "N8N_SENTRY_DSN",
+ "N8N_VERSION",
+ "ENVIRONMENT",
+ "DEPLOYMENT_NAME"
],
"uid": 2000,
"gid": 2000
diff --git a/packages/@n8n/api-types/src/frontend-settings.ts b/packages/@n8n/api-types/src/frontend-settings.ts
index 6b2f3231d3d..54b79568219 100644
--- a/packages/@n8n/api-types/src/frontend-settings.ts
+++ b/packages/@n8n/api-types/src/frontend-settings.ts
@@ -27,7 +27,8 @@ export interface IUserManagementSettings {
}
export interface FrontendSettings {
- isDocker?: boolean;
+ inE2ETests: boolean;
+ isDocker: boolean;
databaseType: 'sqlite' | 'mariadb' | 'mysqldb' | 'postgresdb';
endpointForm: string;
endpointFormTest: string;
diff --git a/packages/@n8n/api-types/src/push/execution.ts b/packages/@n8n/api-types/src/push/execution.ts
index 78c0b34a363..9c723e2817f 100644
--- a/packages/@n8n/api-types/src/push/execution.ts
+++ b/packages/@n8n/api-types/src/push/execution.ts
@@ -1,4 +1,4 @@
-import type { IRun, ITaskData, WorkflowExecuteMode } from 'n8n-workflow';
+import type { ITaskData, WorkflowExecuteMode } from 'n8n-workflow';
type ExecutionStarted = {
type: 'executionStarted';
@@ -12,12 +12,17 @@ type ExecutionStarted = {
};
};
+type ExecutionWaiting = {
+ type: 'executionWaiting';
+ data: {
+ executionId: string;
+ };
+};
+
type ExecutionFinished = {
type: 'executionFinished';
data: {
executionId: string;
- data: IRun;
- retryOf?: string;
};
};
@@ -47,6 +52,7 @@ type NodeExecuteAfter = {
export type ExecutionPushMessage =
| ExecutionStarted
+ | ExecutionWaiting
| ExecutionFinished
| ExecutionRecovered
| NodeExecuteBefore
diff --git a/packages/@n8n/config/src/configs/diagnostics.config.ts b/packages/@n8n/config/src/configs/diagnostics.config.ts
new file mode 100644
index 00000000000..58e4740b351
--- /dev/null
+++ b/packages/@n8n/config/src/configs/diagnostics.config.ts
@@ -0,0 +1,30 @@
+import { Config, Env, Nested } from '../decorators';
+
+@Config
+class PostHogConfig {
+ /** API key for PostHog. */
+ @Env('N8N_DIAGNOSTICS_POSTHOG_API_KEY')
+ apiKey: string = 'phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo';
+
+ /** API host for PostHog. */
+ @Env('N8N_DIAGNOSTICS_POSTHOG_API_HOST')
+ apiHost: string = 'https://ph.n8n.io';
+}
+
+@Config
+export class DiagnosticsConfig {
+ /** Whether diagnostics are enabled. */
+ @Env('N8N_DIAGNOSTICS_ENABLED')
+ enabled: boolean = false;
+
+ /** Diagnostics config for frontend. */
+ @Env('N8N_DIAGNOSTICS_CONFIG_FRONTEND')
+ frontendConfig: string = '1zPn9bgWPzlQc0p8Gj1uiK6DOTn;https://telemetry.n8n.io';
+
+ /** Diagnostics config for backend. */
+ @Env('N8N_DIAGNOSTICS_CONFIG_BACKEND')
+ backendConfig: string = '1zPn7YoGC3ZXE9zLeTKLuQCB4F6;https://telemetry.n8n.io';
+
+ @Nested
+ posthogConfig: PostHogConfig;
+}
diff --git a/packages/@n8n/config/src/configs/runners.config.ts b/packages/@n8n/config/src/configs/runners.config.ts
index 5a6969ba6f2..b7d125cf535 100644
--- a/packages/@n8n/config/src/configs/runners.config.ts
+++ b/packages/@n8n/config/src/configs/runners.config.ts
@@ -53,4 +53,12 @@ export class TaskRunnersConfig {
/** Should the output of deduplication be asserted for correctness */
@Env('N8N_RUNNERS_ASSERT_DEDUPLICATION_OUTPUT')
assertDeduplicationOutput: boolean = false;
+
+ /** How long (in seconds) a task is allowed to take for completion, else the task will be aborted and the runner restarted. Must be greater than 0. */
+ @Env('N8N_RUNNERS_TASK_TIMEOUT')
+ taskTimeout: number = 60;
+
+ /** How often (in seconds) the runner must send a heartbeat to the broker, else the task will be aborted and the runner restarted. Must be greater than 0. */
+ @Env('N8N_RUNNERS_HEARTBEAT_INTERVAL')
+ heartbeatInterval: number = 30;
}
diff --git a/packages/@n8n/config/src/index.ts b/packages/@n8n/config/src/index.ts
index 0a89535ee32..a1c0a1f43be 100644
--- a/packages/@n8n/config/src/index.ts
+++ b/packages/@n8n/config/src/index.ts
@@ -1,6 +1,7 @@
import { CacheConfig } from './configs/cache.config';
import { CredentialsConfig } from './configs/credentials.config';
import { DatabaseConfig } from './configs/database.config';
+import { DiagnosticsConfig } from './configs/diagnostics.config';
import { EndpointsConfig } from './configs/endpoints.config';
import { EventBusConfig } from './configs/event-bus.config';
import { ExternalSecretsConfig } from './configs/external-secrets.config';
@@ -117,4 +118,7 @@ export class GlobalConfig {
@Nested
pruning: PruningConfig;
+
+ @Nested
+ diagnostics: DiagnosticsConfig;
}
diff --git a/packages/@n8n/config/test/config.test.ts b/packages/@n8n/config/test/config.test.ts
index eeb98269ded..c60431e97a9 100644
--- a/packages/@n8n/config/test/config.test.ts
+++ b/packages/@n8n/config/test/config.test.ts
@@ -234,6 +234,8 @@ describe('GlobalConfig', () => {
maxOldSpaceSize: '',
maxConcurrency: 5,
assertDeduplicationOutput: false,
+ taskTimeout: 60,
+ heartbeatInterval: 30,
},
sentry: {
backendDsn: '',
@@ -280,6 +282,15 @@ describe('GlobalConfig', () => {
hardDeleteInterval: 15,
softDeleteInterval: 60,
},
+ diagnostics: {
+ enabled: false,
+ frontendConfig: '1zPn9bgWPzlQc0p8Gj1uiK6DOTn;https://telemetry.n8n.io',
+ backendConfig: '1zPn7YoGC3ZXE9zLeTKLuQCB4F6;https://telemetry.n8n.io',
+ posthogConfig: {
+ apiKey: 'phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo',
+ apiHost: 'https://ph.n8n.io',
+ },
+ },
};
it('should use all default values when no env variables are defined', () => {
diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.ts b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.ts
index a75a93c9f40..8c178543fcb 100644
--- a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.ts
+++ b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.ts
@@ -87,6 +87,36 @@ export class EmbeddingsAzureOpenAi implements INodeType {
'Maximum amount of time a request is allowed to take in seconds. Set to -1 for no timeout.',
type: 'number',
},
+ {
+ displayName: 'Dimensions',
+ name: 'dimensions',
+ default: undefined,
+ description:
+ 'The number of dimensions the resulting output embeddings should have. Only supported in text-embedding-3 and later models.',
+ type: 'options',
+ options: [
+ {
+ name: '256',
+ value: 256,
+ },
+ {
+ name: '512',
+ value: 512,
+ },
+ {
+ name: '1024',
+ value: 1024,
+ },
+ {
+ name: '1536',
+ value: 1536,
+ },
+ {
+ name: '3072',
+ value: 3072,
+ },
+ ],
+ },
],
},
],
@@ -105,6 +135,7 @@ export class EmbeddingsAzureOpenAi implements INodeType {
batchSize?: number;
stripNewLines?: boolean;
timeout?: number;
+ dimensions?: number | undefined;
};
if (options.timeout === -1) {
diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsOpenAI/EmbeddingsOpenAi.node.ts b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsOpenAI/EmbeddingsOpenAi.node.ts
index 167581ed2ec..aececc09aed 100644
--- a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsOpenAI/EmbeddingsOpenAi.node.ts
+++ b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsOpenAI/EmbeddingsOpenAi.node.ts
@@ -135,6 +135,36 @@ export class EmbeddingsOpenAi implements INodeType {
type: 'collection',
default: {},
options: [
+ {
+ displayName: 'Dimensions',
+ name: 'dimensions',
+ default: undefined,
+ description:
+ 'The number of dimensions the resulting output embeddings should have. Only supported in text-embedding-3 and later models.',
+ type: 'options',
+ options: [
+ {
+ name: '256',
+ value: 256,
+ },
+ {
+ name: '512',
+ value: 512,
+ },
+ {
+ name: '1024',
+ value: 1024,
+ },
+ {
+ name: '1536',
+ value: 1536,
+ },
+ {
+ name: '3072',
+ value: 3072,
+ },
+ ],
+ },
{
displayName: 'Base URL',
name: 'baseURL',
@@ -179,6 +209,7 @@ export class EmbeddingsOpenAi implements INodeType {
batchSize?: number;
stripNewLines?: boolean;
timeout?: number;
+ dimensions?: number | undefined;
};
if (options.timeout === -1) {
diff --git a/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverWorkflow/RetrieverWorkflow.node.ts b/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverWorkflow/RetrieverWorkflow.node.ts
index 0aafabf1d4c..bbee5539994 100644
--- a/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverWorkflow/RetrieverWorkflow.node.ts
+++ b/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverWorkflow/RetrieverWorkflow.node.ts
@@ -10,6 +10,7 @@ import type {
INodeTypeDescription,
SupplyData,
INodeParameterResourceLocator,
+ ExecuteWorkflowData,
} from 'n8n-workflow';
import { BaseRetriever, type BaseRetrieverInput } from '@langchain/core/retrievers';
@@ -293,6 +294,8 @@ export class RetrieverWorkflow implements INodeType {
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise {
+ const workflowProxy = this.getWorkflowDataProxy(0);
+
class WorkflowRetriever extends BaseRetriever {
lc_namespace = ['n8n-nodes-langchain', 'retrievers', 'workflow'];
@@ -349,6 +352,9 @@ export class RetrieverWorkflow implements INodeType {
},
);
}
+
+ // same as current workflow
+ baseMetadata.workflowId = workflowProxy.$workflow.id;
}
const rawData: IDataObject = { query };
@@ -384,21 +390,29 @@ export class RetrieverWorkflow implements INodeType {
const items = [newItem] as INodeExecutionData[];
- let receivedItems: INodeExecutionData[][];
+ let receivedData: ExecuteWorkflowData;
try {
- receivedItems = (await this.executeFunctions.executeWorkflow(
+ receivedData = await this.executeFunctions.executeWorkflow(
workflowInfo,
items,
config?.getChild(),
- )) as INodeExecutionData[][];
+ {
+ parentExecution: {
+ executionId: workflowProxy.$execution.id,
+ workflowId: workflowProxy.$workflow.id,
+ },
+ },
+ );
} catch (error) {
// Make sure a valid error gets returned that can by json-serialized else it will
// not show up in the frontend
throw new NodeOperationError(this.executeFunctions.getNode(), error as Error);
}
+ const receivedItems = receivedData.data?.[0] ?? [];
+
const returnData: Document[] = [];
- for (const [index, itemData] of receivedItems[0].entries()) {
+ for (const [index, itemData] of receivedItems.entries()) {
const pageContent = objectToString(itemData.json);
returnData.push(
new Document({
@@ -406,6 +420,7 @@ export class RetrieverWorkflow implements INodeType {
metadata: {
...baseMetadata,
itemIndex: index,
+ executionId: receivedData.executionId,
},
}),
);
diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/ToolWorkflow.node.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/ToolWorkflow.node.ts
index f912e162d9f..b76bcc8ec31 100644
--- a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/ToolWorkflow.node.ts
+++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/ToolWorkflow.node.ts
@@ -14,8 +14,10 @@ import type {
ISupplyDataFunctions,
SupplyData,
ExecutionError,
+ ExecuteWorkflowData,
IDataObject,
INodeParameterResourceLocator,
+ ITaskMetadata,
} from 'n8n-workflow';
import { NodeConnectionType, NodeOperationError, jsonParse } from 'n8n-workflow';
@@ -358,9 +360,14 @@ export class ToolWorkflow implements INodeType {
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise {
+ const workflowProxy = this.getWorkflowDataProxy(0);
+
const name = this.getNodeParameter('name', itemIndex) as string;
const description = this.getNodeParameter('description', itemIndex) as string;
+ let subExecutionId: string | undefined;
+ let subWorkflowId: string | undefined;
+
const useSchema = this.getNodeParameter('specifyInputSchema', itemIndex) as boolean;
let tool: DynamicTool | DynamicStructuredTool | undefined = undefined;
@@ -396,11 +403,16 @@ export class ToolWorkflow implements INodeType {
) as INodeParameterResourceLocator;
workflowInfo.id = value as string;
}
+
+ subWorkflowId = workflowInfo.id;
} else if (source === 'parameter') {
// Read workflow from parameter
const workflowJson = this.getNodeParameter('workflowJson', itemIndex) as string;
try {
workflowInfo.code = JSON.parse(workflowJson) as IWorkflowBase;
+
+ // subworkflow is same as parent workflow
+ subWorkflowId = workflowProxy.$workflow.id;
} catch (error) {
throw new NodeOperationError(
this.getNode(),
@@ -440,13 +452,15 @@ export class ToolWorkflow implements INodeType {
const items = [newItem] as INodeExecutionData[];
- let receivedData: INodeExecutionData;
+ let receivedData: ExecuteWorkflowData;
try {
- receivedData = (await this.executeWorkflow(
- workflowInfo,
- items,
- runManager?.getChild(),
- )) as INodeExecutionData;
+ receivedData = await this.executeWorkflow(workflowInfo, items, runManager?.getChild(), {
+ parentExecution: {
+ executionId: workflowProxy.$execution.id,
+ workflowId: workflowProxy.$workflow.id,
+ },
+ });
+ subExecutionId = receivedData.executionId;
} catch (error) {
// Make sure a valid error gets returned that can by json-serialized else it will
// not show up in the frontend
@@ -454,6 +468,7 @@ export class ToolWorkflow implements INodeType {
}
const response: string | undefined = get(receivedData, [
+ 'data',
0,
0,
'json',
@@ -503,10 +518,25 @@ export class ToolWorkflow implements INodeType {
response = `There was an error: "${executionError.message}"`;
}
+ let metadata: ITaskMetadata | undefined;
+ if (subExecutionId && subWorkflowId) {
+ metadata = {
+ subExecution: {
+ executionId: subExecutionId,
+ workflowId: subWorkflowId,
+ },
+ };
+ }
+
if (executionError) {
- void this.addOutputData(NodeConnectionType.AiTool, index, executionError);
+ void this.addOutputData(NodeConnectionType.AiTool, index, executionError, metadata);
} else {
- void this.addOutputData(NodeConnectionType.AiTool, index, [[{ json: { response } }]]);
+ void this.addOutputData(
+ NodeConnectionType.AiTool,
+ index,
+ [[{ json: { response } }]],
+ metadata,
+ );
}
return response;
};
diff --git a/packages/@n8n/nodes-langchain/utils/logWrapper.ts b/packages/@n8n/nodes-langchain/utils/logWrapper.ts
index 10e55ba6ef9..fa1a38b31a5 100644
--- a/packages/@n8n/nodes-langchain/utils/logWrapper.ts
+++ b/packages/@n8n/nodes-langchain/utils/logWrapper.ts
@@ -10,7 +10,12 @@ import type { Tool } from '@langchain/core/tools';
import { VectorStore } from '@langchain/core/vectorstores';
import { TextSplitter } from '@langchain/textsplitters';
import type { BaseDocumentLoader } from 'langchain/dist/document_loaders/base';
-import type { IExecuteFunctions, INodeExecutionData, ISupplyDataFunctions } from 'n8n-workflow';
+import type {
+ IExecuteFunctions,
+ INodeExecutionData,
+ ISupplyDataFunctions,
+ ITaskMetadata,
+} from 'n8n-workflow';
import { NodeOperationError, NodeConnectionType } from 'n8n-workflow';
import { logAiEvent, isToolsInstance, isBaseChatMemory, isBaseChatMessageHistory } from './helpers';
@@ -220,8 +225,24 @@ export function logWrapper(
arguments: [query, config],
})) as Array>>;
+ const executionId: string | undefined = response[0]?.metadata?.executionId as string;
+ const workflowId: string | undefined = response[0]?.metadata?.workflowId as string;
+
+ const metadata: ITaskMetadata = {};
+ if (executionId && workflowId) {
+ metadata.subExecution = {
+ executionId,
+ workflowId,
+ };
+ }
+
logAiEvent(executeFunctions, 'ai-documents-retrieved', { query });
- executeFunctions.addOutputData(connectionType, index, [[{ json: { response } }]]);
+ executeFunctions.addOutputData(
+ connectionType,
+ index,
+ [[{ json: { response } }]],
+ metadata,
+ );
return response;
};
}
diff --git a/packages/@n8n/task-runner/package.json b/packages/@n8n/task-runner/package.json
index b595fefe6e4..dd9ee6ae176 100644
--- a/packages/@n8n/task-runner/package.json
+++ b/packages/@n8n/task-runner/package.json
@@ -35,6 +35,8 @@
},
"dependencies": {
"@n8n/config": "workspace:*",
+ "@sentry/integrations": "catalog:",
+ "@sentry/node": "catalog:",
"acorn": "8.14.0",
"acorn-walk": "8.3.4",
"n8n-core": "workspace:*",
diff --git a/packages/@n8n/task-runner/src/__tests__/error-reporter.test.ts b/packages/@n8n/task-runner/src/__tests__/error-reporter.test.ts
new file mode 100644
index 00000000000..9345819329b
--- /dev/null
+++ b/packages/@n8n/task-runner/src/__tests__/error-reporter.test.ts
@@ -0,0 +1,31 @@
+import { mock } from 'jest-mock-extended';
+import { ApplicationError } from 'n8n-workflow';
+
+import { ErrorReporter } from '../error-reporter';
+
+describe('ErrorReporter', () => {
+ const errorReporting = new ErrorReporter(mock());
+
+ describe('beforeSend', () => {
+ it('should return null if originalException is an ApplicationError with level warning', () => {
+ const hint = { originalException: new ApplicationError('Test error', { level: 'warning' }) };
+ expect(errorReporting.beforeSend(mock(), hint)).toBeNull();
+ });
+
+ it('should return event if originalException is an ApplicationError with level error', () => {
+ const hint = { originalException: new ApplicationError('Test error', { level: 'error' }) };
+ expect(errorReporting.beforeSend(mock(), hint)).not.toBeNull();
+ });
+
+ it('should return null if originalException is an Error with a non-unique stack', () => {
+ const hint = { originalException: new Error('Test error') };
+ errorReporting.beforeSend(mock(), hint);
+ expect(errorReporting.beforeSend(mock(), hint)).toBeNull();
+ });
+
+ it('should return event if originalException is an Error with a unique stack', () => {
+ const hint = { originalException: new Error('Test error') };
+ expect(errorReporting.beforeSend(mock(), hint)).not.toBeNull();
+ });
+ });
+});
diff --git a/packages/@n8n/task-runner/src/config/base-runner-config.ts b/packages/@n8n/task-runner/src/config/base-runner-config.ts
index 01e00c177ac..e7949d97042 100644
--- a/packages/@n8n/task-runner/src/config/base-runner-config.ts
+++ b/packages/@n8n/task-runner/src/config/base-runner-config.ts
@@ -1,4 +1,16 @@
-import { Config, Env } from '@n8n/config';
+import { Config, Env, Nested } from '@n8n/config';
+
+@Config
+class HealthcheckServerConfig {
+ @Env('N8N_RUNNERS_SERVER_ENABLED')
+ enabled: boolean = false;
+
+ @Env('N8N_RUNNERS_SERVER_HOST')
+ host: string = '127.0.0.1';
+
+ @Env('N8N_RUNNERS_SERVER_PORT')
+ port: number = 5680;
+}
@Config
export class BaseRunnerConfig {
@@ -13,4 +25,7 @@ export class BaseRunnerConfig {
@Env('N8N_RUNNERS_MAX_CONCURRENCY')
maxConcurrency: number = 5;
+
+ @Nested
+ healthcheckServer!: HealthcheckServerConfig;
}
diff --git a/packages/@n8n/task-runner/src/config/main-config.ts b/packages/@n8n/task-runner/src/config/main-config.ts
index a290c0c3803..10b504f1d6f 100644
--- a/packages/@n8n/task-runner/src/config/main-config.ts
+++ b/packages/@n8n/task-runner/src/config/main-config.ts
@@ -2,6 +2,7 @@ import { Config, Nested } from '@n8n/config';
import { BaseRunnerConfig } from './base-runner-config';
import { JsRunnerConfig } from './js-runner-config';
+import { SentryConfig } from './sentry-config';
@Config
export class MainConfig {
@@ -10,4 +11,7 @@ export class MainConfig {
@Nested
jsRunnerConfig!: JsRunnerConfig;
+
+ @Nested
+ sentryConfig!: SentryConfig;
}
diff --git a/packages/@n8n/task-runner/src/config/sentry-config.ts b/packages/@n8n/task-runner/src/config/sentry-config.ts
new file mode 100644
index 00000000000..691f64244f0
--- /dev/null
+++ b/packages/@n8n/task-runner/src/config/sentry-config.ts
@@ -0,0 +1,21 @@
+import { Config, Env } from '@n8n/config';
+
+@Config
+export class SentryConfig {
+ /** Sentry DSN */
+ @Env('N8N_SENTRY_DSN')
+ sentryDsn: string = '';
+
+ //#region Metadata about the environment
+
+ @Env('N8N_VERSION')
+ n8nVersion: string = '';
+
+ @Env('ENVIRONMENT')
+ environment: string = '';
+
+ @Env('DEPLOYMENT_NAME')
+ deploymentName: string = '';
+
+ //#endregion
+}
diff --git a/packages/@n8n/task-runner/src/error-reporter.ts b/packages/@n8n/task-runner/src/error-reporter.ts
new file mode 100644
index 00000000000..167cc37c924
--- /dev/null
+++ b/packages/@n8n/task-runner/src/error-reporter.ts
@@ -0,0 +1,93 @@
+import { RewriteFrames } from '@sentry/integrations';
+import { init, setTag, captureException, close } from '@sentry/node';
+import type { ErrorEvent, EventHint } from '@sentry/types';
+import * as a from 'assert/strict';
+import { createHash } from 'crypto';
+import { ApplicationError } from 'n8n-workflow';
+
+import type { SentryConfig } from '@/config/sentry-config';
+
+/**
+ * Handles error reporting using Sentry
+ */
+export class ErrorReporter {
+ private isInitialized = false;
+
+ /** Hashes of error stack traces, to deduplicate error reports. */
+ private readonly seenErrors = new Set();
+
+ private get dsn() {
+ return this.sentryConfig.sentryDsn;
+ }
+
+ constructor(private readonly sentryConfig: SentryConfig) {
+ a.ok(this.dsn, 'Sentry DSN is required to initialize Sentry');
+ }
+
+ async start() {
+ if (this.isInitialized) return;
+
+ // Collect longer stacktraces
+ Error.stackTraceLimit = 50;
+
+ process.on('uncaughtException', captureException);
+
+ const ENABLED_INTEGRATIONS = [
+ 'InboundFilters',
+ 'FunctionToString',
+ 'LinkedErrors',
+ 'OnUnhandledRejection',
+ 'ContextLines',
+ ];
+
+ setTag('server_type', 'task_runner');
+
+ init({
+ dsn: this.dsn,
+ release: this.sentryConfig.n8nVersion,
+ environment: this.sentryConfig.environment,
+ enableTracing: false,
+ serverName: this.sentryConfig.deploymentName,
+ beforeBreadcrumb: () => null,
+ beforeSend: async (event, hint) => await this.beforeSend(event, hint),
+ integrations: (integrations) => [
+ ...integrations.filter(({ name }) => ENABLED_INTEGRATIONS.includes(name)),
+ new RewriteFrames({ root: process.cwd() }),
+ ],
+ });
+
+ this.isInitialized = true;
+ }
+
+ async stop() {
+ if (!this.isInitialized) {
+ return;
+ }
+
+ await close(1000);
+ }
+
+ async beforeSend(event: ErrorEvent, { originalException }: EventHint) {
+ if (!originalException) return null;
+
+ if (originalException instanceof Promise) {
+ originalException = await originalException.catch((error) => error as Error);
+ }
+
+ if (originalException instanceof ApplicationError) {
+ const { level, extra, tags } = originalException;
+ if (level === 'warning') return null;
+ event.level = level;
+ if (extra) event.extra = { ...event.extra, ...extra };
+ if (tags) event.tags = { ...event.tags, ...tags };
+ }
+
+ if (originalException instanceof Error && originalException.stack) {
+ const eventHash = createHash('sha1').update(originalException.stack).digest('base64');
+ if (this.seenErrors.has(eventHash)) return null;
+ this.seenErrors.add(eventHash);
+ }
+
+ return event;
+ }
+}
diff --git a/packages/@n8n/task-runner/src/healthcheck-server.ts b/packages/@n8n/task-runner/src/healthcheck-server.ts
new file mode 100644
index 00000000000..c6d8965a86b
--- /dev/null
+++ b/packages/@n8n/task-runner/src/healthcheck-server.ts
@@ -0,0 +1,38 @@
+import { ApplicationError } from 'n8n-workflow';
+import { createServer } from 'node:http';
+
+export class HealthcheckServer {
+ private server = createServer((_, res) => {
+ res.writeHead(200);
+ res.end('OK');
+ });
+
+ async start(host: string, port: number) {
+ return await new Promise((resolve, reject) => {
+ const portInUseErrorHandler = (error: NodeJS.ErrnoException) => {
+ if (error.code === 'EADDRINUSE') {
+ reject(new ApplicationError(`Port ${port} is already in use`));
+ } else {
+ reject(error);
+ }
+ };
+
+ this.server.on('error', portInUseErrorHandler);
+
+ this.server.listen(port, host, () => {
+ this.server.removeListener('error', portInUseErrorHandler);
+ console.log(`Healthcheck server listening on ${host}, port ${port}`);
+ resolve();
+ });
+ });
+ }
+
+ async stop() {
+ return await new Promise((resolve, reject) => {
+ this.server.close((error) => {
+ if (error) reject(error);
+ else resolve();
+ });
+ });
+ }
+}
diff --git a/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts b/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts
index 621a9c81a7f..cd966ef8ac4 100644
--- a/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts
+++ b/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts
@@ -36,6 +36,12 @@ describe('JsTaskRunner', () => {
...defaultConfig.jsRunnerConfig,
...opts,
},
+ sentryConfig: {
+ sentryDsn: '',
+ deploymentName: '',
+ environment: '',
+ n8nVersion: '',
+ },
});
const defaultTaskRunner = createRunnerWithOpts();
diff --git a/packages/@n8n/task-runner/src/start.ts b/packages/@n8n/task-runner/src/start.ts
index fcaab84d51d..e09ddf33321 100644
--- a/packages/@n8n/task-runner/src/start.ts
+++ b/packages/@n8n/task-runner/src/start.ts
@@ -2,10 +2,14 @@ import { ensureError } from 'n8n-workflow';
import Container from 'typedi';
import { MainConfig } from './config/main-config';
+import type { ErrorReporter } from './error-reporter';
+import type { HealthcheckServer } from './healthcheck-server';
import { JsTaskRunner } from './js-task-runner/js-task-runner';
+let healthcheckServer: HealthcheckServer | undefined;
let runner: JsTaskRunner | undefined;
let isShuttingDown = false;
+let errorReporter: ErrorReporter | undefined;
function createSignalHandler(signal: string) {
return async function onSignal() {
@@ -20,11 +24,18 @@ function createSignalHandler(signal: string) {
if (runner) {
await runner.stop();
runner = undefined;
+ void healthcheckServer?.stop();
+ }
+
+ if (errorReporter) {
+ await errorReporter.stop();
+ errorReporter = undefined;
}
} catch (e) {
const error = ensureError(e);
console.error('Error stopping task runner', { error });
} finally {
+ console.log('Task runner stopped');
process.exit(0);
}
};
@@ -33,8 +44,22 @@ function createSignalHandler(signal: string) {
void (async function start() {
const config = Container.get(MainConfig);
+ if (config.sentryConfig.sentryDsn) {
+ const { ErrorReporter } = await import('@/error-reporter');
+ errorReporter = new ErrorReporter(config.sentryConfig);
+ await errorReporter.start();
+ }
+
runner = new JsTaskRunner(config);
+ const { enabled, host, port } = config.baseRunnerConfig.healthcheckServer;
+
+ if (enabled) {
+ const { HealthcheckServer } = await import('./healthcheck-server');
+ healthcheckServer = new HealthcheckServer();
+ await healthcheckServer.start(host, port);
+ }
+
process.on('SIGINT', createSignalHandler('SIGINT'));
process.on('SIGTERM', createSignalHandler('SIGTERM'));
})().catch((e) => {
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 88f84389319..1dcbbebbd12 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -25,6 +25,7 @@
"start:default": "cd bin && ./n8n",
"start:windows": "cd bin && n8n",
"test": "pnpm test:sqlite",
+ "test:dev": "N8N_LOG_LEVEL=silent DB_TYPE=sqlite jest --watch",
"test:sqlite": "N8N_LOG_LEVEL=silent DB_TYPE=sqlite jest",
"test:postgres": "N8N_LOG_LEVEL=silent DB_TYPE=postgresdb DB_POSTGRESDB_SCHEMA=alt_schema DB_TABLE_PREFIX=test_ jest --no-coverage",
"test:mysql": "N8N_LOG_LEVEL=silent DB_TYPE=mysqldb DB_TABLE_PREFIX=test_ jest --no-coverage",
@@ -97,8 +98,8 @@
"@n8n_io/license-sdk": "2.13.1",
"@oclif/core": "4.0.7",
"@rudderstack/rudder-sdk-node": "2.0.9",
- "@sentry/integrations": "7.87.0",
- "@sentry/node": "7.87.0",
+ "@sentry/integrations": "catalog:",
+ "@sentry/node": "catalog:",
"aws4": "1.11.0",
"axios": "catalog:",
"bcryptjs": "2.4.3",
@@ -122,7 +123,7 @@
"express-rate-limit": "7.2.0",
"fast-glob": "catalog:",
"flat": "5.0.2",
- "flatted": "3.2.7",
+ "flatted": "catalog:",
"formidable": "3.5.1",
"handlebars": "4.7.8",
"helmet": "7.1.0",
diff --git a/packages/cli/src/__tests__/workflow-execute-additional-data.test.ts b/packages/cli/src/__tests__/workflow-execute-additional-data.test.ts
index 88aee515406..d0aeb3111f6 100644
--- a/packages/cli/src/__tests__/workflow-execute-additional-data.test.ts
+++ b/packages/cli/src/__tests__/workflow-execute-additional-data.test.ts
@@ -1,9 +1,11 @@
import { mock } from 'jest-mock-extended';
-import type {
- IExecuteWorkflowInfo,
- IWorkflowExecuteAdditionalData,
- ExecuteWorkflowOptions,
- IRun,
+import type { IWorkflowBase } from 'n8n-workflow';
+import {
+ type IExecuteWorkflowInfo,
+ type IWorkflowExecuteAdditionalData,
+ type ExecuteWorkflowOptions,
+ type IRun,
+ type INodeExecutionData,
} from 'n8n-workflow';
import type PCancelable from 'p-cancelable';
import Container from 'typedi';
@@ -21,43 +23,59 @@ import { WorkflowStatisticsService } from '@/services/workflow-statistics.servic
import { SubworkflowPolicyChecker } from '@/subworkflows/subworkflow-policy-checker.service';
import { Telemetry } from '@/telemetry';
import { PermissionChecker } from '@/user-management/permission-checker';
-import { executeWorkflow, getBase } from '@/workflow-execute-additional-data';
+import { executeWorkflow, getBase, getRunData } from '@/workflow-execute-additional-data';
import { mockInstance } from '@test/mocking';
-const run = mock({
- data: { resultData: {} },
- finished: true,
- mode: 'manual',
- startedAt: new Date(),
- status: 'new',
-});
+const EXECUTION_ID = '123';
+const LAST_NODE_EXECUTED = 'Last node executed';
-const cancelablePromise = mock>({
- then: jest
- .fn()
- .mockImplementation(async (onfulfilled) => await Promise.resolve(run).then(onfulfilled)),
- catch: jest
- .fn()
- .mockImplementation(async (onrejected) => await Promise.resolve(run).catch(onrejected)),
- finally: jest
- .fn()
- .mockImplementation(async (onfinally) => await Promise.resolve(run).finally(onfinally)),
- [Symbol.toStringTag]: 'PCancelable',
-});
+const getMockRun = ({ lastNodeOutput }: { lastNodeOutput: Array }) =>
+ mock({
+ data: {
+ resultData: {
+ runData: {
+ [LAST_NODE_EXECUTED]: [
+ {
+ startTime: 100,
+ data: {
+ main: lastNodeOutput,
+ },
+ },
+ ],
+ },
+ lastNodeExecuted: LAST_NODE_EXECUTED,
+ },
+ },
+ finished: true,
+ mode: 'manual',
+ startedAt: new Date(),
+ status: 'new',
+ });
+
+const getCancelablePromise = async (run: IRun) =>
+ await mock>({
+ then: jest
+ .fn()
+ .mockImplementation(async (onfulfilled) => await Promise.resolve(run).then(onfulfilled)),
+ catch: jest
+ .fn()
+ .mockImplementation(async (onrejected) => await Promise.resolve(run).catch(onrejected)),
+ finally: jest
+ .fn()
+ .mockImplementation(async (onfinally) => await Promise.resolve(run).finally(onfinally)),
+ [Symbol.toStringTag]: 'PCancelable',
+ });
+
+const processRunExecutionData = jest.fn();
jest.mock('n8n-core', () => ({
__esModule: true,
...jest.requireActual('n8n-core'),
WorkflowExecute: jest.fn().mockImplementation(() => ({
- processRunExecutionData: jest.fn().mockReturnValue(cancelablePromise),
+ processRunExecutionData,
})),
}));
-jest.mock('../workflow-helpers', () => ({
- ...jest.requireActual('../workflow-helpers'),
- getDataLastExecutedNodeData: jest.fn().mockReturnValue({ data: { main: [] } }),
-}));
-
describe('WorkflowExecuteAdditionalData', () => {
const variablesService = mockInstance(VariablesService);
variablesService.getAllCached.mockResolvedValue([]);
@@ -95,17 +113,129 @@ describe('WorkflowExecuteAdditionalData', () => {
expect(eventService.emit).toHaveBeenCalledWith(eventName, payload);
});
- it('`executeWorkflow` should set subworkflow execution as running', async () => {
- const executionId = '123';
- workflowRepository.get.mockResolvedValue(mock({ id: executionId, nodes: [] }));
- activeExecutions.add.mockResolvedValue(executionId);
+ describe('executeWorkflow', () => {
+ const runWithData = getMockRun({ lastNodeOutput: [[{ json: { test: 1 } }]] });
- await executeWorkflow(
- mock(),
- mock(),
- mock({ loadedWorkflowData: undefined }),
- );
+ beforeEach(() => {
+ workflowRepository.get.mockResolvedValue(
+ mock({ id: EXECUTION_ID, nodes: [] }),
+ );
+ activeExecutions.add.mockResolvedValue(EXECUTION_ID);
+ processRunExecutionData.mockReturnValue(getCancelablePromise(runWithData));
+ });
- expect(executionRepository.setRunning).toHaveBeenCalledWith(executionId);
+ it('should execute workflow, return data and execution id', async () => {
+ const response = await executeWorkflow(
+ mock(),
+ mock(),
+ mock({ loadedWorkflowData: undefined, doNotWaitToFinish: false }),
+ );
+
+ expect(response).toEqual({
+ data: runWithData.data.resultData.runData[LAST_NODE_EXECUTED][0].data!.main,
+ executionId: EXECUTION_ID,
+ });
+ });
+
+ it('should execute workflow, skip waiting', async () => {
+ const response = await executeWorkflow(
+ mock(),
+ mock(),
+ mock({ loadedWorkflowData: undefined, doNotWaitToFinish: true }),
+ );
+
+ expect(response).toEqual({
+ data: [null],
+ executionId: EXECUTION_ID,
+ });
+ });
+
+ it('should set sub workflow execution as running', async () => {
+ await executeWorkflow(
+ mock(),
+ mock(),
+ mock({ loadedWorkflowData: undefined }),
+ );
+
+ expect(executionRepository.setRunning).toHaveBeenCalledWith(EXECUTION_ID);
+ });
+ });
+
+ describe('getRunData', () => {
+ it('should throw error to add trigger ndoe', async () => {
+ const workflow = mock({
+ id: '1',
+ name: 'test',
+ nodes: [],
+ active: false,
+ });
+ await expect(getRunData(workflow)).rejects.toThrowError('Missing node to start execution');
+ });
+
+ const workflow = mock({
+ id: '1',
+ name: 'test',
+ nodes: [
+ {
+ type: 'n8n-nodes-base.executeWorkflowTrigger',
+ },
+ ],
+ active: false,
+ });
+
+ it('should return default data', async () => {
+ expect(await getRunData(workflow)).toEqual({
+ executionData: {
+ executionData: {
+ contextData: {},
+ metadata: {},
+ nodeExecutionStack: [
+ {
+ data: { main: [[{ json: {} }]] },
+ metadata: { parentExecution: undefined },
+ node: workflow.nodes[0],
+ source: null,
+ },
+ ],
+ waitingExecution: {},
+ waitingExecutionSource: {},
+ },
+ resultData: { runData: {} },
+ startData: {},
+ },
+ executionMode: 'integrated',
+ workflowData: workflow,
+ });
+ });
+
+ it('should return run data with input data and metadata', async () => {
+ const data = [{ json: { test: 1 } }];
+ const parentExecution = {
+ executionId: '123',
+ workflowId: '567',
+ };
+ expect(await getRunData(workflow, data, parentExecution)).toEqual({
+ executionData: {
+ executionData: {
+ contextData: {},
+ metadata: {},
+ nodeExecutionStack: [
+ {
+ data: { main: [data] },
+ metadata: { parentExecution },
+ node: workflow.nodes[0],
+ source: null,
+ },
+ ],
+ waitingExecution: {},
+ waitingExecutionSource: {},
+ },
+ resultData: { runData: {} },
+ startData: {},
+ },
+ executionMode: 'integrated',
+ workflowData: workflow,
+ });
+ });
});
});
diff --git a/packages/cli/src/config/schema.ts b/packages/cli/src/config/schema.ts
index 9f8bc452320..54fa07e7f5d 100644
--- a/packages/cli/src/config/schema.ts
+++ b/packages/cli/src/config/schema.ts
@@ -296,43 +296,6 @@ export const schema = {
},
},
- diagnostics: {
- enabled: {
- doc: 'Whether diagnostic mode is enabled.',
- format: Boolean,
- default: true,
- env: 'N8N_DIAGNOSTICS_ENABLED',
- },
- config: {
- posthog: {
- apiKey: {
- doc: 'API key for PostHog',
- format: String,
- default: 'phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo',
- env: 'N8N_DIAGNOSTICS_POSTHOG_API_KEY',
- },
- apiHost: {
- doc: 'API host for PostHog',
- format: String,
- default: 'https://ph.n8n.io',
- env: 'N8N_DIAGNOSTICS_POSTHOG_API_HOST',
- },
- },
- frontend: {
- doc: 'Diagnostics config for frontend.',
- format: String,
- default: '1zPn9bgWPzlQc0p8Gj1uiK6DOTn;https://telemetry.n8n.io',
- env: 'N8N_DIAGNOSTICS_CONFIG_FRONTEND',
- },
- backend: {
- doc: 'Diagnostics config for backend.',
- format: String,
- default: '1zPn7YoGC3ZXE9zLeTKLuQCB4F6;https://telemetry.n8n.io',
- env: 'N8N_DIAGNOSTICS_CONFIG_BACKEND',
- },
- },
- },
-
defaultLocale: {
doc: 'Default locale for the UI',
format: String,
diff --git a/packages/cli/src/constants.ts b/packages/cli/src/constants.ts
index be26616fb64..df52a36b961 100644
--- a/packages/cli/src/constants.ts
+++ b/packages/cli/src/constants.ts
@@ -110,25 +110,12 @@ export const UM_FIX_INSTRUCTION =
'Please fix the database by running ./packages/cli/bin/n8n user-management:reset';
/**
- * Units of time in milliseconds
- * @deprecated Please use constants.Time instead.
- */
-export const TIME = {
- SECOND: 1000,
- MINUTE: 60 * 1000,
- HOUR: 60 * 60 * 1000,
- DAY: 24 * 60 * 60 * 1000,
-} as const;
-
-/**
- * Convert time from any unit to any other unit
- *
- * Please amend conversions as necessary.
- * Eventually this will superseed `TIME` above
+ * Convert time from any time unit to any other unit
*/
export const Time = {
milliseconds: {
toMinutes: 1 / (60 * 1000),
+ toSeconds: 1 / 1000,
},
seconds: {
toMilliseconds: 1000,
@@ -150,9 +137,9 @@ export const MIN_PASSWORD_CHAR_LENGTH = 8;
export const MAX_PASSWORD_CHAR_LENGTH = 64;
-export const TEST_WEBHOOK_TIMEOUT = 2 * TIME.MINUTE;
+export const TEST_WEBHOOK_TIMEOUT = 2 * Time.minutes.toMilliseconds;
-export const TEST_WEBHOOK_TIMEOUT_BUFFER = 30 * TIME.SECOND;
+export const TEST_WEBHOOK_TIMEOUT_BUFFER = 30 * Time.seconds.toMilliseconds;
export const GENERIC_OAUTH2_CREDENTIALS_WITH_EDITABLE_SCOPE = [
'oAuth2Api',
diff --git a/packages/cli/src/databases/entities/test-definition.ee.ts b/packages/cli/src/databases/entities/test-definition.ee.ts
index f8ec8d6cb7f..dd39ebef02d 100644
--- a/packages/cli/src/databases/entities/test-definition.ee.ts
+++ b/packages/cli/src/databases/entities/test-definition.ee.ts
@@ -1,18 +1,10 @@
-import {
- Column,
- Entity,
- Generated,
- Index,
- ManyToOne,
- PrimaryColumn,
- RelationId,
-} from '@n8n/typeorm';
+import { Column, Entity, Index, ManyToOne, RelationId } from '@n8n/typeorm';
import { Length } from 'class-validator';
import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee';
import { WorkflowEntity } from '@/databases/entities/workflow-entity';
-import { WithTimestamps } from './abstract-entity';
+import { WithTimestampsAndStringId } from './abstract-entity';
/**
* Entity representing a Test Definition
@@ -24,11 +16,7 @@ import { WithTimestamps } from './abstract-entity';
@Entity()
@Index(['workflow'])
@Index(['evaluationWorkflow'])
-export class TestDefinition extends WithTimestamps {
- @Generated()
- @PrimaryColumn()
- id: number;
-
+export class TestDefinition extends WithTimestampsAndStringId {
@Column({ length: 255 })
@Length(1, 255, {
message: 'Test definition name must be $constraint1 to $constraint2 characters long.',
diff --git a/packages/cli/src/databases/migrations/mysqldb/1731582748663-MigrateTestDefinitionKeyToString.ts b/packages/cli/src/databases/migrations/mysqldb/1731582748663-MigrateTestDefinitionKeyToString.ts
new file mode 100644
index 00000000000..793aa3a262e
--- /dev/null
+++ b/packages/cli/src/databases/migrations/mysqldb/1731582748663-MigrateTestDefinitionKeyToString.ts
@@ -0,0 +1,18 @@
+import type { MigrationContext, IrreversibleMigration } from '@/databases/types';
+
+export class MigrateTestDefinitionKeyToString1731582748663 implements IrreversibleMigration {
+ async up(context: MigrationContext) {
+ const { queryRunner, tablePrefix } = context;
+
+ await queryRunner.query(
+ `ALTER TABLE ${tablePrefix}test_definition CHANGE id tmp_id int NOT NULL AUTO_INCREMENT;`,
+ );
+ await queryRunner.query(
+ `ALTER TABLE ${tablePrefix}test_definition ADD COLUMN id varchar(36) NOT NULL;`,
+ );
+ await queryRunner.query(`UPDATE ${tablePrefix}test_definition SET id = CONVERT(tmp_id, CHAR);`);
+ await queryRunner.query(
+ `CREATE INDEX \`TMP_idx_${tablePrefix}test_definition_id\` ON ${tablePrefix}test_definition (\`id\`);`,
+ );
+ }
+}
diff --git a/packages/cli/src/databases/migrations/mysqldb/index.ts b/packages/cli/src/databases/migrations/mysqldb/index.ts
index 5f0b23d6013..4b219e9a266 100644
--- a/packages/cli/src/databases/migrations/mysqldb/index.ts
+++ b/packages/cli/src/databases/migrations/mysqldb/index.ts
@@ -43,6 +43,7 @@ import { MigrateIntegerKeysToString1690000000001 } from './1690000000001-Migrate
import { SeparateExecutionData1690000000030 } from './1690000000030-SeparateExecutionData';
import { FixExecutionDataType1690000000031 } from './1690000000031-FixExecutionDataType';
import { AddActivatedAtUserSetting1717498465931 } from './1717498465931-AddActivatedAtUserSetting';
+import { MigrateTestDefinitionKeyToString1731582748663 } from './1731582748663-MigrateTestDefinitionKeyToString';
import { CreateLdapEntities1674509946020 } from '../common/1674509946020-CreateLdapEntities';
import { PurgeInvalidWorkflowConnections1675940580449 } from '../common/1675940580449-PurgeInvalidWorkflowConnections';
import { RemoveResetPasswordColumns1690000000030 } from '../common/1690000000030-RemoveResetPasswordColumns';
@@ -142,4 +143,5 @@ export const mysqlMigrations: Migration[] = [
UpdateProcessedDataValueColumnToText1729607673464,
CreateTestDefinitionTable1730386903556,
AddDescriptionToTestDefinition1731404028106,
+ MigrateTestDefinitionKeyToString1731582748663,
];
diff --git a/packages/cli/src/databases/migrations/postgresdb/1731582748663-MigrateTestDefinitionKeyToString.ts b/packages/cli/src/databases/migrations/postgresdb/1731582748663-MigrateTestDefinitionKeyToString.ts
new file mode 100644
index 00000000000..9afc1c3ae38
--- /dev/null
+++ b/packages/cli/src/databases/migrations/postgresdb/1731582748663-MigrateTestDefinitionKeyToString.ts
@@ -0,0 +1,30 @@
+import type { MigrationContext, IrreversibleMigration } from '@/databases/types';
+
+export class MigrateTestDefinitionKeyToString1731582748663 implements IrreversibleMigration {
+ async up(context: MigrationContext) {
+ const { queryRunner, tablePrefix } = context;
+
+ await queryRunner.query(
+ `ALTER TABLE ${tablePrefix}test_definition RENAME COLUMN id to tmp_id;`,
+ );
+ await queryRunner.query(`ALTER TABLE ${tablePrefix}test_definition ADD COLUMN id varchar(36);`);
+ await queryRunner.query(`UPDATE ${tablePrefix}test_definition SET id = tmp_id::text;`);
+ await queryRunner.query(
+ `ALTER TABLE ${tablePrefix}test_definition ALTER COLUMN id SET NOT NULL;`,
+ );
+ await queryRunner.query(
+ `ALTER TABLE ${tablePrefix}test_definition ALTER COLUMN tmp_id DROP DEFAULT;`,
+ );
+ await queryRunner.query(`DROP SEQUENCE IF EXISTS ${tablePrefix}test_definition_id_seq;`);
+ await queryRunner.query(
+ `CREATE UNIQUE INDEX "pk_${tablePrefix}test_definition_id" ON ${tablePrefix}test_definition ("id");`,
+ );
+
+ await queryRunner.query(
+ `ALTER TABLE ${tablePrefix}test_definition DROP CONSTRAINT IF EXISTS "PK_${tablePrefix}245a0013672c8cdc7727afa9b99";`,
+ );
+
+ await queryRunner.query(`ALTER TABLE ${tablePrefix}test_definition DROP COLUMN tmp_id;`);
+ await queryRunner.query(`ALTER TABLE ${tablePrefix}test_definition ADD PRIMARY KEY (id);`);
+ }
+}
diff --git a/packages/cli/src/databases/migrations/postgresdb/index.ts b/packages/cli/src/databases/migrations/postgresdb/index.ts
index 53722563718..689840b9371 100644
--- a/packages/cli/src/databases/migrations/postgresdb/index.ts
+++ b/packages/cli/src/databases/migrations/postgresdb/index.ts
@@ -43,6 +43,7 @@ import { AddMissingPrimaryKeyOnExecutionData1690787606731 } from './169078760673
import { MigrateToTimestampTz1694091729095 } from './1694091729095-MigrateToTimestampTz';
import { AddActivatedAtUserSetting1717498465931 } from './1717498465931-AddActivatedAtUserSetting';
import { FixExecutionMetadataSequence1721377157740 } from './1721377157740-FixExecutionMetadataSequence';
+import { MigrateTestDefinitionKeyToString1731582748663 } from './1731582748663-MigrateTestDefinitionKeyToString';
import { CreateLdapEntities1674509946020 } from '../common/1674509946020-CreateLdapEntities';
import { PurgeInvalidWorkflowConnections1675940580449 } from '../common/1675940580449-PurgeInvalidWorkflowConnections';
import { RemoveResetPasswordColumns1690000000030 } from '../common/1690000000030-RemoveResetPasswordColumns';
@@ -142,4 +143,5 @@ export const postgresMigrations: Migration[] = [
UpdateProcessedDataValueColumnToText1729607673464,
CreateTestDefinitionTable1730386903556,
AddDescriptionToTestDefinition1731404028106,
+ MigrateTestDefinitionKeyToString1731582748663,
];
diff --git a/packages/cli/src/databases/migrations/sqlite/1731582748663-MigrateTestDefinitionKeyToString.ts b/packages/cli/src/databases/migrations/sqlite/1731582748663-MigrateTestDefinitionKeyToString.ts
new file mode 100644
index 00000000000..4ffa3da9833
--- /dev/null
+++ b/packages/cli/src/databases/migrations/sqlite/1731582748663-MigrateTestDefinitionKeyToString.ts
@@ -0,0 +1,25 @@
+import type { MigrationContext, IrreversibleMigration } from '@/databases/types';
+
+export class MigrateTestDefinitionKeyToString1731582748663 implements IrreversibleMigration {
+ transaction = false as const;
+
+ async up(context: MigrationContext) {
+ const { queryRunner, tablePrefix } = context;
+
+ await queryRunner.query(`
+ CREATE TABLE "${tablePrefix}TMP_test_definition" ("id" varchar(36) PRIMARY KEY NOT NULL, "name" varchar(255) NOT NULL, "workflowId" varchar(36) NOT NULL, "evaluationWorkflowId" varchar(36), "annotationTagId" varchar(16), "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "description" text, CONSTRAINT "FK_${tablePrefix}test_definition_annotation_tag" FOREIGN KEY ("annotationTagId") REFERENCES "annotation_tag_entity" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_${tablePrefix}test_definition_evaluation_workflow_entity" FOREIGN KEY ("evaluationWorkflowId") REFERENCES "workflow_entity" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_${tablePrefix}test_definition_workflow_entity" FOREIGN KEY ("workflowId") REFERENCES "workflow_entity" ("id") ON DELETE CASCADE ON UPDATE NO ACTION);`);
+ await queryRunner.query(
+ `INSERT INTO "${tablePrefix}TMP_test_definition" SELECT * FROM "${tablePrefix}test_definition";`,
+ );
+ await queryRunner.query(`DROP TABLE "${tablePrefix}test_definition";`);
+ await queryRunner.query(
+ `ALTER TABLE "${tablePrefix}TMP_test_definition" RENAME TO "${tablePrefix}test_definition";`,
+ );
+ await queryRunner.query(
+ `CREATE INDEX "idx_${tablePrefix}test_definition_workflow_id" ON "${tablePrefix}test_definition" ("workflowId");`,
+ );
+ await queryRunner.query(
+ `CREATE INDEX "idx_${tablePrefix}test_definition_evaluation_workflow_id" ON "${tablePrefix}test_definition" ("evaluationWorkflowId");`,
+ );
+ }
+}
diff --git a/packages/cli/src/databases/migrations/sqlite/index.ts b/packages/cli/src/databases/migrations/sqlite/index.ts
index e9b09ff483e..5c7c6d9a072 100644
--- a/packages/cli/src/databases/migrations/sqlite/index.ts
+++ b/packages/cli/src/databases/migrations/sqlite/index.ts
@@ -40,6 +40,7 @@ import { AddActivatedAtUserSetting1717498465931 } from './1717498465931-AddActiv
import { AddApiKeysTable1724951148974 } from './1724951148974-AddApiKeysTable';
import { AddMissingPrimaryKeyOnAnnotationTagMapping1728659839644 } from './1728659839644-AddMissingPrimaryKeyOnAnnotationTagMapping';
import { AddDescriptionToTestDefinition1731404028106 } from './1731404028106-AddDescriptionToTestDefinition';
+import { MigrateTestDefinitionKeyToString1731582748663 } from './1731582748663-MigrateTestDefinitionKeyToString';
import { UniqueWorkflowNames1620821879465 } from '../common/1620821879465-UniqueWorkflowNames';
import { UpdateWorkflowCredentials1630330987096 } from '../common/1630330987096-UpdateWorkflowCredentials';
import { AddNodeIds1658930531669 } from '../common/1658930531669-AddNodeIds';
@@ -136,6 +137,7 @@ const sqliteMigrations: Migration[] = [
UpdateProcessedDataValueColumnToText1729607673464,
CreateTestDefinitionTable1730386903556,
AddDescriptionToTestDefinition1731404028106,
+ MigrateTestDefinitionKeyToString1731582748663,
];
export { sqliteMigrations };
diff --git a/packages/cli/src/databases/repositories/test-definition.repository.ee.ts b/packages/cli/src/databases/repositories/test-definition.repository.ee.ts
index e9ec3da65d1..ecd4bdde345 100644
--- a/packages/cli/src/databases/repositories/test-definition.repository.ee.ts
+++ b/packages/cli/src/databases/repositories/test-definition.repository.ee.ts
@@ -37,7 +37,7 @@ export class TestDefinitionRepository extends Repository {
return { testDefinitions, count };
}
- async getOne(id: number, accessibleWorkflowIds: string[]) {
+ async getOne(id: string, accessibleWorkflowIds: string[]) {
return await this.findOne({
where: {
id,
@@ -49,7 +49,7 @@ export class TestDefinitionRepository extends Repository {
});
}
- async deleteById(id: number, accessibleWorkflowIds: string[]) {
+ async deleteById(id: string, accessibleWorkflowIds: string[]) {
return await this.delete({
id,
workflow: {
diff --git a/packages/cli/src/evaluation/test-definition.service.ee.ts b/packages/cli/src/evaluation/test-definition.service.ee.ts
index 404167078bf..d32a2e153d1 100644
--- a/packages/cli/src/evaluation/test-definition.service.ee.ts
+++ b/packages/cli/src/evaluation/test-definition.service.ee.ts
@@ -30,7 +30,7 @@ export class TestDefinitionService {
workflowId?: string;
evaluationWorkflowId?: string;
annotationTagId?: string;
- id?: number;
+ id?: string;
}) {
const entity: TestDefinitionLike = {};
@@ -72,13 +72,13 @@ export class TestDefinitionService {
workflowId?: string;
evaluationWorkflowId?: string;
annotationTagId?: string;
- id?: number;
+ id?: string;
}) {
const entity = this.toEntityLike(attrs);
return this.testDefinitionRepository.create(entity);
}
- async findOne(id: number, accessibleWorkflowIds: string[]) {
+ async findOne(id: string, accessibleWorkflowIds: string[]) {
return await this.testDefinitionRepository.getOne(id, accessibleWorkflowIds);
}
@@ -88,7 +88,7 @@ export class TestDefinitionService {
return await this.testDefinitionRepository.save(test);
}
- async update(id: number, attrs: TestDefinitionLike) {
+ async update(id: string, attrs: TestDefinitionLike) {
if (attrs.name) {
const updatedTest = this.toEntity(attrs);
await validateEntity(updatedTest);
@@ -115,7 +115,7 @@ export class TestDefinitionService {
}
}
- async delete(id: number, accessibleWorkflowIds: string[]) {
+ async delete(id: string, accessibleWorkflowIds: string[]) {
const deleteResult = await this.testDefinitionRepository.deleteById(id, accessibleWorkflowIds);
if (deleteResult.affected === 0) {
diff --git a/packages/cli/src/evaluation/test-definitions.controller.ee.ts b/packages/cli/src/evaluation/test-definitions.controller.ee.ts
index c73afaeb3d2..eaa7745c519 100644
--- a/packages/cli/src/evaluation/test-definitions.controller.ee.ts
+++ b/packages/cli/src/evaluation/test-definitions.controller.ee.ts
@@ -2,7 +2,6 @@ import express from 'express';
import assert from 'node:assert';
import { Get, Post, Patch, RestController, Delete } from '@/decorators';
-import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import {
@@ -11,21 +10,12 @@ import {
} from '@/evaluation/test-definition.schema';
import { listQueryMiddleware } from '@/middlewares';
import { getSharedWorkflowIds } from '@/public-api/v1/handlers/workflows/workflows.service';
-import { isPositiveInteger } from '@/utils';
import { TestDefinitionService } from './test-definition.service.ee';
import { TestDefinitionsRequest } from './test-definitions.types.ee';
@RestController('/evaluation/test-definitions')
export class TestDefinitionsController {
- private validateId(id: string) {
- if (!isPositiveInteger(id)) {
- throw new BadRequestError('Test ID is not a number');
- }
-
- return Number(id);
- }
-
constructor(private readonly testDefinitionService: TestDefinitionService) {}
@Get('/', { middlewares: listQueryMiddleware })
@@ -40,7 +30,7 @@ export class TestDefinitionsController {
@Get('/:id')
async getOne(req: TestDefinitionsRequest.GetOne) {
- const testDefinitionId = this.validateId(req.params.id);
+ const { id: testDefinitionId } = req.params;
const userAccessibleWorkflowIds = await getSharedWorkflowIds(req.user, ['workflow:read']);
@@ -82,7 +72,7 @@ export class TestDefinitionsController {
@Delete('/:id')
async delete(req: TestDefinitionsRequest.Delete) {
- const testDefinitionId = this.validateId(req.params.id);
+ const { id: testDefinitionId } = req.params;
const userAccessibleWorkflowIds = await getSharedWorkflowIds(req.user, ['workflow:read']);
@@ -96,7 +86,7 @@ export class TestDefinitionsController {
@Patch('/:id')
async patch(req: TestDefinitionsRequest.Patch, res: express.Response) {
- const testDefinitionId = this.validateId(req.params.id);
+ const { id: testDefinitionId } = req.params;
const bodyParseResult = testDefinitionPatchRequestBodySchema.safeParse(req.body);
if (!bodyParseResult.success) {
diff --git a/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts b/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts
index 7e98877dc73..58d694e5560 100644
--- a/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts
+++ b/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts
@@ -2,7 +2,6 @@ import type { GlobalConfig } from '@n8n/config';
import { mock } from 'jest-mock-extended';
import type { IWorkflowBase } from 'n8n-workflow';
-import config from '@/config';
import { N8N_VERSION } from '@/constants';
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
import type { ProjectRelationRepository } from '@/databases/repositories/project-relation.repository';
@@ -66,7 +65,7 @@ describe('TelemetryEventRelay', () => {
});
beforeEach(() => {
- config.set('diagnostics.enabled', true);
+ globalConfig.diagnostics.enabled = true;
});
afterEach(() => {
@@ -75,7 +74,7 @@ describe('TelemetryEventRelay', () => {
describe('init', () => {
it('with diagnostics enabled, should init telemetry and register listeners', async () => {
- config.set('diagnostics.enabled', true);
+ globalConfig.diagnostics.enabled = true;
const telemetryEventRelay = new TelemetryEventRelay(
eventService,
telemetry,
@@ -96,7 +95,7 @@ describe('TelemetryEventRelay', () => {
});
it('with diagnostics disabled, should neither init telemetry nor register listeners', async () => {
- config.set('diagnostics.enabled', false);
+ globalConfig.diagnostics.enabled = false;
const telemetryEventRelay = new TelemetryEventRelay(
eventService,
telemetry,
diff --git a/packages/cli/src/events/relays/telemetry.event-relay.ts b/packages/cli/src/events/relays/telemetry.event-relay.ts
index 88f954ab932..9e7b0265974 100644
--- a/packages/cli/src/events/relays/telemetry.event-relay.ts
+++ b/packages/cli/src/events/relays/telemetry.event-relay.ts
@@ -37,7 +37,7 @@ export class TelemetryEventRelay extends EventRelay {
}
async init() {
- if (!config.getEnv('diagnostics.enabled')) return;
+ if (!this.globalConfig.diagnostics.enabled) return;
await this.telemetry.init();
diff --git a/packages/cli/src/execution-lifecycle-hooks/__tests__/save-execution-progress.test.ts b/packages/cli/src/execution-lifecycle-hooks/__tests__/save-execution-progress.test.ts
index b0db5becac7..d89f2fb734d 100644
--- a/packages/cli/src/execution-lifecycle-hooks/__tests__/save-execution-progress.test.ts
+++ b/packages/cli/src/execution-lifecycle-hooks/__tests__/save-execution-progress.test.ts
@@ -1,5 +1,4 @@
import {
- deepCopy,
ErrorReporterProxy,
type IRunExecutionData,
type ITaskData,
@@ -87,37 +86,6 @@ test('should update execution when saving progress is enabled', async () => {
expect(reporterSpy).not.toHaveBeenCalled();
});
-test('should update execution when saving progress is disabled, but waitTill is defined', async () => {
- jest.spyOn(fnModule, 'toSaveSettings').mockReturnValue({
- ...commonSettings,
- progress: false,
- });
-
- const reporterSpy = jest.spyOn(ErrorReporterProxy, 'error');
-
- executionRepository.findSingleExecution.mockResolvedValue({} as IExecutionResponse);
-
- const args = deepCopy(commonArgs);
- args[4].waitTill = new Date();
- await saveExecutionProgress(...args);
-
- expect(executionRepository.updateExistingExecution).toHaveBeenCalledWith('some-execution-id', {
- data: {
- executionData: undefined,
- resultData: {
- lastNodeExecuted: 'My Node',
- runData: {
- 'My Node': [{}],
- },
- },
- startData: {},
- },
- status: 'running',
- });
-
- expect(reporterSpy).not.toHaveBeenCalled();
-});
-
test('should report error on failure', async () => {
jest.spyOn(fnModule, 'toSaveSettings').mockReturnValue({
...commonSettings,
diff --git a/packages/cli/src/execution-lifecycle-hooks/save-execution-progress.ts b/packages/cli/src/execution-lifecycle-hooks/save-execution-progress.ts
index 6cd1cfd08f7..ca9899e1ec1 100644
--- a/packages/cli/src/execution-lifecycle-hooks/save-execution-progress.ts
+++ b/packages/cli/src/execution-lifecycle-hooks/save-execution-progress.ts
@@ -16,7 +16,7 @@ export async function saveExecutionProgress(
) {
const saveSettings = toSaveSettings(workflowData.settings);
- if (!saveSettings.progress && !executionData.waitTill) return;
+ if (!saveSettings.progress) return;
const logger = Container.get(Logger);
diff --git a/packages/cli/src/execution-lifecycle-hooks/to-save-settings.ts b/packages/cli/src/execution-lifecycle-hooks/to-save-settings.ts
index 7a25adaeba9..a7af8f3ddc2 100644
--- a/packages/cli/src/execution-lifecycle-hooks/to-save-settings.ts
+++ b/packages/cli/src/execution-lifecycle-hooks/to-save-settings.ts
@@ -18,20 +18,20 @@ export function toSaveSettings(workflowSettings: IWorkflowSettings = {}) {
PROGRESS: config.getEnv('executions.saveExecutionProgress'),
};
+ const {
+ saveDataErrorExecution = DEFAULTS.ERROR,
+ saveDataSuccessExecution = DEFAULTS.SUCCESS,
+ saveManualExecutions = DEFAULTS.MANUAL,
+ saveExecutionProgress = DEFAULTS.PROGRESS,
+ } = workflowSettings;
+
return {
- error: workflowSettings.saveDataErrorExecution
- ? workflowSettings.saveDataErrorExecution !== 'none'
- : DEFAULTS.ERROR !== 'none',
- success: workflowSettings.saveDataSuccessExecution
- ? workflowSettings.saveDataSuccessExecution !== 'none'
- : DEFAULTS.SUCCESS !== 'none',
- manual:
- workflowSettings === undefined || workflowSettings.saveManualExecutions === 'DEFAULT'
- ? DEFAULTS.MANUAL
- : (workflowSettings.saveManualExecutions ?? DEFAULTS.MANUAL),
- progress:
- workflowSettings === undefined || workflowSettings.saveExecutionProgress === 'DEFAULT'
- ? DEFAULTS.PROGRESS
- : (workflowSettings.saveExecutionProgress ?? DEFAULTS.PROGRESS),
+ error: saveDataErrorExecution === 'DEFAULT' ? DEFAULTS.ERROR : saveDataErrorExecution === 'all',
+ success:
+ saveDataSuccessExecution === 'DEFAULT'
+ ? DEFAULTS.SUCCESS
+ : saveDataSuccessExecution === 'all',
+ manual: saveManualExecutions === 'DEFAULT' ? DEFAULTS.MANUAL : saveManualExecutions,
+ progress: saveExecutionProgress === 'DEFAULT' ? DEFAULTS.PROGRESS : saveExecutionProgress,
};
}
diff --git a/packages/cli/src/interfaces.ts b/packages/cli/src/interfaces.ts
index c79f3d67e57..e50b96c3845 100644
--- a/packages/cli/src/interfaces.ts
+++ b/packages/cli/src/interfaces.ts
@@ -1,6 +1,5 @@
import type { Scope } from '@n8n/permissions';
import type { Application } from 'express';
-import type { WorkflowExecute } from 'n8n-core';
import type {
ExecutionError,
ICredentialDataDecryptedObject,
@@ -14,7 +13,6 @@ import type {
ITelemetryTrackProperties,
IWorkflowBase,
CredentialLoadingDetails,
- Workflow,
WorkflowExecuteMode,
ExecutionStatus,
ExecutionSummary,
@@ -300,12 +298,6 @@ export interface IWorkflowErrorData {
};
}
-export interface IWorkflowExecuteProcess {
- startedAt: Date;
- workflow: Workflow;
- workflowExecute: WorkflowExecute;
-}
-
export interface IWorkflowStatisticsDataLoaded {
dataLoaded: boolean;
}
diff --git a/packages/cli/src/posthog/__tests__/posthog.test.ts b/packages/cli/src/posthog/__tests__/posthog.test.ts
index 5c8fe282bfc..5e11d247733 100644
--- a/packages/cli/src/posthog/__tests__/posthog.test.ts
+++ b/packages/cli/src/posthog/__tests__/posthog.test.ts
@@ -3,7 +3,6 @@ import { mock } from 'jest-mock-extended';
import { InstanceSettings } from 'n8n-core';
import { PostHog } from 'posthog-node';
-import config from '@/config';
import { PostHogClient } from '@/posthog';
import { mockInstance } from '@test/mocking';
@@ -20,12 +19,11 @@ describe('PostHog', () => {
const globalConfig = mock({ logging: { level: 'debug' } });
beforeAll(() => {
- config.set('diagnostics.config.posthog.apiKey', apiKey);
- config.set('diagnostics.config.posthog.apiHost', apiHost);
+ globalConfig.diagnostics.posthogConfig = { apiKey, apiHost };
});
beforeEach(() => {
- config.set('diagnostics.enabled', true);
+ globalConfig.diagnostics.enabled = true;
jest.resetAllMocks();
});
@@ -37,7 +35,7 @@ describe('PostHog', () => {
});
it('does not initialize or track if diagnostics are not enabled', async () => {
- config.set('diagnostics.enabled', false);
+ globalConfig.diagnostics.enabled = false;
const ph = new PostHogClient(instanceSettings, globalConfig);
await ph.init();
diff --git a/packages/cli/src/posthog/index.ts b/packages/cli/src/posthog/index.ts
index 8dec9755b38..be025c8a850 100644
--- a/packages/cli/src/posthog/index.ts
+++ b/packages/cli/src/posthog/index.ts
@@ -4,7 +4,6 @@ import type { FeatureFlags, ITelemetryTrackProperties } from 'n8n-workflow';
import type { PostHog } from 'posthog-node';
import { Service } from 'typedi';
-import config from '@/config';
import type { PublicUser } from '@/interfaces';
@Service()
@@ -17,14 +16,14 @@ export class PostHogClient {
) {}
async init() {
- const enabled = config.getEnv('diagnostics.enabled');
+ const { enabled, posthogConfig } = this.globalConfig.diagnostics;
if (!enabled) {
return;
}
const { PostHog } = await import('posthog-node');
- this.postHog = new PostHog(config.getEnv('diagnostics.config.posthog.apiKey'), {
- host: config.getEnv('diagnostics.config.posthog.apiHost'),
+ this.postHog = new PostHog(posthogConfig.apiKey, {
+ host: posthogConfig.apiHost,
});
const logLevel = this.globalConfig.logging.level;
diff --git a/packages/cli/src/runners/__tests__/task-broker.test.ts b/packages/cli/src/runners/__tests__/task-broker.test.ts
index 614d04c3b5d..4cbc4ebfc0d 100644
--- a/packages/cli/src/runners/__tests__/task-broker.test.ts
+++ b/packages/cli/src/runners/__tests__/task-broker.test.ts
@@ -1,8 +1,12 @@
+import type { TaskRunnersConfig } from '@n8n/config';
import type { RunnerMessage, TaskResultData } from '@n8n/task-runner';
import { mock } from 'jest-mock-extended';
-import type { INodeTypeBaseDescription } from 'n8n-workflow';
+import { ApplicationError, type INodeTypeBaseDescription } from 'n8n-workflow';
+
+import { Time } from '@/constants';
import { TaskRejectError } from '../errors';
+import type { RunnerLifecycleEvents } from '../runner-lifecycle-events';
import { TaskBroker } from '../task-broker.service';
import type { TaskOffer, TaskRequest, TaskRunner } from '../task-broker.service';
@@ -12,7 +16,7 @@ describe('TaskBroker', () => {
let taskBroker: TaskBroker;
beforeEach(() => {
- taskBroker = new TaskBroker(mock());
+ taskBroker = new TaskBroker(mock(), mock(), mock());
jest.restoreAllMocks();
});
@@ -618,4 +622,131 @@ describe('TaskBroker', () => {
});
});
});
+
+ describe('task timeouts', () => {
+ let taskBroker: TaskBroker;
+ let config: TaskRunnersConfig;
+ let runnerLifecycleEvents = mock();
+
+ beforeAll(() => {
+ jest.useFakeTimers();
+ config = mock({ taskTimeout: 30 });
+ taskBroker = new TaskBroker(mock(), config, runnerLifecycleEvents);
+ });
+
+ afterAll(() => {
+ jest.useRealTimers();
+ });
+
+ it('on sending task, we should set up task timeout', async () => {
+ jest.spyOn(global, 'setTimeout');
+
+ const taskId = 'task1';
+ const runnerId = 'runner1';
+ const runner = mock({ id: runnerId });
+ const runnerMessageCallback = jest.fn();
+
+ taskBroker.registerRunner(runner, runnerMessageCallback);
+ taskBroker.setTasks({
+ [taskId]: { id: taskId, runnerId, requesterId: 'requester1', taskType: 'test' },
+ });
+
+ await taskBroker.sendTaskSettings(taskId, {});
+
+ expect(setTimeout).toHaveBeenCalledWith(
+ expect.any(Function),
+ config.taskTimeout * Time.seconds.toMilliseconds,
+ );
+ });
+
+ it('on task completion, we should clear timeout', async () => {
+ jest.spyOn(global, 'clearTimeout');
+
+ const taskId = 'task1';
+ const runnerId = 'runner1';
+ const requesterId = 'requester1';
+ const requesterCallback = jest.fn();
+
+ taskBroker.registerRequester(requesterId, requesterCallback);
+ taskBroker.setTasks({
+ [taskId]: {
+ id: taskId,
+ runnerId,
+ requesterId,
+ taskType: 'test',
+ timeout: setTimeout(() => {}, config.taskTimeout * Time.seconds.toMilliseconds),
+ },
+ });
+
+ await taskBroker.taskDoneHandler(taskId, { result: [] });
+
+ expect(clearTimeout).toHaveBeenCalled();
+ expect(taskBroker.getTasks().get(taskId)).toBeUndefined();
+ });
+
+ it('on task error, we should clear timeout', async () => {
+ jest.spyOn(global, 'clearTimeout');
+
+ const taskId = 'task1';
+ const runnerId = 'runner1';
+ const requesterId = 'requester1';
+ const requesterCallback = jest.fn();
+
+ taskBroker.registerRequester(requesterId, requesterCallback);
+ taskBroker.setTasks({
+ [taskId]: {
+ id: taskId,
+ runnerId,
+ requesterId,
+ taskType: 'test',
+ timeout: setTimeout(() => {}, config.taskTimeout * Time.seconds.toMilliseconds),
+ },
+ });
+
+ await taskBroker.taskErrorHandler(taskId, new Error('Test error'));
+
+ expect(clearTimeout).toHaveBeenCalled();
+ expect(taskBroker.getTasks().get(taskId)).toBeUndefined();
+ });
+
+ it('on timeout, we should emit `runner:timed-out-during-task` event and send error to requester', async () => {
+ jest.spyOn(global, 'clearTimeout');
+
+ const taskId = 'task1';
+ const runnerId = 'runner1';
+ const requesterId = 'requester1';
+ const runner = mock({ id: runnerId });
+ const runnerCallback = jest.fn();
+ const requesterCallback = jest.fn();
+
+ taskBroker.registerRunner(runner, runnerCallback);
+ taskBroker.registerRequester(requesterId, requesterCallback);
+
+ taskBroker.setTasks({
+ [taskId]: { id: taskId, runnerId, requesterId, taskType: 'test' },
+ });
+
+ await taskBroker.sendTaskSettings(taskId, {});
+
+ jest.runAllTimers();
+
+ await Promise.resolve();
+
+ expect(runnerLifecycleEvents.emit).toHaveBeenCalledWith('runner:timed-out-during-task');
+
+ await Promise.resolve();
+
+ expect(clearTimeout).toHaveBeenCalled();
+
+ expect(requesterCallback).toHaveBeenCalledWith({
+ type: 'broker:taskerror',
+ taskId,
+ error: new ApplicationError(`Task execution timed out after ${config.taskTimeout} seconds`),
+ });
+
+ await Promise.resolve();
+
+ expect(taskBroker.getTasks().get(taskId)).toBeUndefined();
+ });
+ });
});
diff --git a/packages/cli/src/runners/__tests__/task-runner-process.test.ts b/packages/cli/src/runners/__tests__/task-runner-process.test.ts
index fbab9ee1e3f..9eeb8d69fc6 100644
--- a/packages/cli/src/runners/__tests__/task-runner-process.test.ts
+++ b/packages/cli/src/runners/__tests__/task-runner-process.test.ts
@@ -7,6 +7,8 @@ import type { TaskRunnerAuthService } from '@/runners/auth/task-runner-auth.serv
import { TaskRunnerProcess } from '@/runners/task-runner-process';
import { mockInstance } from '@test/mocking';
+import type { RunnerLifecycleEvents } from '../runner-lifecycle-events';
+
const spawnMock = jest.fn(() =>
mock({
stdout: {
@@ -25,7 +27,7 @@ describe('TaskRunnerProcess', () => {
runnerConfig.enabled = true;
runnerConfig.mode = 'internal_childprocess';
const authService = mock();
- let taskRunnerProcess = new TaskRunnerProcess(logger, runnerConfig, authService);
+ let taskRunnerProcess = new TaskRunnerProcess(logger, runnerConfig, authService, mock());
afterEach(async () => {
spawnMock.mockClear();
@@ -35,34 +37,59 @@ describe('TaskRunnerProcess', () => {
it('should throw if runner mode is external', () => {
runnerConfig.mode = 'external';
- expect(() => new TaskRunnerProcess(logger, runnerConfig, authService)).toThrow();
+ expect(() => new TaskRunnerProcess(logger, runnerConfig, authService, mock())).toThrow();
runnerConfig.mode = 'internal_childprocess';
});
+
+ it('should register listener for `runner:failed-heartbeat-check` event', () => {
+ const runnerLifecycleEvents = mock();
+ new TaskRunnerProcess(logger, runnerConfig, authService, runnerLifecycleEvents);
+
+ expect(runnerLifecycleEvents.on).toHaveBeenCalledWith(
+ 'runner:failed-heartbeat-check',
+ expect.any(Function),
+ );
+ });
+
+ it('should register listener for `runner:timed-out-during-task` event', () => {
+ const runnerLifecycleEvents = mock();
+ new TaskRunnerProcess(logger, runnerConfig, authService, runnerLifecycleEvents);
+
+ expect(runnerLifecycleEvents.on).toHaveBeenCalledWith(
+ 'runner:timed-out-during-task',
+ expect.any(Function),
+ );
+ });
});
describe('start', () => {
beforeEach(() => {
- taskRunnerProcess = new TaskRunnerProcess(logger, runnerConfig, authService);
+ taskRunnerProcess = new TaskRunnerProcess(logger, runnerConfig, authService, mock());
});
- test.each(['PATH', 'NODE_FUNCTION_ALLOW_BUILTIN', 'NODE_FUNCTION_ALLOW_EXTERNAL'])(
- 'should propagate %s from env as is',
- async (envVar) => {
- jest.spyOn(authService, 'createGrantToken').mockResolvedValue('grantToken');
- process.env[envVar] = 'custom value';
+ test.each([
+ 'PATH',
+ 'NODE_FUNCTION_ALLOW_BUILTIN',
+ 'NODE_FUNCTION_ALLOW_EXTERNAL',
+ 'N8N_SENTRY_DSN',
+ 'N8N_VERSION',
+ 'ENVIRONMENT',
+ 'DEPLOYMENT_NAME',
+ ])('should propagate %s from env as is', async (envVar) => {
+ jest.spyOn(authService, 'createGrantToken').mockResolvedValue('grantToken');
+ process.env[envVar] = 'custom value';
- await taskRunnerProcess.start();
+ await taskRunnerProcess.start();
- // @ts-expect-error The type is not correct
- const options = spawnMock.mock.calls[0][2] as SpawnOptions;
- expect(options.env).toEqual(
- expect.objectContaining({
- [envVar]: 'custom value',
- }),
- );
- },
- );
+ // @ts-expect-error The type is not correct
+ const options = spawnMock.mock.calls[0][2] as SpawnOptions;
+ expect(options.env).toEqual(
+ expect.objectContaining({
+ [envVar]: 'custom value',
+ }),
+ );
+ });
it('should pass NODE_OPTIONS env if maxOldSpaceSize is configured', async () => {
jest.spyOn(authService, 'createGrantToken').mockResolvedValue('grantToken');
diff --git a/packages/cli/src/runners/__tests__/task-runner-ws-server.test.ts b/packages/cli/src/runners/__tests__/task-runner-ws-server.test.ts
new file mode 100644
index 00000000000..223cdbdc544
--- /dev/null
+++ b/packages/cli/src/runners/__tests__/task-runner-ws-server.test.ts
@@ -0,0 +1,45 @@
+import type { TaskRunnersConfig } from '@n8n/config';
+import { mock } from 'jest-mock-extended';
+
+import { Time } from '@/constants';
+import { TaskRunnerWsServer } from '@/runners/runner-ws-server';
+
+describe('TaskRunnerWsServer', () => {
+ describe('heartbeat timer', () => {
+ it('should set up heartbeat timer on server start', async () => {
+ const setIntervalSpy = jest.spyOn(global, 'setInterval');
+
+ const server = new TaskRunnerWsServer(
+ mock(),
+ mock(),
+ mock(),
+ mock({ path: '/runners', heartbeatInterval: 30 }),
+ mock(),
+ );
+
+ expect(setIntervalSpy).toHaveBeenCalledWith(
+ expect.any(Function),
+ 30 * Time.seconds.toMilliseconds,
+ );
+
+ await server.shutdown();
+ });
+
+ it('should clear heartbeat timer on server stop', async () => {
+ jest.spyOn(global, 'setInterval');
+ const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
+
+ const server = new TaskRunnerWsServer(
+ mock(),
+ mock(),
+ mock(),
+ mock({ path: '/runners', heartbeatInterval: 30 }),
+ mock(),
+ );
+
+ await server.shutdown();
+
+ expect(clearIntervalSpy).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/packages/cli/src/runners/default-task-runner-disconnect-analyzer.ts b/packages/cli/src/runners/default-task-runner-disconnect-analyzer.ts
index e101c65e281..d61179372b8 100644
--- a/packages/cli/src/runners/default-task-runner-disconnect-analyzer.ts
+++ b/packages/cli/src/runners/default-task-runner-disconnect-analyzer.ts
@@ -1,8 +1,10 @@
import { Service } from 'typedi';
+import config from '@/config';
+
import { TaskRunnerDisconnectedError } from './errors/task-runner-disconnected-error';
-import type { DisconnectAnalyzer } from './runner-types';
-import type { TaskRunner } from './task-broker.service';
+import { TaskRunnerFailedHeartbeatError } from './errors/task-runner-failed-heartbeat.error';
+import type { DisconnectAnalyzer, DisconnectErrorOptions } from './runner-types';
/**
* Analyzes the disconnect reason of a task runner to provide a more
@@ -10,7 +12,16 @@ import type { TaskRunner } from './task-broker.service';
*/
@Service()
export class DefaultTaskRunnerDisconnectAnalyzer implements DisconnectAnalyzer {
- async determineDisconnectReason(runnerId: TaskRunner['id']): Promise {
- return new TaskRunnerDisconnectedError(runnerId);
+ async toDisconnectError(opts: DisconnectErrorOptions): Promise {
+ const { reason, heartbeatInterval } = opts;
+
+ if (reason === 'failed-heartbeat-check' && heartbeatInterval) {
+ return new TaskRunnerFailedHeartbeatError(
+ heartbeatInterval,
+ config.get('deployment.type') !== 'cloud',
+ );
+ }
+
+ return new TaskRunnerDisconnectedError(opts.runnerId ?? 'Unknown runner ID');
}
}
diff --git a/packages/cli/src/runners/errors/task-runner-failed-heartbeat.error.ts b/packages/cli/src/runners/errors/task-runner-failed-heartbeat.error.ts
new file mode 100644
index 00000000000..55b94485740
--- /dev/null
+++ b/packages/cli/src/runners/errors/task-runner-failed-heartbeat.error.ts
@@ -0,0 +1,32 @@
+import { ApplicationError } from 'n8n-workflow';
+
+export class TaskRunnerFailedHeartbeatError extends ApplicationError {
+ description: string;
+
+ constructor(heartbeatInterval: number, isSelfHosted: boolean) {
+ super('Task execution aborted because runner became unresponsive');
+
+ const subtitle =
+ 'The task runner failed to respond as expected, so it was considered unresponsive, and the task was aborted. You can try the following:';
+
+ const fixes = {
+ optimizeScript:
+ 'Optimize your script to prevent CPU-intensive operations, e.g. by breaking them down into smaller chunks or batch processing.',
+ ensureTermination:
+ 'Ensure that all paths in your script are able to terminate, i.e. no infinite loops.',
+ increaseInterval: `If your task can reasonably keep the task runner busy for more than ${heartbeatInterval} ${heartbeatInterval === 1 ? 'second' : 'seconds'}, increase the heartbeat interval using the N8N_RUNNERS_HEARTBEAT_INTERVAL environment variable.`,
+ };
+
+ const suggestions = [fixes.optimizeScript, fixes.ensureTermination];
+
+ if (isSelfHosted) suggestions.push(fixes.increaseInterval);
+
+ const suggestionsText = suggestions
+ .map((suggestion, index) => `${index + 1}. ${suggestion}`)
+ .join(' ');
+
+ const description = `${subtitle}
${suggestionsText}`;
+
+ this.description = description;
+ }
+}
diff --git a/packages/cli/src/runners/errors/task-runner-timeout.error.ts b/packages/cli/src/runners/errors/task-runner-timeout.error.ts
new file mode 100644
index 00000000000..88f35330287
--- /dev/null
+++ b/packages/cli/src/runners/errors/task-runner-timeout.error.ts
@@ -0,0 +1,34 @@
+import { ApplicationError } from 'n8n-workflow';
+
+export class TaskRunnerTimeoutError extends ApplicationError {
+ description: string;
+
+ constructor(taskTimeout: number, isSelfHosted: boolean) {
+ super(
+ `Task execution timed out after ${taskTimeout} ${taskTimeout === 1 ? 'second' : 'seconds'}`,
+ );
+
+ const subtitle =
+ 'The task runner was taking too long on this task, so it was suspected of being unresponsive and restarted, and the task was aborted. You can try the following:';
+
+ const fixes = {
+ optimizeScript:
+ 'Optimize your script to prevent long-running tasks, e.g. by processing data in smaller batches.',
+ ensureTermination:
+ 'Ensure that all paths in your script are able to terminate, i.e. no infinite loops.',
+ increaseTimeout: `If your task can reasonably take more than ${taskTimeout} ${taskTimeout === 1 ? 'second' : 'seconds'}, increase the timeout using the N8N_RUNNERS_TASK_TIMEOUT environment variable.`,
+ };
+
+ const suggestions = [fixes.optimizeScript, fixes.ensureTermination];
+
+ if (isSelfHosted) suggestions.push(fixes.increaseTimeout);
+
+ const suggestionsText = suggestions
+ .map((suggestion, index) => `${index + 1}. ${suggestion}`)
+ .join(' ');
+
+ const description = `${subtitle}