From 508c636c2cf6414667ee2088fc67b823ef4cbac0 Mon Sep 17 00:00:00 2001
From: Romeo Balta <7095569+romeobalta@users.noreply.github.com>
Date: Mon, 4 Aug 2025 09:24:15 +0100
Subject: [PATCH] feat(editor): Ready to run workflows experiment
(no-changelog) (#17946)
---
.../frontend/@n8n/i18n/src/locales/en.json | 5 +
.../frontend/@n8n/stores/src/constants.ts | 1 +
.../handlers/executionFinished.ts | 50 +-
packages/frontend/editor-ui/src/constants.ts | 8 +
.../stores/readyToRunWorkflows.store.ts | 109 ++++
.../workflows/1_playground.ts | 321 +++++++++++
.../workflows/2_playground.ts | 487 ++++++++++++++++
.../workflows/3_playground.ts | 253 +++++++++
.../workflows/4_playground.ts | 518 ++++++++++++++++++
.../frontend/editor-ui/src/views/NodeView.vue | 8 +
.../editor-ui/src/views/WorkflowsView.vue | 107 ++++
11 files changed, 1846 insertions(+), 21 deletions(-)
create mode 100644 packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/stores/readyToRunWorkflows.store.ts
create mode 100644 packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/workflows/1_playground.ts
create mode 100644 packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/workflows/2_playground.ts
create mode 100644 packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/workflows/3_playground.ts
create mode 100644 packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/workflows/4_playground.ts
diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json
index ccc73b20281..c9b4bcdec0c 100644
--- a/packages/frontend/@n8n/i18n/src/locales/en.json
+++ b/packages/frontend/@n8n/i18n/src/locales/en.json
@@ -2718,6 +2718,11 @@
"workflows.ai.starter.collection.card": "Learn how to build AI Agents",
"workflows.ai.starter.collection.folder.name": "🎁 n8n basics: Learn how to build Agents in n8n",
"workflows.ai.starter.collection.error": "Error loading AI Agent starter collection. Please try again later.",
+ "workflows.readyToRunWorkflows.card": "Try a workflow - no setup neeeded",
+ "workflows.readyToRunWorkflows.callout": "See n8n in action - no setup needed",
+ "workflows.readyToRunWorkflows.cta": "Run a workflow",
+ "workflows.readyToRunWorkflows.folder.name": "🚀 Ready-to-run workflows",
+ "workflows.readyToRunWorkflows.error": "Error loading n8n collection. Please try again later.",
"workflowSelectorParameterInput.createNewSubworkflow.name": "My Sub-Workflow",
"importCurlModal.title": "Import cURL command",
"importCurlModal.input.label": "cURL Command",
diff --git a/packages/frontend/@n8n/stores/src/constants.ts b/packages/frontend/@n8n/stores/src/constants.ts
index a491d3bbd97..da27d9360eb 100644
--- a/packages/frontend/@n8n/stores/src/constants.ts
+++ b/packages/frontend/@n8n/stores/src/constants.ts
@@ -33,4 +33,5 @@ export const STORES = {
FOCUS_PANEL: 'focusPanel',
AI_TEMPLATES_STARTER_COLLECTION: 'aiTemplatesStarterCollection',
PERSONALIZED_TEMPLATES: 'personalizedTemplates',
+ EXPERIMENT_READY_TO_RUN_WORKFLOWS: 'readyToRunWorkflows',
} as const;
diff --git a/packages/frontend/editor-ui/src/composables/usePushConnection/handlers/executionFinished.ts b/packages/frontend/editor-ui/src/composables/usePushConnection/handlers/executionFinished.ts
index dc4e3832574..481f65d7e40 100644
--- a/packages/frontend/editor-ui/src/composables/usePushConnection/handlers/executionFinished.ts
+++ b/packages/frontend/editor-ui/src/composables/usePushConnection/handlers/executionFinished.ts
@@ -1,33 +1,34 @@
-import type { ExecutionFinished } from '@n8n/api-types/push/execution';
-import { useUIStore } from '@/stores/ui.store';
import type { IExecutionResponse } from '@/Interface';
+import { useExternalHooks } from '@/composables/useExternalHooks';
+import { useNodeHelpers } from '@/composables/useNodeHelpers';
+import { useRunWorkflow } from '@/composables/useRunWorkflow';
+import { useTelemetry } from '@/composables/useTelemetry';
+import { useToast } from '@/composables/useToast';
+import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
+import { useWorkflowSaving } from '@/composables/useWorkflowSaving';
import { WORKFLOW_SETTINGS_MODAL_KEY } from '@/constants';
+import { codeNodeEditorEventBus, globalLinkActionsEventBus } from '@/event-bus';
+import { useAITemplatesStarterCollectionStore } from '@/experiments/aiTemplatesStarterCollection/stores/aiTemplatesStarterCollection.store';
+import { useReadyToRunWorkflowsStore } from '@/experiments/readyToRunWorkflows/stores/readyToRunWorkflows.store';
+import { useNodeTypesStore } from '@/stores/nodeTypes.store';
+import { useSettingsStore } from '@/stores/settings.store';
+import { useUIStore } from '@/stores/ui.store';
+import { useWorkflowsStore } from '@/stores/workflows.store';
import { getEasyAiWorkflowJson } from '@/utils/easyAiWorkflowUtils';
import {
clearPopupWindowState,
+ getExecutionErrorMessage,
+ getExecutionErrorToastConfiguration,
hasTrimmedData,
hasTrimmedItem,
- getExecutionErrorToastConfiguration,
- getExecutionErrorMessage,
} from '@/utils/executionUtils';
-import { useWorkflowsStore } from '@/stores/workflows.store';
-import { useSettingsStore } from '@/stores/settings.store';
-import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
-import { useTelemetry } from '@/composables/useTelemetry';
-import { parse } from 'flatted';
-import { useToast } from '@/composables/useToast';
-import type { useRouter } from 'vue-router';
-import { useI18n } from '@n8n/i18n';
-import { TelemetryHelpers, EVALUATION_TRIGGER_NODE_TYPE } from 'n8n-workflow';
-import type { IWorkflowBase, ExpressionError, IDataObject, IRunExecutionData } from 'n8n-workflow';
-import { codeNodeEditorEventBus, globalLinkActionsEventBus } from '@/event-bus';
import { getTriggerNodeServiceName } from '@/utils/nodeTypesUtils';
-import { useExternalHooks } from '@/composables/useExternalHooks';
-import { useNodeHelpers } from '@/composables/useNodeHelpers';
-import { useNodeTypesStore } from '@/stores/nodeTypes.store';
-import { useRunWorkflow } from '@/composables/useRunWorkflow';
-import { useWorkflowSaving } from '@/composables/useWorkflowSaving';
-import { useAITemplatesStarterCollectionStore } from '@/experiments/aiTemplatesStarterCollection/stores/aiTemplatesStarterCollection.store';
+import type { ExecutionFinished } from '@n8n/api-types/push/execution';
+import { useI18n } from '@n8n/i18n';
+import { parse } from 'flatted';
+import type { ExpressionError, IDataObject, IRunExecutionData, IWorkflowBase } from 'n8n-workflow';
+import { EVALUATION_TRIGGER_NODE_TYPE, TelemetryHelpers } from 'n8n-workflow';
+import type { useRouter } from 'vue-router';
export type SimplifiedExecution = Pick<
IExecutionResponse,
@@ -44,6 +45,7 @@ export async function executionFinished(
const workflowsStore = useWorkflowsStore();
const uiStore = useUIStore();
const aiTemplatesStarterCollectionStore = useAITemplatesStarterCollectionStore();
+ const readyToRunWorkflowsStore = useReadyToRunWorkflowsStore();
workflowsStore.lastAddedExecutingNode = null;
@@ -71,6 +73,12 @@ export async function executionFinished(
data.status,
);
}
+ if (workflow.meta.templateId.startsWith('37_onboarding_experiments_batch_aug11')) {
+ readyToRunWorkflowsStore.trackExecuteWorkflow(
+ workflow.meta.templateId.split('-').pop() ?? '',
+ data.status,
+ );
+ }
}
uiStore.setProcessingExecutionResults(true);
diff --git a/packages/frontend/editor-ui/src/constants.ts b/packages/frontend/editor-ui/src/constants.ts
index 807f8b91d73..2d15e446c45 100644
--- a/packages/frontend/editor-ui/src/constants.ts
+++ b/packages/frontend/editor-ui/src/constants.ts
@@ -767,12 +767,20 @@ export const TEMPLATE_ONBOARDING_EXPERIMENT = {
variantSuggestedTemplates: 'variant-suggested-templates',
};
+export const BATCH_11AUG_EXPERIMENT = {
+ name: '37_onboarding_experiments_batch_aug11',
+ control: 'control',
+ variantReadyToRun: 'variant-ready-to-run-workflows',
+ variantStarterPack: 'variant-starter-pack-v2',
+};
+
export const EXPERIMENTS_TO_TRACK = [
WORKFLOW_BUILDER_EXPERIMENT.name,
RAG_STARTER_WORKFLOW_EXPERIMENT.name,
EXTRA_TEMPLATE_LINKS_EXPERIMENT.name,
TEMPLATE_ONBOARDING_EXPERIMENT.name,
NDV_UI_OVERHAUL_EXPERIMENT.name,
+ BATCH_11AUG_EXPERIMENT.name,
];
export const MFA_FORM = {
diff --git a/packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/stores/readyToRunWorkflows.store.ts b/packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/stores/readyToRunWorkflows.store.ts
new file mode 100644
index 00000000000..d142df5bf7b
--- /dev/null
+++ b/packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/stores/readyToRunWorkflows.store.ts
@@ -0,0 +1,109 @@
+import { useTelemetry } from '@/composables/useTelemetry';
+import { BATCH_11AUG_EXPERIMENT } from '@/constants';
+import { useCloudPlanStore } from '@/stores/cloudPlan.store';
+import { useFoldersStore } from '@/stores/folders.store';
+import { usePostHog } from '@/stores/posthog.store';
+import { useWorkflowsStore } from '@/stores/workflows.store';
+import { useI18n } from '@n8n/i18n';
+import type { WorkflowDataCreate } from '@n8n/rest-api-client';
+import { STORES } from '@n8n/stores';
+import { useLocalStorage } from '@vueuse/core';
+import { defineStore } from 'pinia';
+import { computed } from 'vue';
+import { PLAYGROUND_1 } from '../workflows/1_playground';
+import { PLAYGROUND_2 } from '../workflows/2_playground';
+import { PLAYGROUND_3 } from '../workflows/3_playground';
+import { PLAYGROUND_4 } from '../workflows/4_playground';
+
+const LOCAL_STORAGE_SETTING_KEY = 'N8N_READY_TO_RUN_WORKFLOWS_DISMISSED';
+
+export const useReadyToRunWorkflowsStore = defineStore(
+ STORES.EXPERIMENT_READY_TO_RUN_WORKFLOWS,
+ () => {
+ const telemetry = useTelemetry();
+ const i18n = useI18n();
+ const foldersStore = useFoldersStore();
+ const workflowsStore = useWorkflowsStore();
+ const posthogStore = usePostHog();
+ const cloudPlanStore = useCloudPlanStore();
+
+ const isFeatureEnabled = computed(() => {
+ return (
+ posthogStore.getVariant(BATCH_11AUG_EXPERIMENT.name) ===
+ BATCH_11AUG_EXPERIMENT.variantReadyToRun && cloudPlanStore.userIsTrialing
+ );
+ });
+
+ const calloutDismissedRef = useLocalStorage(LOCAL_STORAGE_SETTING_KEY, false);
+ const isCalloutDismissed = computed(() => calloutDismissedRef.value);
+
+ const dismissCallout = () => {
+ calloutDismissedRef.value = true;
+ };
+
+ const trackCreateWorkflows = (source: 'card' | 'callout') => {
+ telemetry.track('User created ready to run workflows', {
+ source,
+ });
+ };
+
+ const trackDismissCallout = () => {
+ telemetry.track('User dismissed ready to run workflows callout');
+ };
+
+ const trackOpenWorkflow = (template: string) => {
+ telemetry.track('User opened ready to run workflow', {
+ template,
+ });
+ };
+
+ const trackExecuteWorkflow = (template: string, status: string) => {
+ telemetry.track('User executed ready to run workflow', {
+ template,
+ status,
+ });
+ };
+
+ const createWorkflows = async (projectId: string, parentFolderId?: string) => {
+ const collectionFolder = await foldersStore.createFolder(
+ i18n.baseText('workflows.readyToRunWorkflows.folder.name'),
+ projectId,
+ parentFolderId,
+ );
+ const playground1: WorkflowDataCreate = {
+ ...PLAYGROUND_1,
+ parentFolderId: collectionFolder.id,
+ };
+ const playground2: WorkflowDataCreate = {
+ ...PLAYGROUND_2,
+ parentFolderId: collectionFolder.id,
+ };
+ const playground3: WorkflowDataCreate = {
+ ...PLAYGROUND_3,
+ parentFolderId: collectionFolder.id,
+ };
+ const playground4: WorkflowDataCreate = {
+ ...PLAYGROUND_4,
+ parentFolderId: collectionFolder.id,
+ };
+ await workflowsStore.createNewWorkflow(playground4);
+ await workflowsStore.createNewWorkflow(playground3);
+ await workflowsStore.createNewWorkflow(playground2);
+ await workflowsStore.createNewWorkflow(playground1);
+ dismissCallout();
+
+ return collectionFolder;
+ };
+
+ return {
+ isFeatureEnabled,
+ isCalloutDismissed,
+ createWorkflows,
+ dismissCallout,
+ trackCreateWorkflows,
+ trackDismissCallout,
+ trackOpenWorkflow,
+ trackExecuteWorkflow,
+ };
+ },
+);
diff --git a/packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/workflows/1_playground.ts b/packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/workflows/1_playground.ts
new file mode 100644
index 00000000000..d775c524ea3
--- /dev/null
+++ b/packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/workflows/1_playground.ts
@@ -0,0 +1,321 @@
+import type { WorkflowDataCreate } from '@n8n/rest-api-client';
+
+export const PLAYGROUND_1: WorkflowDataCreate = {
+ meta: {
+ templateId: '37_onboarding_experiments_batch_aug11-1_filter_data',
+ },
+ name: '▶️ 1. Filter data coming from an API',
+ nodes: [
+ {
+ parameters: {},
+ type: 'n8n-nodes-base.merge',
+ typeVersion: 3.2,
+ position: [448, 176],
+ id: '01f2f222-4ff2-41ec-afd9-68496d2e0cb3',
+ name: 'Merge',
+ },
+ {
+ parameters: {},
+ type: 'n8n-nodes-base.manualTrigger',
+ typeVersion: 1,
+ position: [-768, 192],
+ id: 'e5ef1b32-ce8a-4c71-aa14-6885a09feabe',
+ name: 'When clicking ‘Execute workflow’',
+ },
+ {
+ parameters: {
+ assignments: {
+ assignments: [
+ {
+ id: '2fd0b039-7dd9-4666-bc24-d3a81e6d4b68',
+ name: 'quote_category',
+ value: 'Personal',
+ type: 'string',
+ },
+ ],
+ },
+ options: {},
+ },
+ type: 'n8n-nodes-base.set',
+ typeVersion: 3.4,
+ position: [160, 272],
+ id: '42d065cd-1580-4b2a-8cc6-796be9a1da2a',
+ name: 'Set Category = Personal',
+ },
+ {
+ parameters: {
+ assignments: {
+ assignments: [
+ {
+ id: '1ff91e4a-8460-4991-a273-c5f24b4038e9',
+ name: 'quote_category',
+ value: 'team',
+ type: 'string',
+ },
+ ],
+ },
+ options: {},
+ },
+ type: 'n8n-nodes-base.set',
+ typeVersion: 3.4,
+ position: [160, 64],
+ id: 'a5259694-11ed-442c-be2a-e0000926fb30',
+ name: 'Set Category = Team',
+ },
+ {
+ parameters: {
+ assignments: {
+ assignments: [
+ {
+ id: 'c014a174-3f17-41bf-9fa5-19e822427346',
+ name: 'author',
+ value: "={{ $('Make an API request to get a random quote').item.json.author }}",
+ type: 'string',
+ },
+ {
+ id: '1d60a497-d964-406b-96fc-0206c82d5742',
+ name: 'quote',
+ value: "={{ $('Make an API request to get a random quote').item.json.quote }}",
+ type: 'string',
+ },
+ {
+ id: '5faf3496-8aa3-4a71-934c-6c5e3f08100b',
+ name: 'quote_category',
+ value: '={{ $json.quote_category }}',
+ type: 'string',
+ },
+ ],
+ },
+ options: {},
+ },
+ type: 'n8n-nodes-base.set',
+ typeVersion: 3.4,
+ position: [736, 176],
+ id: '82b78022-f986-4bb0-aac8-f34558d46e5d',
+ name: 'Quote with category',
+ },
+ {
+ parameters: {
+ content:
+ 'The node below is an `HTTP Node`. It makes a request to an API, which returns a single random quote. ',
+ height: 512,
+ width: 304,
+ color: 7,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [-512, -48],
+ id: 'd6145c80-0504-4a0b-bfd7-5e85867b3d76',
+ name: 'Sticky Note3',
+ },
+ {
+ parameters: {
+ content:
+ 'The `Filter node` checks if the quote contains "you" or "your" , to categorise the quote.\n\nIf matched, we create a `quote_category` variable in the `Set node` , with the value to "Team"',
+ height: 512,
+ width: 496,
+ color: 7,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [-160, -48],
+ id: '75efc471-b2a7-4cb0-8299-f7ad710ff67e',
+ name: 'Sticky Note5',
+ },
+ {
+ parameters: {
+ content:
+ 'You can reference data input from earlier nodes. We use here the `author` variable which was returned from the first node, the API request.\n\n',
+ height: 512,
+ width: 288,
+ color: 7,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [640, -48],
+ id: '9df256c6-a437-4a53-a760-e6964e73c5c3',
+ name: 'Sticky Note6',
+ },
+ {
+ parameters: {
+ content: 'The `Merge` combines the outputs of both branches into a single list.\n',
+ height: 512,
+ color: 7,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [368, -48],
+ id: 'ad7ec1f3-7a95-444f-a5b8-363fc1224f95',
+ name: 'Sticky Note8',
+ },
+ {
+ parameters: {
+ content:
+ '### ⏩ Next up: \n\n- Tweak and edit this workflow. It\'s made for you to hack up! \n*Example: Try adding the quote `id` to the final output in the "Quote with category" node.*\n\n- Try out the other workflows in the Playground \n\n\n\n\n',
+ height: 240,
+ width: 400,
+ color: 4,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [960, 96],
+ id: '0d9362fd-e399-4ce5-ba47-c9ab59b452de',
+ name: 'Sticky Note9',
+ },
+ {
+ parameters: {
+ content:
+ '**Tip: Ressources**\n- Use the `n8n Assistant` or any LLM like `ChatGPT` to explain a screenshot, fix issues, or create workflows for you\n- Learn and get inspired with [templates](https://n8n.io/workflows/)\n- Follow the [n8n Courses](https://docs.n8n.io/courses/) or find tutorials on Youtube\n- Ask [the community](https://community.n8n.io/) for help \n',
+ height: 176,
+ width: 400,
+ color: 5,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [960, 480],
+ id: '724338d8-3e2b-43a1-8a3d-a38be2a50ed3',
+ name: 'Sticky Note7',
+ },
+ {
+ parameters: {
+ content:
+ '## ▶ Click to start\n\n1. Click the orange `Execute Workflow` button \n2. Double-click nodes to view data flows\n2. Re-run to see results change',
+ height: 448,
+ width: 368,
+ color: 4,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [-912, -48],
+ id: 'a05fb79b-a6e6-498d-bdb2-c73ff7898233',
+ name: 'Sticky Note11',
+ },
+ {
+ parameters: {
+ conditions: {
+ options: {
+ caseSensitive: false,
+ leftValue: '',
+ typeValidation: 'strict',
+ version: 2,
+ },
+ conditions: [
+ {
+ id: '4b2c3ebb-ad22-4d62-a64e-fdc2837565bc',
+ leftValue: '={{ $json.quote }}',
+ rightValue: 'you',
+ operator: {
+ type: 'string',
+ operation: 'contains',
+ },
+ },
+ {
+ id: 'da268099-9c51-4e93-bc02-d8f0eda03ffd',
+ leftValue: '={{ $json.quote }}',
+ rightValue: 'your',
+ operator: {
+ type: 'string',
+ operation: 'contains',
+ },
+ },
+ ],
+ combinator: 'or',
+ },
+ options: {
+ ignoreCase: true,
+ },
+ },
+ type: 'n8n-nodes-base.if',
+ typeVersion: 2.2,
+ position: [-96, 192],
+ id: 'f2dda369-4176-493e-8c6e-c3390a6968a3',
+ name: 'Filter the quote',
+ },
+ {
+ parameters: {
+ url: 'https://dummyjson.com/quotes/random',
+ options: {},
+ },
+ id: 'c4437d01-a813-48a7-ac9a-366740d44428',
+ name: 'Make an API request to get a random quote',
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 1,
+ position: [-400, 192],
+ },
+ ],
+ connections: {
+ Merge: {
+ main: [
+ [
+ {
+ node: 'Quote with category',
+ type: 'main',
+ index: 0,
+ },
+ ],
+ ],
+ },
+ 'When clicking ‘Execute workflow’': {
+ main: [
+ [
+ {
+ node: 'Make an API request to get a random quote',
+ type: 'main',
+ index: 0,
+ },
+ ],
+ ],
+ },
+ 'Set Category = Personnal': {
+ main: [
+ [
+ {
+ node: 'Merge',
+ type: 'main',
+ index: 1,
+ },
+ ],
+ ],
+ },
+ 'Set Category = Team': {
+ main: [
+ [
+ {
+ node: 'Merge',
+ type: 'main',
+ index: 0,
+ },
+ ],
+ ],
+ },
+ 'Filter the quote': {
+ main: [
+ [
+ {
+ node: 'Set Category = Team',
+ type: 'main',
+ index: 0,
+ },
+ ],
+ [
+ {
+ node: 'Set Category = Personnal',
+ type: 'main',
+ index: 0,
+ },
+ ],
+ ],
+ },
+ 'Make an API request to get a random quote': {
+ main: [
+ [
+ {
+ node: 'Filter the quote',
+ type: 'main',
+ index: 0,
+ },
+ ],
+ ],
+ },
+ },
+};
diff --git a/packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/workflows/2_playground.ts b/packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/workflows/2_playground.ts
new file mode 100644
index 00000000000..80648076c6a
--- /dev/null
+++ b/packages/frontend/editor-ui/src/experiments/readyToRunWorkflows/workflows/2_playground.ts
@@ -0,0 +1,487 @@
+/* eslint-disable n8n-local-rules/no-interpolation-in-regular-string */
+import type { WorkflowDataCreate } from '@n8n/rest-api-client';
+
+export const PLAYGROUND_2: WorkflowDataCreate = {
+ meta: {
+ templateId: '37_onboarding_experiments_batch_aug11-2_process_user_answers',
+ },
+ settings: {
+ executionOrder: 'v1',
+ },
+ name: '▶️ 2. Process user answers from a form',
+ nodes: [
+ {
+ parameters: {
+ numberInputs: 3,
+ },
+ type: 'n8n-nodes-base.merge',
+ typeVersion: 3.2,
+ position: [960, 336],
+ id: 'f6f94912-64a3-4671-9bda-abb03f4dc42e',
+ name: 'Merge',
+ },
+ {
+ parameters: {
+ content:
+ '## ▶ Click to start \n\n1. Click the orange `Execute Worfklow` button\n2. Submit the form\n3. Double-click nodes to view data flows\n4. Try different answers',
+ height: 432,
+ width: 352,
+ color: 4,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [-992, 160],
+ id: 'e5382a25-e813-4fba-bd8e-c16918521da7',
+ name: 'Sticky Note11',
+ },
+ {
+ parameters: {
+ content:
+ 'The `Switch` node routes the workflow based on the selected meal type: chicken, vegetarian, or surprise.',
+ height: 432,
+ width: 272,
+ color: 7,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [-592, 160],
+ id: 'ab955ee5-03ea-45cb-afbb-4b36ccfdbda6',
+ name: 'Sticky Note',
+ },
+ {
+ parameters: {
+ rules: {
+ values: [
+ {
+ conditions: {
+ options: {
+ caseSensitive: false,
+ leftValue: '',
+ typeValidation: 'strict',
+ version: 2,
+ },
+ conditions: [
+ {
+ id: 'c90e7527-e4ff-41a8-9177-ffdf7d25c72e',
+ leftValue:
+ "={{ $json['What type of meal would you like to cook tonight ? '] }}",
+ rightValue: 'chicken',
+ operator: {
+ type: 'string',
+ operation: 'contains',
+ },
+ },
+ ],
+ combinator: 'and',
+ },
+ renameOutput: true,
+ outputKey: 'chicken',
+ },
+ {
+ conditions: {
+ options: {
+ caseSensitive: false,
+ leftValue: '',
+ typeValidation: 'strict',
+ version: 2,
+ },
+ conditions: [
+ {
+ leftValue:
+ "={{ $json['What type of meal would you like to cook tonight ? '] }}",
+ rightValue: 'vegetarian',
+ operator: {
+ type: 'string',
+ operation: 'contains',
+ },
+ id: '18bd7d98-f5e3-46df-96c6-8d1c5fea7cf2',
+ },
+ ],
+ combinator: 'and',
+ },
+ renameOutput: true,
+ outputKey: 'vegetarian',
+ },
+ {
+ conditions: {
+ options: {
+ caseSensitive: false,
+ leftValue: '',
+ typeValidation: 'strict',
+ version: 2,
+ },
+ conditions: [
+ {
+ id: '74441f58-e5e5-487c-972d-ec7f9107436d',
+ leftValue:
+ "={{ $json['What type of meal would you like to cook tonight ? '] }}",
+ rightValue: 'surprise',
+ operator: {
+ type: 'string',
+ operation: 'contains',
+ },
+ },
+ ],
+ combinator: 'and',
+ },
+ renameOutput: true,
+ outputKey: 'surprise',
+ },
+ ],
+ },
+ options: {
+ ignoreCase: true,
+ },
+ },
+ type: 'n8n-nodes-base.switch',
+ typeVersion: 3.2,
+ position: [-512, 336],
+ id: '6cbc178b-a420-4d57-a107-247600ae00c8',
+ name: 'Route based on meal preference',
+ },
+ {
+ parameters: {
+ url: '=https://dummyjson.com/recipes/{{$today.weekday}}',
+ options: {},
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [-160, 560],
+ id: '98e4c862-5bf8-4cd9-8548-62d30b3c548a',
+ name: 'Get a random recipe from the API',
+ },
+ {
+ parameters: {
+ url: "=https://dummyjson.com/recipes/search?q={{ $json['What type of meal would you like to cook tonight ? '] }}",
+ options: {},
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [-160, 352],
+ id: '52d53f34-9036-48f8-a71c-f501cc7e37b0',
+ name: 'Get vegetarian recipes from the API',
+ },
+ {
+ parameters: {
+ url: '=https://dummyjson.com/recipes/search?q=chicken',
+ options: {},
+ },
+ type: 'n8n-nodes-base.httpRequest',
+ typeVersion: 4.2,
+ position: [-160, 144],
+ id: '240a9947-81fb-48ba-ac46-27381f4f67b6',
+ name: 'Get chicken recipes from the API',
+ },
+ {
+ parameters: {
+ content: 'These `HTTP nodes` call the API to get recipes based on the user’s choice.',
+ height: 816,
+ width: 304,
+ color: 7,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [-272, -48],
+ id: 'f27030aa-9cca-4872-b672-b7ab8f307584',
+ name: 'Sticky Note2',
+ },
+ {
+ parameters: {
+ content:
+ 'In the Chicken branch, the API returns an array with 8 chicken recipes.\n\nThe `Split Out` node splits the array into separate items: 8 items, one per recipe.',
+ height: 576,
+ width: 224,
+ color: 7,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [80, -48],
+ id: 'e4bc5319-ae92-4a72-878e-8c83ca80d203',
+ name: 'Sticky Note4',
+ },
+ {
+ parameters: {
+ content: 'The `Form node` displays the selected recipe on the completion screen',
+ height: 384,
+ color: 7,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [1152, 208],
+ id: '6eb7f690-756d-46df-b927-91426e9635da',
+ name: 'Sticky Note5',
+ },
+ {
+ parameters: {
+ content: 'The `Merge` node combines data from all three recipe branches.',
+ height: 384,
+ color: 7,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [864, 208],
+ id: '8e52483c-c740-454b-898b-cf457af693a8',
+ name: 'Sticky Note6',
+ },
+ {
+ parameters: {
+ formTitle: 'n8n Form',
+ formFields: {
+ values: [
+ {
+ fieldLabel: 'What type of meal would you like to cook tonight ? ',
+ fieldType: 'dropdown',
+ fieldOptions: {
+ values: [
+ {
+ option: 'Chicken-based',
+ },
+ {
+ option: 'Vegetarian',
+ },
+ {
+ option: 'Surprise me! ',
+ },
+ ],
+ },
+ requiredField: true,
+ },
+ {
+ fieldLabel: "What's your name? ",
+ requiredField: true,
+ },
+ ],
+ },
+ options: {},
+ },
+ type: 'n8n-nodes-base.formTrigger',
+ typeVersion: 2.2,
+ position: [-864, 352],
+ id: '3e5b2d36-5126-45f7-a81c-c1cfc82a392d',
+ name: 'Trigger when user submits form',
+ webhookId: 'd9a8c65e-486f-4304-a34d-87e9d68aa868',
+ },
+ {
+ parameters: {
+ content:
+ 'We want to suggest 1 recipe only, not 8.\nSo we sort by cooking time with the `Sort` node, and then pick the recipe with lowest cooking time using `Limit` node',
+ height: 384,
+ width: 416,
+ color: 7,
+ },
+ type: 'n8n-nodes-base.stickyNote',
+ typeVersion: 1,
+ position: [368, -48],
+ id: '520e150e-9b22-4ed8-b3f2-1d128ed9e4c2',
+ name: 'Sticky Note7',
+ },
+ {
+ parameters: {
+ sortFieldsUi: {
+ sortField: [
+ {
+ fieldName: 'cookTimeMinutes',
+ },
+ ],
+ },
+ options: {},
+ },
+ type: 'n8n-nodes-base.sort',
+ typeVersion: 1,
+ position: [432, 144],
+ id: '17834d09-5d83-420f-9109-105e92baffef',
+ name: 'Sort by cooking time',
+ },
+ {
+ parameters: {},
+ type: 'n8n-nodes-base.limit',
+ typeVersion: 1,
+ position: [624, 144],
+ id: '9db21680-c947-44c1-8194-48e42f995755',
+ name: 'Limit to recipe with lowest cooking time',
+ },
+ {
+ parameters: {
+ operation: 'completion',
+ completionTitle: 'Recipe',
+ completionMessage:
+ "=Hey {{ $('Trigger when user submits form').item.json['What\\'s your name? '] }},
\n\nWhat about cooking a {{ $json.name }} tonight?
\n\nIt shouldn't take you more than {{ $json.prepTimeMinutes }} minutes to prepare.
\n\n