{"workflow":{"id":14208,"name":"Validate JSON payloads against a schema with detailed error messages (no AI)","views":26,"recentViews":1,"totalViews":26,"createdAt":"2026-03-20T23:12:40.480Z","description":"## What is this?\n\nA modular schema checker that returns detailed error messages on validation failure.\n\nStop your workflows from breaking due to bad input. This subworkflow validates incoming JSON against a schema you define and returns clear, human readable errors before anything has the chance to break.\n\nThe most common use: a webhook receives data, you validate it, and either continue or return a `400` with exactly what was wrong. This is how production APIs handle input validation, and this template brings that same reliability to webhooks in n8n.\n\nWorks anywhere you need to check data: webhook payloads, form submissions, API requests, or LLM outputs.\n\n**Supports:** type checks, required fields, enums, regex patterns, numeric ranges, array constraints, conditional logic (`oneOf` / `anyOf` / `allOf`), and rejection of unknown fields.\n\n## Why use this?\n\nBad input breaks workflows and is a nightmare to debug. This catches it upfront so you can error fast, fail safe, and return useful messages instead of breaking down.\n\nIt also makes a self documenting API. The error messages are _so clear_ that the caller doesn't even need to check docs, they're given the fix right in the error.\n\n## How it works\n\nCall this workflow via an **Execute Sub-Workflow** node with two params:\n\n- **`requiredSchema`** - the JSON Schema defining what valid data looks like\n- **`paramsToValidate`** - the actual JSON to check (for instance, `$json.body`)\n\n`requiredSchema` must be an expression wrapped in `{{ }}` to be treated like an object.\n\nReturns `{ valid: true }` on success. On failure, returns `valid: false` with a `validationError` string and the full `requiredSchema` so you know exactly what went wrong and what was expected.\n\n_see the \"Usage Example\" in the template_\n\n### Example error output\n\n```\nValidation failed (3 issues):\n• name: Missing required field \"name\" - Customer full name\n• email: \"not-an-email\" is not valid - expected: Contact email address\n• plan: \"premium\" is not an allowed value. Must be one of: starter, pro, enterprise\n```\n\nEvery error includes the field path, what went wrong, and the description from your schema.\n\n## Don't know JSON Schema?\n\nThe template includes a **prompt template sticky note**. Copy it into any LLM chat with an example of your data and it'll generate a ready-to-use schema for you.\n\n## Quick start\n\n1. Add an **Execute Sub-Workflow** node pointing to this workflow\n2. Set `requiredSchema` to `={{ your_schema_here }}`\n3. Set `paramsToValidate` to `={{ $json.body }}`\n4. Route on `valid`: `true` continues, `false` handles the error","workflow":{"meta":{"instanceId":"cb484ba7b742928a2048bf8829668bed5b5ad9787579adea888f05980292a4a7"},"nodes":[{"id":"0bff1b8c-ed35-4673-835a-47f413847ef2","name":"When Executed by Another Workflow","type":"n8n-nodes-base.executeWorkflowTrigger","position":[1328,352],"parameters":{"workflowInputs":{"values":[{"name":"requiredSchema","type":"object"},{"name":"paramsToValidate","type":"object"}]}},"typeVersion":1.1},{"id":"d639a119-f215-41b5-9669-85306a3146bf","name":"Schema Validation","type":"n8n-nodes-base.code","position":[1664,352],"parameters":{"jsCode":"/**\n * Lightweight JSON Schema validator with human-readable error messages.\n *\n * Supports: type, const, enum, required, properties, additionalProperties,\n * minLength, maxLength, pattern, minimum, maximum, minItems, maxItems,\n * items, oneOf, anyOf, allOf, not.\n */\n\n// ─── Schema & Input ──────────────────────────────────────────────────\nconst requiredSchema = $input.first().json.requiredSchema;\nconst jsonToValidate = $input.first().json.paramsToValidate;\n\n// ─── Helpers ─────────────────────────────────────────────────────────\n\n/**\n * Creates an error object with a path, message, and optional description.\n * Keeps internal metadata separate from user-facing fields via _meta.\n */\nfunction makeError(path, message, schema = {}, meta = {}) {\n  const error = { path, message };\n  if (schema.description) error.description = schema.description;\n  if (Object.keys(meta).length) error._meta = meta;\n  return error;\n}\n\n/**\n * Returns the human-readable type name for a value.\n * Distinguishes arrays, null, and NaN from generic typeof results.\n */\nfunction getTypeName(value) {\n  if (value === null) return \"null\";\n  if (Array.isArray(value)) return \"array\";\n  if (typeof value === \"number\" && isNaN(value)) return \"NaN\";\n  return typeof value;\n}\n\n/**\n * Checks if a value matches JSON Schema's \"integer\" type.\n * Must be a finite number with no fractional part. NaN fails.\n */\nfunction isInteger(value) {\n  return typeof value === \"number\" && Number.isFinite(value) && Math.floor(value) === value;\n}\n\n/**\n * Checks if a value is a valid, finite number.\n * Rejects NaN, Infinity, and -Infinity — none are meaningful\n * in the context of JSON Schema numeric constraints.\n */\nfunction isValidNumber(value) {\n  return typeof value === \"number\" && Number.isFinite(value);\n}\n\n/**\n * Checks whether the given value is a plain object (not null, not an array).\n * Used for schema shape validation and internal type guards.\n */\nfunction isPlainObject(value) {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Deep equality comparison for JSON-compatible values.\n * Handles primitives, arrays, and plain objects recursively.\n * Used by checkEnum so objects/arrays are compared by structure, not reference.\n */\nfunction deepEqual(a, b) {\n  if (a === b) return true;\n  if (a === null || b === null) return false;\n  if (typeof a !== typeof b) return false;\n\n  if (Array.isArray(a)) {\n    if (!Array.isArray(b) || a.length !== b.length) return false;\n    return a.every((val, i) => deepEqual(val, b[i]));\n  }\n\n  if (typeof a === \"object\") {\n    if (Array.isArray(b)) return false;\n    const keysA = Object.keys(a);\n    const keysB = Object.keys(b);\n    if (keysA.length !== keysB.length) return false;\n    return keysA.every((key) => Object.prototype.hasOwnProperty.call(b, key) && deepEqual(a[key], b[key]));\n  }\n\n  return false;\n}\n\n/**\n * Safely tests a string against a regex pattern.\n * Returns { match: boolean, error?: string }.\n * Catches malformed regex patterns instead of crashing.\n */\nfunction safeRegexTest(pattern, value) {\n  try {\n    return { match: new RegExp(pattern).test(value) };\n  } catch (e) {\n    return { match: false, error: `Invalid regex pattern: ${pattern}` };\n  }\n}\n\n/**\n * Pluralizes \"Field\"/\"is\" based on count for error messages.\n * e.g. formatFieldList([\"a\"]) -> 'Field \"a\" is'\n *      formatFieldList([\"a\",\"b\"]) -> 'Fields \"a\", \"b\" are'\n */\nfunction formatFieldList(fields) {\n  const quoted = fields.map((f) => `\"${f}\"`).join(\", \");\n  return fields.length === 1\n    ? `Field ${quoted} is`\n    : `Fields ${quoted} are`;\n}\n\n/**\n * Extracts const-based context from a oneOf variant schema.\n * Returns a \"when x is y\" clause for enriching error messages.\n *\n * e.g. { properties: { type: { const: \"free_product\" } } }\n *   -> ' when type is \"free_product\"'\n */\nfunction buildWhenClause(variantSchema) {\n  const pairs = [];\n  for (const [key, propSchema] of Object.entries(variantSchema.properties || {})) {\n    if (propSchema.const !== undefined) {\n      pairs.push(`${key} is \"${propSchema.const}\"`);\n    }\n  }\n  return pairs.length ? ` when ${pairs.join(\" and \")}` : \"\";\n}\n\n/**\n * Checks whether a value is explicitly nullable in the schema.\n * Handles both enum and const allowing null.\n */\nfunction isNullAllowed(schema) {\n  if (schema.const === null) return true;\n  if (Array.isArray(schema.enum) && schema.enum.some((v) => deepEqual(v, null))) return true;\n  return false;\n}\n\n/**\n * Builds a child path by appending a property key to a parent path.\n * Handles the root case (empty parent) so we get \"name\" instead of \".name\".\n */\nfunction joinPath(parent, key) {\n  return parent ? `${parent}.${key}` : key;\n}\n\n/**\n * Builds a child path by appending an array index to a parent path.\n * Handles the root case (empty parent) so we get \"[0]\" instead of \".[0]\".\n */\nfunction indexPath(parent, index) {\n  return `${parent}[${index}]`;\n}\n\n// ─── Type Validators ─────────────────────────────────────────────────\n\n/**\n * Validates that the data's type matches the schema's declared type.\n * Handles the \"integer\" type that JSON Schema supports but JS typeof doesn't.\n * Rejects NaN and Infinity as invalid numbers.\n */\nfunction checkType(schema, data, at) {\n  if (!schema.type) return [];\n\n  const actual = getTypeName(data);\n\n  // \"integer\" is a special case — typeof returns \"number\" for both ints and floats\n  if (schema.type === \"integer\") {\n    if (!isInteger(data)) {\n      const display = actual === \"number\" ? \"non-integer number\" : actual;\n      return [makeError(at, `Expected type \"integer\" but got \"${display}\"`, schema)];\n    }\n    return [];\n  }\n\n  // NaN technically has typeof \"number\" but is never a valid number\n  if (schema.type === \"number\" && actual === \"NaN\") {\n    return [makeError(at, `Expected type \"number\" but got NaN`, schema)];\n  }\n\n  // Infinity/-Infinity are typeof \"number\" but not finite.\n  // Reject them at the type level so they don't silently pass through.\n  if (schema.type === \"number\" && !Number.isFinite(data)) {\n    return [makeError(at, `Expected a finite number but got ${data}`, schema)];\n  }\n\n  if (actual !== schema.type) {\n    return [makeError(at, `Expected type \"${schema.type}\" but got \"${actual}\"`, schema)];\n  }\n  return [];\n}\n\n/**\n * Validates string-specific constraints: minLength, maxLength, pattern.\n * Falls back to description for pattern errors when available.\n * Catches invalid regex patterns gracefully.\n */\nfunction checkString(schema, data, at) {\n  if (typeof data !== \"string\") return [];\n  const errors = [];\n\n  if (schema.minLength !== undefined && data.length < schema.minLength) {\n    const msg = schema.minLength === 1\n      ? \"Must not be empty\"\n      : `Must be at least ${schema.minLength} character(s) long, got ${data.length}`;\n    errors.push(makeError(at, msg, schema));\n  }\n\n  if (schema.maxLength !== undefined && data.length > schema.maxLength) {\n    errors.push(makeError(at, `Must be at most ${schema.maxLength} character(s) long, got ${data.length}`, schema));\n  }\n\n  // Pattern: use safeRegexTest to avoid crashing on malformed patterns\n  if (schema.pattern) {\n    const result = safeRegexTest(schema.pattern, data);\n    if (result.error) {\n      // Malformed regex in schema — report as a schema error\n      errors.push(makeError(at, result.error));\n    } else if (!result.match) {\n      // Prefer description over raw regex in the message\n      const msg = schema.description\n        ? `\"${data}\" is not valid — expected: ${schema.description}`\n        : `\"${data}\" does not match required pattern: ${schema.pattern}`;\n      errors.push(makeError(at, msg));\n    }\n  }\n\n  return errors;\n}\n\n/**\n * Validates number-specific constraints: minimum, maximum,\n * exclusiveMinimum, exclusiveMaximum.\n * Runs for both \"number\" and \"integer\" schema types.\n */\nfunction checkNumber(schema, data, at) {\n  if (!isValidNumber(data)) return [];\n  const errors = [];\n\n  if (schema.minimum !== undefined && data < schema.minimum)\n    errors.push(makeError(at, `Must be at least ${schema.minimum}, got ${data}`, schema));\n  if (schema.maximum !== undefined && data > schema.maximum)\n    errors.push(makeError(at, `Must be at most ${schema.maximum}, got ${data}`, schema));\n  if (schema.exclusiveMinimum !== undefined && data <= schema.exclusiveMinimum)\n    errors.push(makeError(at, `Must be greater than ${schema.exclusiveMinimum}, got ${data}`, schema));\n  if (schema.exclusiveMaximum !== undefined && data >= schema.exclusiveMaximum)\n    errors.push(makeError(at, `Must be less than ${schema.exclusiveMaximum}, got ${data}`, schema));\n\n  return errors;\n}\n\n/**\n * Validates enum constraint — value must be one of the listed options.\n * Uses deepEqual so objects and arrays are compared by structure, not reference.\n */\nfunction checkEnum(schema, data, at) {\n  if (!schema.enum) return [];\n  if (schema.enum.some((allowed) => deepEqual(allowed, data))) return [];\n  return [makeError(at, `\"${data}\" is not an allowed value. Must be one of: ${schema.enum.join(\", \")}`, schema)];\n}\n\n/**\n * Validates const constraint — value must exactly equal the specified constant.\n */\nfunction checkConst(schema, data, at) {\n  if (schema.const === undefined) return [];\n  if (data === schema.const) return [];\n  return [makeError(at, `Must be \"${schema.const}\" but got \"${data}\"`, schema)];\n}\n\n// ─── Composite Validators ────────────────────────────────────────────\n\n/**\n * Validates array-specific constraints: minItems, maxItems, and\n * recursively validates each item against the items schema.\n * Attaches the raw value to leaf-level errors for context.\n *\n * Container-level errors (minItems, maxItems) use `label` so root-level\n * arrays get a readable path instead of an empty string.\n * Child item paths still use raw `at` for clean indexing (e.g. \"[0]\").\n */\nfunction checkArray(schema, data, at) {\n  if (!Array.isArray(data)) return [];\n  const errors = [];\n  const label = at || \"root\";\n\n  if (schema.minItems !== undefined && data.length < schema.minItems)\n    errors.push(makeError(label, `Must have at least ${schema.minItems} item(s), got ${data.length}`, schema));\n  if (schema.maxItems !== undefined && data.length > schema.maxItems)\n    errors.push(makeError(label, `Must have at most ${schema.maxItems} item(s), got ${data.length}`, schema));\n\n  if (schema.items) {\n    data.forEach((item, i) => {\n      const itemErrors = validate(schema.items, item, indexPath(at, i));\n      // Attach raw value for non-object items so the final message shows what was received\n      itemErrors.forEach((e) => {\n        if (e.value === undefined && typeof item !== \"object\") e.value = item;\n      });\n      errors.push(...itemErrors);\n    });\n  }\n\n  return errors;\n}\n\n/**\n * Validates object-specific constraints: required fields, property schemas,\n * and additionalProperties.\n *\n * When additionalProperties is false with no properties defined,\n * treats the allowed set as empty (rejects all keys).\n *\n * Validates that schema.required is an array before iterating. A bare string\n * like \"name\" would be iterated character-by-character by for..of, producing\n * nonsensical \"missing field 'n'\" errors.\n */\nfunction checkObject(schema, data, at) {\n  if (typeof data !== \"object\" || data === null || Array.isArray(data)) return [];\n  if (!schema.properties && !schema.required && schema.additionalProperties === undefined) return [];\n\n  const errors = [];\n\n  // Guard against non-array required (e.g. required: \"name\")\n  if (schema.required !== undefined && !Array.isArray(schema.required)) {\n    errors.push(makeError(at || \"root\", `Schema error: \"required\" must be an array, got ${getTypeName(schema.required)}`));\n  } else {\n    // Required fields\n    for (const key of schema.required || []) {\n      if (!(key in data)) {\n        const propSchema = schema.properties?.[key] || {};\n        errors.push(makeError(joinPath(at, key), `Missing required field \"${key}\"`, propSchema));\n      }\n    }\n  }\n\n  // Validate each declared property that exists on the data\n  for (const [key, propSchema] of Object.entries(schema.properties || {})) {\n    if (key in data) {\n      errors.push(...validate(propSchema, data[key], joinPath(at, key)));\n    }\n  }\n\n  // Reject undeclared properties when additionalProperties is false.\n  // If properties is not defined, treat allowed set as empty — all keys rejected.\n  if (schema.additionalProperties === false) {\n    const allowed = new Set(Object.keys(schema.properties || {}));\n    for (const key of Object.keys(data)) {\n      if (!allowed.has(key)) {\n        errors.push(makeError(joinPath(at, key), `Unknown field \"${key}\" is not allowed`));\n      }\n    }\n  }\n\n  return errors;\n}\n\n// ─── Logical Combinators ─────────────────────────────────────────────\n\n/**\n * Validates the \"not\" keyword.\n * The data is valid only if it FAILS the inner schema.\n * Reports which forbidden fields were found.\n * Handles generic \"not\" schemas beyond just \"not.required\".\n */\nfunction checkNot(schema, data, at) {\n  if (!schema.not) return [];\n\n  const innerErrors = validate(schema.not, data, at);\n  const label = at || \"root\";\n\n  // If inner schema produced errors, the \"not\" passes (data doesn't match the forbidden schema)\n  if (innerErrors.length > 0) return [];\n\n  // Data matched the forbidden schema — figure out what to report\n  const errors = [];\n\n  // Case 1: not.required — specific forbidden fields present in the data\n  if (Array.isArray(schema.not.required) && typeof data === \"object\" && data !== null) {\n    const present = schema.not.required.filter((k) => k in data);\n    if (present.length) {\n      errors.push(makeError(label, `${formatFieldList(present)} not allowed here`, {}, { forbidden: present }));\n    }\n  }\n\n  // Case 2: generic not (no required, or no forbidden fields found) — general message\n  if (errors.length === 0) {\n    errors.push(makeError(label, \"Value must not match the excluded schema\"));\n  }\n\n  return errors;\n}\n\n/**\n * Validates the \"oneOf\" keyword using best-match strategy.\n *\n * 1. If exactly one variant matches perfectly, pass.\n * 2. If multiple match, report ambiguity.\n * 3. If none match, pick the closest variant (fewest errors, with a\n *    tiebreaker favoring variants where const fields match the data)\n *    and surface only its errors, enriched with \"when\" context.\n *\n * Handles empty oneOf arrays gracefully.\n */\nfunction checkOneOf(schema, data, at) {\n  if (!schema.oneOf) return [];\n\n  const label = at || \"root\";\n\n  // Edge case: empty oneOf means nothing can match\n  if (schema.oneOf.length === 0) {\n    return [makeError(label, \"No variants defined in oneOf — nothing can match\")];\n  }\n\n  const results = schema.oneOf.map((sub) => ({\n    schema: sub,\n    errors: validate(sub, data, at),\n  }));\n\n  const perfect = results.filter((r) => r.errors.length === 0);\n\n  // Exactly one match — valid\n  if (perfect.length === 1) return [];\n\n  // Multiple matches — ambiguous\n  if (perfect.length > 1) {\n    return [makeError(label, \"Ambiguous — matches multiple variants when only one is allowed\")];\n  }\n\n  // No match — find best fit using const-match tiebreaker.\n  // Const matches heavily reduce score so the variant matching\n  // the user's intent always wins ties.\n  const scored = results.map((r) => {\n    let constMatches = 0;\n    for (const [key, propSchema] of Object.entries(r.schema.properties || {})) {\n      if (\n        propSchema.const !== undefined &&\n        typeof data === \"object\" &&\n        data !== null &&\n        data[key] === propSchema.const\n      ) {\n        constMatches++;\n      }\n    }\n    return { ...r, score: r.errors.length - constMatches * (results.length + 1) };\n  });\n  scored.sort((a, b) => a.score - b.score);\n\n  const best = scored[0];\n  const whenClause = buildWhenClause(best.schema);\n\n  // Enrich errors with context and filter out noise (const mismatch errors)\n  const enriched = best.errors\n    .filter((e) => !e.message.startsWith('Must be \"'))\n    .map((e) => {\n      const forbidden = e._meta?.forbidden;\n\n      if (forbidden && whenClause) {\n        // Preserve description from original error if present\n        const enrichedSchema = e.description ? { description: e.description } : {};\n        return makeError(e.path, `${formatFieldList(forbidden)} not allowed${whenClause}`, enrichedSchema);\n      }\n      if (e.message.includes(\"Missing required field\") && whenClause) {\n        const enrichedSchema = e.description ? { description: e.description } : {};\n        return makeError(e.path, `${e.message}${whenClause}`, enrichedSchema);\n      }\n      return e;\n    });\n\n  if (enriched.length) return enriched;\n\n  // Fallback: couldn't isolate clear errors from best match\n  const summary = results.map((r) => {\n    const constEntry = Object.entries(r.schema.properties || {}).find(([, v]) => v.const !== undefined);\n    const name = constEntry ? constEntry[1].const : \"unknown\";\n    return `\"${name}\": ${r.errors.map((e) => e.message).join(\"; \")}`;\n  });\n  return [makeError(label, `Does not match any allowed variant: ${summary.join(\" | \")}`)];\n}\n\n/**\n * Validates \"anyOf\" — at least one subschema must match.\n */\nfunction checkAnyOf(schema, data, at) {\n  if (!schema.anyOf) return [];\n  const anyMatch = schema.anyOf.some((sub) => validate(sub, data, at).length === 0);\n  if (anyMatch) return [];\n  return [makeError(at || \"root\", \"Does not match any allowed option\", schema)];\n}\n\n/**\n * Validates \"allOf\" — every subschema must match.\n */\nfunction checkAllOf(schema, data, at) {\n  if (!schema.allOf) return [];\n  return schema.allOf.flatMap((sub) => validate(sub, data, at));\n}\n\n// ─── Main Validator ──────────────────────────────────────────────────\n\n/**\n * Recursively validates data against a JSON Schema.\n * Returns an array of error objects. Empty array = valid.\n *\n * Path starts as \"\" (root). Child paths are built via joinPath/indexPath\n * so top-level fields appear as \"name\" not \"root.name\", while nested\n * fields read naturally as \"address.street\" or \"tags[0]\".\n * Only true root-level errors (e.g. wrong top-level type) show as \"root\".\n *\n * Handles null/undefined before type checking because typeof null === \"object\" in JS.\n * Allows null/undefined when explicitly permitted by const or enum.\n *\n * Rejects non-plain-object schemas up front instead of silently\n * passing everything or crashing on null.\n *\n * When schema.const is defined, enum is skipped. const is strictly\n * more specific (single value) and subsumes enum.\n */\nfunction validate(schema, data, path = \"\") {\n  const at = path;\n\n  // Schema shape guard — reject anything that isn't a plain object.\n  // Catches null, undefined, strings, numbers, booleans, and arrays, all of\n  // which would either crash or silently produce zero errors.\n  if (!isPlainObject(schema)) {\n    return [makeError(at || \"root\", `Invalid schema: expected a plain object, got ${getTypeName(schema)}`)];\n  }\n\n  // Null/undefined: allow if schema explicitly permits it via const or enum,\n  // otherwise reject early before type check (typeof null === \"object\" in JS)\n  if (data === null || data === undefined) {\n    if (isNullAllowed(schema)) {\n      // null is explicitly valid here — still check const/enum but skip everything else\n      const errors = [...checkConst(schema, data, at || \"root\")];\n      // Skip enum when const is defined — const subsumes enum\n      if (schema.const === undefined) errors.push(...checkEnum(schema, data, at || \"root\"));\n      return errors;\n    }\n    return [makeError(at || \"root\", \"Field is missing or null\", schema)];\n  }\n\n  // Type check (gates further validation — wrong type means deeper checks are meaningless)\n  const typeErrors = checkType(schema, data, at || \"root\");\n  if (typeErrors.length) return typeErrors;\n\n  // When const is defined, it's the authoritative value check —\n  // enum is redundant and potentially contradictory. Skip it.\n  const enumErrors = schema.const !== undefined ? [] : checkEnum(schema, data, at || \"root\");\n\n  // Run all validators and collect errors\n  return [\n    ...checkConst(schema, data, at || \"root\"),\n    ...checkString(schema, data, at || \"root\"),\n    ...checkNumber(schema, data, at || \"root\"),\n    ...enumErrors,\n    ...checkArray(schema, data, at),\n    ...checkObject(schema, data, at),\n    ...checkNot(schema, data, at),\n    ...checkOneOf(schema, data, at),\n    ...checkAnyOf(schema, data, at),\n    ...checkAllOf(schema, data, at),\n  ];\n}\n\n// ─── Format & Output ─────────────────────────────────────────────────\n\n/**\n * Formats a single error object into a readable bullet line.\n * Includes the raw value if present, and appends description when\n * it adds context beyond what's already in the message.\n */\nfunction formatError(error) {\n  let line = `• ${error.path}: ${error.message}`;\n  if (error.value !== undefined) line += ` (got: \"${error.value}\")`;\n  if (error.description && !error.message.includes(error.description)) line += ` — ${error.description}`;\n  return line;\n}\n\n/**\n * Strips internal metadata (_meta) from error objects\n * so it never leaks into the output.\n */\nfunction cleanErrors(errors) {\n  return errors.map(({ _meta, ...rest }) => rest);\n}\n\n// ─── Run ─────────────────────────────────────────────────────────────\n\nlet output;\n\ntry {\n  const errors = validate(requiredSchema, jsonToValidate);\n\n  if (errors.length) {\n    const msg = cleanErrors(errors).map(formatError).join(\"\\n\");\n\n    output = {\n      valid: false,\n      validationError: `Validation failed (${errors.length} issue${errors.length > 1 ? \"s\" : \"\"}):\\n${msg}`,\n      requiredSchema: JSON.parse(JSON.stringify(requiredSchema)),\n    };\n  } else {\n    output = {\n      valid: true,\n      validationError: null,\n    };\n  }\n} catch (error) {\n  output = {\n    valid: false,\n    validationError: `Validator error: ${error.message}`,\n  };\n}\n\nreturn output;"},"typeVersion":2},{"id":"00618ad0-367e-4ca0-9032-8357d3cc87dd","name":"PLACEHOLDER: Source of your data","type":"n8n-nodes-base.set","position":[2000,560],"parameters":{"options":{},"assignments":{"assignments":[{"id":"33c2bdce-dcba-49d5-8964-044eedf7f58a","name":"body","type":"object","value":"{\n  \"email\": \"user@example.com\",\n  \"plan\": \"cloud\",\n  \"seat_count\": 25\n}"}]}},"typeVersion":3.4},{"id":"8287706f-d600-42fd-b843-2b13bd106d14","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[1920,432],"parameters":{"color":7,"width":528,"height":320,"content":"## Copy and paste this sticky with the nodes inside of it into your AI prompt\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n_these nodes are just here to give the llm extra context_\n\n## JSON Schema Generator\n\nThe nodes below are part of a reusable n8n subworkflow that validates JSON objects against a JSON Schema. I need you to generate a schema for my use case.\n\nThe schema gets passed into the `requiredSchema` param and the JSON to validate goes into `paramsToValidate`. Both are wrapped in `={{ }}` so n8n treats them as objects.\n\n### What the Validator Supports\n\nYou can use any of these keywords in the schema you generate:\n\n- **Types:** `string`, `number`, `integer`, `boolean`, `array`, `object`, `null`\n- **String:** `minLength`, `maxLength`, `pattern` (regex)\n- **Number:** `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`\n- **Value restrictions:** `enum` (list of allowed values), `const` (exact match)\n- **Arrays:** `minItems`, `maxItems`, `items` (validates each element recursively)\n- **Objects:** `required`, `properties`, `additionalProperties`\n- **Conditional logic:** `oneOf`, `anyOf`, `allOf`, `not`\n- **`description`** — this is important: the validator pulls descriptions into error messages, so every field needs one\n\n### Rules for Generating the Schema\n\n- Every field MUST have a `description` in plain language — they appear in validation errors\n- Use `integer` instead of `number` for whole numbers\n- Use `enum` for fields with a fixed set of allowed values\n- Use `oneOf` with `const` and `not: { required: [...] }` for conditional required/forbidden fields\n- Use `additionalProperties: false` if extra fields should be rejected\n- Use `pattern` with regex for formatted strings (emails, URLs, codes, etc.)\n- Use `minimum` / `maximum` for numeric bounds\n- Use `minLength: 1` for strings that must not be empty\n- Use `minItems` for arrays that need at least one entry\n\n### Before Generating\n\nAsk me follow-up questions first to make sure you understand:\n\n- Which fields are required vs optional\n- Any conditional logic (e.g. \"field X is required only when field Y is a certain value\")\n- Valid value ranges, enums, or patterns for each field\n- Whether unknown/extra fields should be rejected\n- Any fields that conflict with each other (e.g. \"don't send A and B together\")\n\n### Output Format\n\nAsk the user whether they want:\n\n1. **Full n8n nodes** (default) — the complete set of ready-to-paste n8n nodes: the Execute Sub-Workflow node (with the schema already embedded), the If node to check `valid`, and the Respond to Webhook node for returning 400 errors.\n2. **Schema only** — just the raw JSON Schema object.\n\nOn the **first response**, default to returning the full nodes unless the user says otherwise. On **subsequent changes or revisions**, only return the updated schema — the user already has the nodes and just needs to swap the schema value.\n\n### How to Embed the Schema in n8n\n\nWhen generating the Execute Sub-Workflow node, the schema must be set correctly in the `requiredSchema` parameter:\n\n- The value must be wrapped in `={{ }}` so n8n evaluates it as a JavaScript expression that returns an object — not a string.\n- The schema JSON goes directly between `={{ ` and ` }}` with no quotes around the outer object.\n- All regex backslashes inside `pattern` values must be **double-escaped** (`\\\\\\\\`) because the expression is evaluated as a JS string first. For example, the regex `^\\S+@\\S+\\.\\S+$` becomes `\"^\\\\\\\\S+@\\\\\\\\S+\\\\\\\\.\\\\\\\\S+$\"` in the node parameter.\n- The `paramsToValidate` param should be set to `={{ $json.body }}` or wherever the incoming data lives. Remind the user to update this path to match their actual data location.\n- The Execute Sub-Workflow node must point to the ID of the Param Schema Validation workflow. Remind the user to re-select the workflow from the dropdown after importing since workflow IDs differ per instance.\n\n### Generating the Full Nodes\n\nWhen returning full nodes, use the following node structure as a base and only modify the `requiredSchema` value with the generated schema. Keep all other fields, positions, and wiring intact.\n\nThe nodes to include are:\n\n1. **Execute Sub-Workflow node** (`n8n-nodes-base.executeWorkflow`) — calls the validator with `requiredSchema` and `paramsToValidate`\n\n2. **If node** (`n8n-nodes-base.if`) — checks `{{ $json.valid }}` is true\n\n3. **Respond to Webhook node** (`n8n-nodes-base.respondToWebhook`) — returns a 400 with the validation error and schema in the response body\n\nThe Respond to Webhook node MUST use this exact response body format:\n\n```\n\n{\n\n  \"error\": {{ $json.validationError.toJsonString() }},\n\n  \"requestSchema\": {{ $json.requiredSchema.toJsonString() }}\n\n}\n\n```\n\nSet the response code to `400`. This format ensures the caller gets both the human-readable error string and the full schema they violated, which is especially useful for LLM-based callers that can self-correct.\n\nInclude the `connections` object so the nodes are wired: Execute Sub-Workflow → If → (true) continues, (false) → Respond to Webhook with 400.\n\nDo NOT include the Webhook trigger node — the user will already have their own trigger.\n\n### My JSON\n\nThe user will provide an example of their JSON request separately. If they do not, ask them for it."},"typeVersion":1},{"id":"23b82931-8571-4d0f-b4c7-bbfd3dbd6ed2","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[480,224],"parameters":{"width":704,"height":816,"content":"# JSON Schema Validator\n\n## How it works\n\nCall this via an **Execute Sub-Workflow** node with two params:\n\n- **`requiredSchema`** — your JSON Schema (wrapped in `={{ }}`)\n- **`paramsToValidate`** — the data to check, e.g. `={{ $json.body }}`\n\nReturns `{ valid: true }` on success. On failure, returns `valid: false` with a `validationError` string and the full `requiredSchema` — useful for error responses or feeding back into an LLM to self-correct.\n\n## Example error output\n\nValidation failed (3 issues):\n- name: Missing required field \"name\" — Customer full name\n- email: \"not-an-email\" is not valid — expected: Contact email address\n- plan: \"premium\" is not an allowed value. Must be one of: starter, pro, enterprise\n\n## Don't know JSON Schema?\n\nCopy the **prompt template sticky note** and its two nodes, paste into any LLM chat with an example of your data, and it'll generate a schema for you.\n\n## Quick start\n\n1. Add an **Execute Sub-Workflow** node pointing to this workflow\n2. Set `requiredSchema` to `={{ your_schema_here }}`\n3. Set `paramsToValidate` to `={{ $json.body }}`\n4. Route on `valid` — `true` continues, `false` handles the error"},"typeVersion":1},{"id":"10948397-947f-4006-95dd-fb9db33f3cf0","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[1920,224],"parameters":{"color":4,"width":528,"height":208,"content":"# Schema Creation\nAIs are great at making schemas. Copy and paste the two nodes below with the entire sticky into your agent of choice with an example of your request.\n\n\n_Out of view in the sticky is a prompt that will help the agent know how to make the schema_"},"typeVersion":1},{"id":"d2af7f8f-c367-4fc9-a794-ed697ed6e7cd","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[2496,224],"parameters":{"color":6,"width":1248,"height":624,"content":"# Usage Example\nThis is an example showing how to wire the validator into a webhook endpoint"},"typeVersion":1},{"id":"1375dac8-c6bf-4b76-bb26-8e935e1540bf","name":"Webhook","type":"n8n-nodes-base.webhook","position":[2576,368],"webhookId":"19b37d89-d47e-4f90-a945-8a92d61d8d8b","parameters":{"path":"19b37d89-d47e-4f90-a945-8a92d61d8d8b","options":{},"responseMode":"responseNode"},"typeVersion":2.1},{"id":"38928989-fd9a-4fac-873f-12dfa4d7dcfd","name":"If Params Valid","type":"n8n-nodes-base.if","position":[2992,368],"parameters":{"options":{},"conditions":{"options":{"version":3,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"12bd4ddc-000b-4e4d-aae0-4a4b0074b027","operator":{"type":"boolean","operation":"true","singleValue":true},"leftValue":"={{ $json.valid }}","rightValue":false}]}},"typeVersion":2.3},{"id":"1aef7e3c-1e25-417d-9ea9-368a2e85c49e","name":"Return 400 param error","type":"n8n-nodes-base.respondToWebhook","position":[2992,592],"parameters":{"options":{"responseCode":400},"respondWith":"json","responseBody":"={\n  \"error\": {{ $json.validationError.toJsonString() }},\n  \"requestSchema\": {{ $json.requiredSchema.toJsonString() }}\n} "},"typeVersion":1.5},{"id":"698fff1b-c266-47e1-81cb-ad252a448889","name":"Your workflow logic here","type":"n8n-nodes-base.noOp","position":[3248,368],"parameters":{},"typeVersion":1},{"id":"93d5b54b-cbc2-4db3-a486-81a8d3e695f3","name":"Return Success Response","type":"n8n-nodes-base.respondToWebhook","position":[3456,368],"parameters":{"options":{},"respondWith":"json","responseBody":"={\n  \"success\": true\n} "},"typeVersion":1.5},{"id":"ccac00e9-f3fd-4345-992f-cee6858c1ebb","name":"Validate Schema","type":"n8n-nodes-base.executeWorkflow","position":[2784,368],"parameters":{"options":{},"workflowId":{"__rl":true,"mode":"list","value":"O1tkgab3t4olf3AJ","cachedResultUrl":"/workflow/O1tkgab3t4olf3AJ","cachedResultName":"Param Schema Validation Template"},"workflowInputs":{"value":{"requiredSchema":"={{ \n{\n  \"type\": \"object\",\n  \"properties\": {\n    \"name\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"description\": \"Customer full name\"\n    },\n    \"email\": {\n      \"type\": \"string\",\n      \"pattern\": \"^\\\\S+@\\\\S+\\\\.\\\\S+$\",\n      \"description\": \"Contact email address\"\n    },\n    \"plan\": {\n      \"type\": \"string\",\n      \"enum\": [\"starter\", \"pro\", \"enterprise\"],\n      \"description\": \"Subscription plan\"\n    },\n    \"seat_count\": {\n      \"type\": \"integer\",\n      \"minimum\": 1,\n      \"maximum\": 500,\n      \"description\": \"Number of licensed seats\"\n    },\n    \"tags\": {\n      \"type\": \"array\",\n      \"minItems\": 1,\n      \"items\": {\n        \"type\": \"string\",\n        \"minLength\": 1,\n        \"description\": \"A tag label\"\n      },\n      \"description\": \"At least one tag for categorization\"\n    }\n  },\n  \"required\": [\"name\", \"email\", \"plan\", \"seat_count\"],\n  \"additionalProperties\": false\n} \n}}","paramsToValidate":"={{ $json.body }}"},"schema":[{"id":"requiredSchema","type":"object","display":true,"removed":false,"required":false,"displayName":"requiredSchema","defaultMatch":false,"canBeUsedToMatch":true},{"id":"paramsToValidate","type":"object","display":true,"removed":false,"required":false,"displayName":"paramsToValidate","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":true}},"typeVersion":1.3},{"id":"17f5acfd-bb81-4078-bac2-ce995514f128","name":"Call 'Param Schema Validation Template'","type":"n8n-nodes-base.executeWorkflow","position":[2256,560],"parameters":{"options":{},"workflowId":{"__rl":true,"mode":"list","value":"O1tkgab3t4olf3AJ","cachedResultUrl":"/workflow/O1tkgab3t4olf3AJ","cachedResultName":"Param Schema Validation Template"},"workflowInputs":{"value":{"requiredSchema":"={{ {\n  \"type\": \"object\",\n  \"properties\": {\n    \"email\": {\n      \"type\": \"string\",\n      \"pattern\": \"^\\\\S+@\\\\S+\\\\.\\\\S+$\",\n      \"description\": \"Contact email address\"\n    },\n    \"plan\": {\n      \"type\": \"string\",\n      \"enum\": [\"community\", \"cloud\", \"enterprise\"],\n      \"description\": \"Subscription tier\"\n    },\n    \"seat_count\": {\n      \"type\": \"integer\",\n      \"minimum\": 1,\n      \"maximum\": 500,\n      \"description\": \"Number of licensed seats\"\n    }\n  },\n  \"required\": [\"email\", \"plan\"],\n  \"additionalProperties\": false\n} }}","paramsToValidate":"={{ $json.body }}"},"schema":[{"id":"requiredSchema","type":"object","display":true,"removed":false,"required":false,"displayName":"requiredSchema","defaultMatch":false,"canBeUsedToMatch":true},{"id":"paramsToValidate","type":"object","display":true,"removed":false,"required":false,"displayName":"paramsToValidate","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":true}},"typeVersion":1.3},{"id":"e6e699de-7f68-41cd-a201-5974877bdc5a","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[3232,560],"parameters":{"color":7,"width":512,"height":288,"content":"Then the `error` field gives a detailed readable description of all of the errors:\n\nValidation failed (6 issues):\n• name: Missing required field \\\"name\\\" — Customer full name\n• email: \\\"not-an-email\\\" is not valid — expected: Contact email address\n• plan: \\\"premium\\\" is not an allowed value. Must be one of: starter, pro, enterprise — Subscription plan\n• seat_count: Expected type \\\"integer\\\" but got \\\"non-integer number\\\" — Number of licensed seats\n• tags: Must have at least 1 item(s), got 0 — At least one tag for categorization\n• referral_code: Unknown field \\\"referral_code\\\" is not allowed"},"typeVersion":1},{"id":"5b24fc13-03da-46ec-809c-887c2ad72e86","name":"Sticky Note5","type":"n8n-nodes-base.stickyNote","position":[1232,224],"parameters":{"color":7,"width":640,"height":304,"content":"### The Actual Workflow\nThese two nodes are the actual functional part of the template, all of the other nodes on the canvas are just for demonstration. \n\n"},"typeVersion":1}],"pinData":{"Webhook":[{"body":{"plan":"premium","tags":[],"email":"not-an-email","seat_count":3.5,"referral_code":"ABC123"}}],"Validate Schema":[{"valid":false,"requiredSchema":{"type":"object","required":["name","email","plan","seat_count"],"properties":{"name":{"type":"string","minLength":1,"description":"Customer full name"},"plan":{"enum":["starter","pro","enterprise"],"type":"string","description":"Subscription plan"},"tags":{"type":"array","items":{"type":"string","minLength":1,"description":"A tag label"},"minItems":1,"description":"At least one tag for categorization"},"email":{"type":"string","pattern":"^\\S+@\\S+\\.\\S+$","description":"Contact email address"},"seat_count":{"type":"integer","maximum":500,"minimum":1,"description":"Number of licensed seats"}},"additionalProperties":false},"validationError":"Validation failed (6 issues):\n• name: Missing required field \"name\" — Customer full name\n• email: \"not-an-email\" is not valid — expected: Contact email address\n• plan: \"premium\" is not an allowed value. Must be one of: starter, pro, enterprise — Subscription plan\n• seat_count: Expected type \"integer\" but got \"non-integer number\" — Number of licensed seats\n• tags: Must have at least 1 item(s), got 0 — At least one tag for categorization\n• referral_code: Unknown field \"referral_code\" is not allowed"}],"Return 400 param error":[{"valid":false,"requiredSchema":{"type":"object","required":["name","email","plan","seat_count"],"properties":{"name":{"type":"string","minLength":1,"description":"Customer full name"},"plan":{"enum":["starter","pro","enterprise"],"type":"string","description":"Subscription plan"},"tags":{"type":"array","items":{"type":"string","minLength":1,"description":"A tag label"},"minItems":1,"description":"At least one tag for categorization"},"email":{"type":"string","pattern":"^\\S+@\\S+\\.\\S+$","description":"Contact email address"},"seat_count":{"type":"integer","maximum":500,"minimum":1,"description":"Number of licensed seats"}},"additionalProperties":false},"validationError":"Validation failed (6 issues):\n• name: Missing required field \"name\" — Customer full name\n• email: \"not-an-email\" is not valid — expected: Contact email address\n• plan: \"premium\" is not an allowed value. Must be one of: starter, pro, enterprise — Subscription plan\n• seat_count: Expected type \"integer\" but got \"non-integer number\" — Number of licensed seats\n• tags: Must have at least 1 item(s), got 0 — At least one tag for categorization\n• referral_code: Unknown field \"referral_code\" is not allowed"}],"When Executed by Another Workflow":[{"requiredSchema":{"object":true},"paramsToValidate":{"object":true}}]},"connections":{"Webhook":{"main":[[{"node":"Validate Schema","type":"main","index":0}]]},"If Params Valid":{"main":[[{"node":"Your workflow logic here","type":"main","index":0}],[{"node":"Return 400 param error","type":"main","index":0}]]},"Validate Schema":{"main":[[{"node":"If Params Valid","type":"main","index":0}]]},"Your workflow logic here":{"main":[[{"node":"Return Success Response","type":"main","index":0}]]},"PLACEHOLDER: Source of your data":{"main":[[{"node":"Call 'Param Schema Validation Template'","type":"main","index":0}]]},"When Executed by Another Workflow":{"main":[[{"node":"Schema Validation","type":"main","index":0}]]}}},"lastUpdatedBy":29,"workflowInfo":{"nodeCount":16,"nodeTypes":{"n8n-nodes-base.if":{"count":1},"n8n-nodes-base.set":{"count":1},"n8n-nodes-base.code":{"count":1},"n8n-nodes-base.noOp":{"count":1},"n8n-nodes-base.webhook":{"count":1},"n8n-nodes-base.stickyNote":{"count":6},"n8n-nodes-base.executeWorkflow":{"count":2},"n8n-nodes-base.respondToWebhook":{"count":2},"n8n-nodes-base.executeWorkflowTrigger":{"count":1}}},"status":"published","readyToDemo":null,"user":{"name":"Liam McGarrigle","username":"liammcgarrigle","bio":"Long time n8n power user. \nCurrently, a developer advocate at n8n. ","verified":true,"links":["https://liammc.gg/links"],"avatar":"https://gravatar.com/avatar/9de8a66d9ac1323749387abfd66d066b6203ff2968fc17cf53aaaa96ffd5226a?r=pg&d=retro&size=200"},"nodes":[{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","codex":{"data":{"alias":["Router","Filter","Condition","Logic","Boolean","Branch"],"details":"The IF node can be used to implement binary conditional logic in your workflow. You can set up one-to-many conditions to evaluate each item of data being inputted into the node. That data will either evaluate to TRUE or FALSE and route out of the node accordingly.\n\nThis node has multiple types of conditions: Bool, String, Number, and Date & Time.","resources":{"generic":[{"url":"https://n8n.io/blog/learn-to-automate-your-factorys-incident-reporting-a-step-by-step-guide/","icon":"🏭","label":"Learn to Automate Your Factory's Incident Reporting: A Step by Step Guide"},{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/create-a-toxic-language-detector-for-telegram/","icon":"🤬","label":"Create a toxic language detector for Telegram in 4 step"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/","icon":"🔗","label":"How to build a low-code, self-hosted URL shortener in 3 steps"},{"url":"https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/","icon":"⚙️","label":"Automate your data processing pipeline in 9 steps"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/automation-for-maintainers-of-open-source-projects/","icon":"🏷️","label":"How to automatically manage contributions to open-source projects"},{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/","icon":"🧠","label":"Why this Product Manager loves workflow automation with n8n"},{"url":"https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/","icon":"🙌","label":"Sending Automated Congratulations with Google Sheets, Twilio, and n8n "},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/","icon":"🎖","label":"Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.if/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow"]}}},"group":"[\"transform\"]","defaults":{"name":"If","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"displayName":"If","typeVersion":2,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":26,"icon":"fa:arrow-right","name":"n8n-nodes-base.noOp","codex":{"data":{"alias":["nothing"],"resources":{"generic":[{"url":"https://n8n.io/blog/learn-to-automate-your-factorys-incident-reporting-a-step-by-step-guide/","icon":"🏭","label":"Learn to Automate Your Factory's Incident Reporting: A Step by Step Guide"},{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/create-a-toxic-language-detector-for-telegram/","icon":"🤬","label":"Create a toxic language detector for Telegram in 4 step"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/","icon":"⚙️","label":"Automate your data processing pipeline in 9 steps"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/","icon":"🙌","label":"Sending Automated Congratulations with Google Sheets, Twilio, and n8n "},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/","icon":"🎖","label":"Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.noop/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"organization\"]","defaults":{"name":"No Operation, do nothing","color":"#b0b0b0"},"iconData":{"icon":"arrow-right","type":"icon"},"displayName":"No Operation, do nothing","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":38,"icon":"fa:pen","name":"n8n-nodes-base.set","codex":{"data":{"alias":["Set","JS","JSON","Filter","Transform","Map"],"resources":{"generic":[{"url":"https://n8n.io/blog/learn-to-automate-your-factorys-incident-reporting-a-step-by-step-guide/","icon":"🏭","label":"Learn to Automate Your Factory's Incident Reporting: A Step by Step Guide"},{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/automatically-pulling-and-visualizing-data-with-n8n/","icon":"📈","label":"Automatically pulling and visualizing data with n8n"},{"url":"https://n8n.io/blog/database-monitoring-and-alerting-with-n8n/","icon":"📡","label":"Database Monitoring and Alerting with n8n"},{"url":"https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/","icon":"🧾","label":"Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/","icon":"🔗","label":"How to build a low-code, self-hosted URL shortener in 3 steps"},{"url":"https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/","icon":"⚙️","label":"Automate your data processing pipeline in 9 steps"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/building-an-expense-tracking-app-in-10-minutes/","icon":"📱","label":"Building an expense tracking app in 10 minutes"},{"url":"https://n8n.io/blog/the-ultimate-guide-to-automate-your-video-collaboration-with-whereby-mattermost-and-n8n/","icon":"📹","label":"The ultimate guide to automate your video collaboration with Whereby, Mattermost, and n8n"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/learn-to-build-powerful-api-endpoints-using-webhooks/","icon":"🧰","label":"Learn to Build Powerful API Endpoints Using Webhooks"},{"url":"https://n8n.io/blog/how-a-membership-development-manager-automates-his-work-and-investments/","icon":"📈","label":"How a Membership Development Manager automates his work and investments"},{"url":"https://n8n.io/blog/a-low-code-bitcoin-ticker-built-with-questdb-and-n8n-io/","icon":"📈","label":"A low-code bitcoin ticker built with QuestDB and n8n.io"},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/","icon":"🎖","label":"Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"},{"url":"https://n8n.io/blog/how-goomer-automated-their-operations-with-over-200-n8n-workflows/","icon":"🛵","label":"How Goomer automated their operations with over 200 n8n workflows"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.set/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Data Transformation"]}}},"group":"[\"input\"]","defaults":{"name":"Edit Fields"},"iconData":{"icon":"pen","type":"icon"},"displayName":"Edit Fields (Set)","typeVersion":3,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":47,"icon":"file:webhook.svg","name":"n8n-nodes-base.webhook","codex":{"data":{"alias":["HTTP","API","Build","WH"],"resources":{"generic":[{"url":"https://n8n.io/blog/learn-how-to-automatically-cross-post-your-content-with-n8n/","icon":"✍️","label":"Learn how to automatically cross-post your content with n8n"},{"url":"https://n8n.io/blog/running-n8n-on-ships-an-interview-with-maranics/","icon":"🛳","label":"Running n8n on ships: An interview with Maranics"},{"url":"https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/","icon":"🔗","label":"How to build a low-code, self-hosted URL shortener in 3 steps"},{"url":"https://n8n.io/blog/what-are-apis-how-to-use-them-with-no-code/","icon":" 🪢","label":"What are APIs and how to use them with no code"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/how-a-digital-strategist-uses-n8n-for-online-marketing/","icon":"💻","label":"How a digital strategist uses n8n for online marketing"},{"url":"https://n8n.io/blog/the-ultimate-guide-to-automate-your-video-collaboration-with-whereby-mattermost-and-n8n/","icon":"📹","label":"The ultimate guide to automate your video collaboration with Whereby, Mattermost, and n8n"},{"url":"https://n8n.io/blog/how-to-automatically-give-kudos-to-contributors-with-github-slack-and-n8n/","icon":"👏","label":"How to automatically give kudos to contributors with GitHub, Slack, and n8n"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/","icon":"🧠","label":"Why this Product Manager loves workflow automation with n8n"},{"url":"https://n8n.io/blog/creating-custom-incident-response-workflows-with-n8n/","label":"How to automate every step of an incident response workflow"},{"url":"https://n8n.io/blog/learn-to-build-powerful-api-endpoints-using-webhooks/","icon":"🧰","label":"Learn to Build Powerful API Endpoints Using Webhooks"},{"url":"https://n8n.io/blog/learn-how-to-use-webhooks-with-mattermost-slash-commands/","icon":"🦄","label":"Learn how to use webhooks with Mattermost slash commands"},{"url":"https://n8n.io/blog/how-goomer-automated-their-operations-with-over-200-n8n-workflows/","icon":"🛵","label":"How Goomer automated their operations with over 200 n8n workflows"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"trigger\"]","defaults":{"name":"Webhook"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCI+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTM1IDM3Yy0yLjIgMC00LTEuOC00LTRzMS44LTQgNC00IDQgMS44IDQgNC0xLjggNC00IDQiLz48cGF0aCBmaWxsPSIjMzc0NzRmIiBkPSJNMzUgNDNjLTMgMC01LjktMS40LTcuOC0zLjdsMy4xLTIuNWMxLjEgMS40IDIuOSAyLjMgNC43IDIuMyAzLjMgMCA2LTIuNyA2LTZzLTIuNy02LTYtNmMtMSAwLTIgLjMtMi45LjdsLTEuNyAxTDIzLjMgMTZsMy41LTEuOSA1LjMgOS40YzEtLjMgMi0uNSAzLS41IDUuNSAwIDEwIDQuNSAxMCAxMFM0MC41IDQzIDM1IDQzIi8+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTE0IDQzQzguNSA0MyA0IDM4LjUgNCAzM2MwLTQuNiAzLjEtOC41IDcuNS05LjdsMSAzLjlDOS45IDI3LjkgOCAzMC4zIDggMzNjMCAzLjMgMi43IDYgNiA2czYtMi43IDYtNnYtMmgxNXY0SDIzLjhjLS45IDQuNi01IDgtOS44IDgiLz48cGF0aCBmaWxsPSIjZTkxZTYzIiBkPSJNMTQgMzdjLTIuMiAwLTQtMS44LTQtNHMxLjgtNCA0LTQgNCAxLjggNCA0LTEuOCA0LTQgNCIvPjxwYXRoIGZpbGw9IiMzNzQ3NGYiIGQ9Ik0yNSAxOWMtMi4yIDAtNC0xLjgtNC00czEuOC00IDQtNCA0IDEuOCA0IDQtMS44IDQtNCA0Ii8+PHBhdGggZmlsbD0iI2U5MWU2MyIgZD0ibTE1LjcgMzQtMy40LTIgNS45LTkuN2MtMi0xLjktMy4yLTQuNS0zLjItNy4zIDAtNS41IDQuNS0xMCAxMC0xMHMxMCA0LjUgMTAgMTBjMCAuOS0uMSAxLjctLjMgMi41bC0zLjktMWMuMS0uNS4yLTEgLjItMS41IDAtMy4zLTIuNy02LTYtNnMtNiAyLjctNiA2YzAgMi4xIDEuMSA0IDIuOSA1LjFsMS43IDF6Ii8+PC9zdmc+"},"displayName":"Webhook","typeVersion":2,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":111,"icon":"fa:sign-in-alt","name":"n8n-nodes-base.executeWorkflow","codex":{"data":{"alias":["n8n","call","sub","workflow","sub-workflow","subworkflow"],"details":"The Execute Workflow node can be used when you want your workflow to treat another workflow as a step in your flow. It allows you to modularize your workflows and have a single source of truth for series of actions you perform often. ","resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.executeworkflow/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers","Flow"]}}},"group":"[\"transform\"]","defaults":{"name":"Execute Workflow","color":"#ff6d5a"},"iconData":{"icon":"sign-in-alt","type":"icon"},"displayName":"Execute Sub-workflow","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":535,"icon":"file:webhook.svg","name":"n8n-nodes-base.respondToWebhook","codex":{"data":{"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.respondtowebhook/"}]},"categories":["Core Nodes","Utility"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"transform\"]","defaults":{"name":"Respond to Webhook"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCI+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTM1IDM3Yy0yLjIgMC00LTEuOC00LTRzMS44LTQgNC00IDQgMS44IDQgNC0xLjggNC00IDQiLz48cGF0aCBmaWxsPSIjMzc0NzRmIiBkPSJNMzUgNDNjLTMgMC01LjktMS40LTcuOC0zLjdsMy4xLTIuNWMxLjEgMS40IDIuOSAyLjMgNC43IDIuMyAzLjMgMCA2LTIuNyA2LTZzLTIuNy02LTYtNmMtMSAwLTIgLjMtMi45LjdsLTEuNyAxTDIzLjMgMTZsMy41LTEuOSA1LjMgOS40YzEtLjMgMi0uNSAzLS41IDUuNSAwIDEwIDQuNSAxMCAxMFM0MC41IDQzIDM1IDQzIi8+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTE0IDQzQzguNSA0MyA0IDM4LjUgNCAzM2MwLTQuNiAzLjEtOC41IDcuNS05LjdsMSAzLjlDOS45IDI3LjkgOCAzMC4zIDggMzNjMCAzLjMgMi43IDYgNiA2czYtMi43IDYtNnYtMmgxNXY0SDIzLjhjLS45IDQuNi01IDgtOS44IDgiLz48cGF0aCBmaWxsPSIjZTkxZTYzIiBkPSJNMTQgMzdjLTIuMiAwLTQtMS44LTQtNHMxLjgtNCA0LTQgNCAxLjggNCA0LTEuOCA0LTQgNCIvPjxwYXRoIGZpbGw9IiMzNzQ3NGYiIGQ9Ik0yNSAxOWMtMi4yIDAtNC0xLjgtNC00czEuOC00IDQtNCA0IDEuOCA0IDQtMS44IDQtNCA0Ii8+PHBhdGggZmlsbD0iI2U5MWU2MyIgZD0ibTE1LjcgMzQtMy40LTIgNS45LTkuN2MtMi0xLjktMy4yLTQuNS0zLjItNy4zIDAtNS41IDQuNS0xMCAxMC0xMHMxMCA0LjUgMTAgMTBjMCAuOS0uMSAxLjctLjMgMi41bC0zLjktMWMuMS0uNS4yLTEgLjItMS41IDAtMy4zLTIuNy02LTYtNnMtNiAyLjctNiA2YzAgMi4xIDEuMSA0IDIuOSA1LjFsMS43IDF6Ii8+PC9zdmc+"},"displayName":"Respond to Webhook","typeVersion":2,"nodeCategories":[{"id":7,"name":"Utility"},{"id":9,"name":"Core Nodes"}]},{"id":565,"icon":"fa:sticky-note","name":"n8n-nodes-base.stickyNote","codex":{"data":{"alias":["Comments","Notes","Sticky"],"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"input\"]","defaults":{"name":"Sticky Note","color":"#FFD233"},"iconData":{"icon":"sticky-note","type":"icon"},"displayName":"Sticky Note","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":834,"icon":"file:code.svg","name":"n8n-nodes-base.code","codex":{"data":{"alias":["cpde","Javascript","JS","Python","Script","Custom Code","Function"],"details":"The Code node allows you to execute JavaScript in your workflow.","resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers","Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"Code"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTcxXzQ0MSkiPgo8cGF0aCBkPSJNMTcwLjI4MyA0OEgxOTYuNUMyMDMuMTI3IDQ4IDIwOC41IDQyLjYyNzQgMjA4LjUgMzZWMTJDMjA4LjUgNS4zNzI1OCAyMDMuMTI3IDAgMTk2LjUgMEgxNzAuMjgzQzEyNi4xIDAgOTAuMjgzIDM1LjgxNzIgOTAuMjgzIDgwVjE3NkM5MC4yODMgMjA2LjkyOCA2NS4yMTA5IDIzMiAzNC4yODMgMjMySDIzQzE2LjM3MjYgMjMyIDExIDIzNy4zNzIgMTEgMjQ0VjI2OEMxMSAyNzQuNjI3IDE2LjM3MjQgMjgwIDIyLjk5OTYgMjgwTDM0LjI4MyAyODBDNjUuMjEwOSAyODAgOTAuMjgzIDMwNS4wNzIgOTAuMjgzIDMzNlY0NDBDOTAuMjgzIDQ3OS43NjQgMTIyLjUxOCA1MTIgMTYyLjI4MyA1MTJIMTk2LjVDMjAzLjEyNyA1MTIgMjA4LjUgNTA2LjYyNyAyMDguNSA1MDBWNDc2QzIwOC41IDQ2OS4zNzMgMjAzLjEyNyA0NjQgMTk2LjUgNDY0SDE2Mi4yODNDMTQ5LjAyOCA0NjQgMTM4LjI4MyA0NTMuMjU1IDEzOC4yODMgNDQwVjMzNkMxMzguMjgzIDMwOS4wMjIgMTI4LjAxMSAyODQuNDQzIDExMS4xNjQgMjY1Ljk2MUMxMDYuMTA5IDI2MC40MTYgMTA2LjEwOSAyNTEuNTg0IDExMS4xNjQgMjQ2LjAzOUMxMjguMDExIDIyNy41NTcgMTM4LjI4MyAyMDIuOTc4IDEzOC4yODMgMTc2VjgwQzEzOC4yODMgNjIuMzI2OSAxNTIuNjEgNDggMTcwLjI4MyA0OFoiIGZpbGw9IiNGRjk5MjIiLz4KPHBhdGggZD0iTTMwNSAzNkMzMDUgNDIuNjI3NCAzMTAuMzczIDQ4IDMxNyA0OEgzNDIuOTc5QzM2MC42NTIgNDggMzc0Ljk3OCA2Mi4zMjY5IDM3NC45NzggODBWMTc2QzM3NC45NzggMjAyLjk3OCAzODUuMjUxIDIyNy41NTcgNDAyLjA5OCAyNDYuMDM5QzQwNy4xNTMgMjUxLjU4NCA0MDcuMTUzIDI2MC40MTYgNDAyLjA5OCAyNjUuOTYxQzM4NS4yNTEgMjg0LjQ0MyAzNzQuOTc4IDMwOS4wMjIgMzc0Ljk3OCAzMzZWNDMyQzM3NC45NzggNDQ5LjY3MyAzNjAuNjUyIDQ2NCAzNDIuOTc5IDQ2NEgzMTdDMzEwLjM3MyA0NjQgMzA1IDQ2OS4zNzMgMzA1IDQ3NlY1MDBDMzA1IDUwNi42MjcgMzEwLjM3MyA1MTIgMzE3IDUxMkgzNDIuOTc5QzM4Ny4xNjEgNTEyIDQyMi45NzggNDc2LjE4MyA0MjIuOTc4IDQzMlYzMzZDNDIyLjk3OCAzMDUuMDcyIDQ0OC4wNTEgMjgwIDQ3OC45NzkgMjgwSDQ5MEM0OTYuNjI3IDI4MCA1MDIgMjc0LjYyOCA1MDIgMjY4VjI0NEM1MDIgMjM3LjM3MyA0OTYuNjI4IDIzMiA0OTAgMjMyTDQ3OC45NzkgMjMyQzQ0OC4wNTEgMjMyIDQyMi45NzggMjA2LjkyOCA0MjIuOTc4IDE3NlY4MEM0MjIuOTc4IDM1LjgxNzIgMzg3LjE2MSAwIDM0Mi45NzkgMEgzMTdDMzEwLjM3MyAwIDMwNSA1LjM3MjU4IDMwNSAxMlYzNloiIGZpbGw9IiNGRjk5MjIiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTcxXzQ0MSI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="},"displayName":"Code","typeVersion":2,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":837,"icon":"fa:sign-out-alt","name":"n8n-nodes-base.executeWorkflowTrigger","codex":{"data":{"resources":{"generic":[],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.executeworkflowtrigger/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"trigger\"]","defaults":{"name":"When Executed by Another Workflow","color":"#ff6d5a"},"iconData":{"icon":"sign-out-alt","type":"icon"},"displayName":"Execute Workflow Trigger","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]}],"categories":[{"id":35,"name":"Document Extraction"}],"image":[]}}