<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pirate Prentice</title>
    <description>The latest articles on DEV Community by Pirate Prentice (@pirateprentice).</description>
    <link>https://dev.to/pirateprentice</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4001338%2Ffd7807bc-c003-4274-b68c-a54b2f2e83e4.png</url>
      <title>DEV Community: Pirate Prentice</title>
      <link>https://dev.to/pirateprentice</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pirateprentice"/>
    <language>en</language>
    <item>
      <title>n8n GitHub Node: Automate Issues, Pull Requests, and Releases in Your Workflows [Free Workflow JSON]</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Thu, 16 Jul 2026 07:22:17 +0000</pubDate>
      <link>https://dev.to/pirateprentice/n8n-github-node-automate-issues-pull-requests-and-releases-in-your-workflows-free-workflow-json-30mn</link>
      <guid>https://dev.to/pirateprentice/n8n-github-node-automate-issues-pull-requests-and-releases-in-your-workflows-free-workflow-json-30mn</guid>
      <description>&lt;p&gt;If your team lives in GitHub, you're already doing manual work that n8n can eliminate: pasting issue links into Slack, copying PR details into project trackers, sending release notes to customers, and keeping spreadsheets in sync with repo activity. The n8n GitHub node connects your repositories to every other tool in your stack—without webhooks you have to maintain, without Zapier's per-task pricing, and without writing GitHub API boilerplate.&lt;/p&gt;

&lt;p&gt;This guide covers every GitHub node operation, the six most common gotchas, three production-ready workflow patterns, and a free downloadable JSON you can import directly into n8n.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the n8n GitHub Node Can Do
&lt;/h2&gt;

&lt;p&gt;The GitHub node in n8n wraps the GitHub REST API. Here's what you can read, create, and update:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issues&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create Issue: open a new issue with title, body, labels, assignees, milestone&lt;/li&gt;
&lt;li&gt;Edit Issue: update state (open/closed), labels, assignees, title, body&lt;/li&gt;
&lt;li&gt;Get Issue / Get All Issues: fetch by number or list with filters (state, label, assignee, since)&lt;/li&gt;
&lt;li&gt;Lock / Unlock Issue: restrict or restore comments&lt;/li&gt;
&lt;li&gt;Create Comment: post a comment on an existing issue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pull Requests&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create PR: open a pull request from branch to base&lt;/li&gt;
&lt;li&gt;Get PR / Get All PRs: list open/closed/merged PRs with filters&lt;/li&gt;
&lt;li&gt;Get PR Files: list changed files and their diffs&lt;/li&gt;
&lt;li&gt;Get PR Reviews: fetch review status and comments&lt;/li&gt;
&lt;li&gt;Update PR: change title, body, state, or base branch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get Repository: fetch repo metadata (stars, forks, topics, default branch)&lt;/li&gt;
&lt;li&gt;List Repositories: list repos for a user or org&lt;/li&gt;
&lt;li&gt;Get File: read file content from any branch/commit (base64 decoded)&lt;/li&gt;
&lt;li&gt;Create / Edit File: commit a file change directly (useful for config updates, README patches)&lt;/li&gt;
&lt;li&gt;Delete File: remove a file via commit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Releases&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create Release: publish a new release with tag, name, body, draft/prerelease flags&lt;/li&gt;
&lt;li&gt;Get Release / Get All Releases: list releases with assets&lt;/li&gt;
&lt;li&gt;Update Release: edit body, name, draft state&lt;/li&gt;
&lt;li&gt;Delete Release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Triggers (GitHub Trigger node)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fires on: push, pull_request, issues, issue_comment, release, create, delete, workflow_run, and more&lt;/li&gt;
&lt;li&gt;Configure via n8n's built-in webhook URL—no manual GitHub webhook setup needed&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Credentials Setup
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Generate a &lt;strong&gt;Personal Access Token (PAT)&lt;/strong&gt; at GitHub → Settings → Developer settings → Personal access tokens (classic or fine-grained)&lt;/li&gt;
&lt;li&gt;Scopes needed: &lt;code&gt;repo&lt;/code&gt; (full control of private repos) or &lt;code&gt;public_repo&lt;/code&gt; for public-only access. For org repos, &lt;code&gt;read:org&lt;/code&gt; may also be needed.&lt;/li&gt;
&lt;li&gt;In n8n: Credentials → New → GitHub → paste the PAT&lt;/li&gt;
&lt;li&gt;Fine-grained tokens (2024+): scope to specific repos and permissions for least-privilege access&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Six Common Gotchas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Base64-encoded file content&lt;/strong&gt;&lt;br&gt;
The "Get File" operation returns content as base64. Add a Code node to decode: &lt;code&gt;Buffer.from($input.item.json.content, 'base64').toString('utf-8')&lt;/code&gt;. Forgetting this gives you garbled output in downstream nodes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Pagination on "Get All" operations&lt;/strong&gt;&lt;br&gt;
n8n's GitHub node handles pagination automatically when "Return All" is enabled—but large repos (1,000+ issues) can be slow and hit rate limits. Set filters (state, label, since date) to narrow the result set before enabling Return All.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Rate limits: 5,000 requests/hour per authenticated user&lt;/strong&gt;&lt;br&gt;
Authenticated requests get 5,000/hr; unauthenticated get 60/hr. Bulk operations over large repos can exhaust this. Add error handling and exponential backoff in a Code node, or schedule heavy pulls during off-hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. GitHub Trigger requires a public webhook endpoint&lt;/strong&gt;&lt;br&gt;
The GitHub Trigger node needs a publicly reachable webhook URL. On a local n8n instance, you'll need an ngrok tunnel or cloud n8n. The trigger auto-registers the webhook in your repo settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. SHA required for file updates&lt;/strong&gt;&lt;br&gt;
The "Edit File" operation requires the current file's SHA (returned by "Get File"). If you skip this step and pass a stale or missing SHA, the operation fails with a 409 conflict.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Organization repos vs. personal repos&lt;/strong&gt;&lt;br&gt;
Some operations require specifying the org name as the "Owner" (not your username). When accessing org repos, confirm the PAT has &lt;code&gt;read:org&lt;/code&gt; scope and that the org allows PATs.&lt;/p&gt;


&lt;h2&gt;
  
  
  Three Production-Ready Workflow Patterns
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Pattern 1: GitHub Issue → Slack + Linear Triage Bot
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; New issues pile up untracked. Team doesn't know which issues are high-priority until someone manually reviews them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GitHub Trigger: fires on &lt;code&gt;issues&lt;/code&gt; event (action: opened)&lt;/li&gt;
&lt;li&gt;AI Node (OpenAI/Anthropic): classify the issue as bug/feature/question, assign severity 1-3 based on title + body&lt;/li&gt;
&lt;li&gt;IF node: if severity = 1 (critical bug)

&lt;ul&gt;
&lt;li&gt;Slack node: post to #incidents with issue link, title, and classification&lt;/li&gt;
&lt;li&gt;Linear node (HTTP Request): create a Linear issue in the "Critical" team with GitHub link&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;IF node else: post to #triage Slack channel for async review&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Zero manual triage. Critical bugs get immediate team visibility. Non-critical issues queue in Slack for async review. No context switching between GitHub and project tools.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pattern 2: Release Notes Auto-Publisher
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Every release requires someone to manually copy the GitHub release body into Slack, email subscribers, and update a changelog doc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GitHub Trigger: fires on &lt;code&gt;release&lt;/code&gt; event (action: published)&lt;/li&gt;
&lt;li&gt;Filter node: skip prereleases (check &lt;code&gt;$input.item.json.body.prerelease === true&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Parallel branches:

&lt;ul&gt;
&lt;li&gt;Branch A: Slack node → post formatted release summary to #announcements&lt;/li&gt;
&lt;li&gt;Branch B: Brevo/Resend node → send release email to opted-in changelog subscribers&lt;/li&gt;
&lt;li&gt;Branch C: Google Sheets node → append version, date, and summary to a changelog Sheet&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GitHub node: Create Comment on all issues/PRs that reference this release milestone (via search)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Releases trigger a full distribution chain with no manual steps. Every stakeholder is updated through their preferred channel within seconds of the tag being published.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pattern 3: PR Quality Gate Notifier
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; PRs sit unreviewed for days. Large PRs (500+ lines) get rubber-stamped. No visibility into review queue health.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Schedule Trigger: runs every day at 9 AM&lt;/li&gt;
&lt;li&gt;GitHub node: Get All PRs (state: open) for your repository&lt;/li&gt;
&lt;li&gt;Code node: calculate age in hours, count changed files from PR metadata&lt;/li&gt;
&lt;li&gt;Item Lists node: aggregate and sort by age descending&lt;/li&gt;
&lt;li&gt;IF node: filter PRs older than 48 hours OR with &amp;gt;10 files changed (high review burden)&lt;/li&gt;
&lt;li&gt;Slack node: post a daily digest to #engineering — "PRs needing attention" list with author, age, file count, link&lt;/li&gt;
&lt;li&gt;IF node (&amp;gt;7 days old): send a direct Slack DM to the PR author asking for a status update or close intent&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Surfaces stale reviews and oversized PRs before they become merge bottlenecks. The daily digest creates a lightweight standup artifact without any manual compilation.&lt;/p&gt;


&lt;h2&gt;
  
  
  Free Workflow JSON
&lt;/h2&gt;

&lt;p&gt;Import this into n8n (Settings → Import Workflow) to get the PR Quality Gate Notifier pre-wired. Update the repository owner/name, Slack channel, and your alert thresholds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GitHub PR Quality Gate Notifier"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"interval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cronExpression"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expression"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0 9 * * 1-5"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Schedule Trigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.scheduleTrigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pullRequest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"getAll"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_ORG_OR_USER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"repository"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_REPO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"returnAll"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"filters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Get Open PRs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.github"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;450&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"jsCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"const now = Date.now();&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;return $input.all().map(item =&amp;gt; {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  const pr = item.json;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  const ageHours = (now - new Date(pr.created_at).getTime()) / 3600000;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  return {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    json: {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      number: pr.number,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      title: pr.title,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      author: pr.user.login,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      url: pr.html_url,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      ageHours: Math.round(ageHours),&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      changedFiles: pr.changed_files || 0,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      stale: ageHours &amp;gt; 48,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;      oversized: (pr.changed_files || 0) &amp;gt; 10&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    }&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  };&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;});"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enrich PRs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;650&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"conditions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"boolean"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"value1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{$json.stale || $json.oversized}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Needs Attention?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;850&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#engineering"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"=:github: *PR needs attention:* &amp;lt;{{$json.url}}|#{{$json.number}} {{$json.title}}&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Author: @{{$json.author}} | Age: {{$json.ageHours}}h | Files: {{$json.changedFiles}}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Slack Alert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.slack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1050&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Schedule Trigger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Get Open PRs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}]]},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Get Open PRs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enrich PRs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}]]},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Enrich PRs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Needs Attention?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}]]},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Needs Attention?"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Slack Alert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]]}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  GitHub Node vs. HTTP Request Node
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;GitHub node&lt;/th&gt;
&lt;th&gt;HTTP Request node&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Auth setup&lt;/td&gt;
&lt;td&gt;PAT via n8n credential&lt;/td&gt;
&lt;td&gt;Manual header&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coverage&lt;/td&gt;
&lt;td&gt;Issues, PRs, releases, files&lt;/td&gt;
&lt;td&gt;Full GitHub API (GraphQL too)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pagination&lt;/td&gt;
&lt;td&gt;Auto (Return All)&lt;/td&gt;
&lt;td&gt;Manual cursor handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ease of use&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use the GitHub node for the operations it covers. Drop to HTTP Request for GraphQL queries, Actions API, or endpoints not yet in the node.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;The GitHub node turns your repository into a live data source for every other tool in your stack. Issues become Linear tickets. Releases become Slack announcements and changelog emails. PRs become engineering digest entries. None of this requires a custom integration service—just an n8n workflow and a PAT.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Want this built for your repo but don't have time to wire it up? The &lt;a href="https://buy.stripe.com/4gMdRaetT3Yv3yxbOs2go03" rel="noopener noreferrer"&gt;Done-For-You n8n Workflow Build&lt;/a&gt; service ($99) delivers a production-ready GitHub automation tailored to your team's workflow within 3 business days.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Or grab the &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;n8n Workflow Automation Pack&lt;/a&gt; ($29) for 10+ pre-built workflow templates including GitHub integrations.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;More n8n guides: &lt;a href="https://dev.to/pirateprentice"&gt;pirateprentice on dev.to&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>github</category>
      <category>automation</category>
      <category>devops</category>
    </item>
    <item>
      <title>n8n Shopify Node: Automate Orders, Customers, and Inventory in Your Workflows [Free Workflow JSON]</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Wed, 15 Jul 2026 07:21:41 +0000</pubDate>
      <link>https://dev.to/pirateprentice/n8n-shopify-node-automate-orders-customers-and-inventory-in-your-workflows-free-workflow-json-2h5d</link>
      <guid>https://dev.to/pirateprentice/n8n-shopify-node-automate-orders-customers-and-inventory-in-your-workflows-free-workflow-json-2h5d</guid>
      <description>&lt;h1&gt;
  
  
  n8n Shopify Node: Automate Orders, Customers, and Inventory in Your Workflows [Free Workflow JSON]
&lt;/h1&gt;

&lt;p&gt;If you're running a Shopify store, you're already drowning in repetitive tasks: exporting orders to spreadsheets, tagging customers by purchase history, emailing suppliers when stock runs low, and reconciling payments in your accounting tool. The n8n Shopify node connects your store to every other tool in your stack—without middleware, without Zapier's per-task pricing, and without writing custom API code.&lt;/p&gt;

&lt;p&gt;This guide covers every Shopify node operation, the six most common gotchas, three production-ready workflow patterns, and a free downloadable JSON you can import directly into n8n.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the n8n Shopify Node Can Do
&lt;/h2&gt;

&lt;p&gt;The Shopify node in n8n covers the core Shopify Admin REST API resources. Here's what you can read, create, and update:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Orders&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get Order: fetch a single order by ID&lt;/li&gt;
&lt;li&gt;Get All Orders: list orders with filters (status, date range, fulfillment status, financial status)&lt;/li&gt;
&lt;li&gt;Create Order: programmatically create an order (useful for phone/offline orders)&lt;/li&gt;
&lt;li&gt;Update Order: change tags, notes, or metafields on existing orders&lt;/li&gt;
&lt;li&gt;Delete Order: remove a draft order&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Customers&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get Customer / Get All Customers: retrieve customer records with purchase history&lt;/li&gt;
&lt;li&gt;Create Customer: add new customers to your Shopify CRM&lt;/li&gt;
&lt;li&gt;Update Customer: add tags, notes, addresses, or metafields&lt;/li&gt;
&lt;li&gt;Delete Customer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Products&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get Product / Get All Products: list products with variant details, inventory levels, images&lt;/li&gt;
&lt;li&gt;Create Product / Update Product / Delete Product&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Triggers (Shopify Trigger node)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order Created / Updated / Paid / Fulfilled / Cancelled / Partially Fulfilled&lt;/li&gt;
&lt;li&gt;Customer Created / Updated&lt;/li&gt;
&lt;li&gt;Product Created / Updated / Deleted&lt;/li&gt;
&lt;li&gt;Checkout Created / Updated&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A Shopify store (any plan — even development stores work for testing)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Custom App&lt;/strong&gt; with the scopes your workflow needs&lt;/li&gt;
&lt;li&gt;The Admin API access token from that app&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Creating the credential in n8n
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In Shopify Admin: &lt;strong&gt;Settings → Apps and sales channels → Develop apps → Create an app&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Configure API scopes:

&lt;ul&gt;
&lt;li&gt;Orders: &lt;code&gt;read_orders&lt;/code&gt;, &lt;code&gt;write_orders&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Customers: &lt;code&gt;read_customers&lt;/code&gt;, &lt;code&gt;write_customers&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Products: &lt;code&gt;read_products&lt;/code&gt;, &lt;code&gt;write_products&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Inventory: &lt;code&gt;read_inventory&lt;/code&gt;, &lt;code&gt;write_inventory&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Install the app → copy the &lt;strong&gt;Admin API access token&lt;/strong&gt; (shown only once)&lt;/li&gt;
&lt;li&gt;In n8n: &lt;strong&gt;Credentials → New → Shopify API&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Shop subdomain: &lt;code&gt;your-store&lt;/code&gt; (not &lt;code&gt;your-store.myshopify.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Access token: paste the token&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scope tip:&lt;/strong&gt; Request only the scopes your workflow actually needs. Shopify logs scope usage — over-permissioned apps get flagged in security reviews.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Six Gotchas That Will Bite You
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The API rate limit is 40 requests/second — not per minute
&lt;/h3&gt;

&lt;p&gt;Shopify's REST API uses a leaky-bucket rate limiter: 40 requests/second per store, replenishing at 2/second. For bulk operations (Get All Orders, syncing large product catalogs), n8n will hit this fast. Add a &lt;strong&gt;Wait node&lt;/strong&gt; (500ms–1000ms delay) between batches, or use &lt;strong&gt;Split In Batches&lt;/strong&gt; with a 10-item chunk and a pause. Hitting the limit returns &lt;code&gt;429 Too Many Requests&lt;/code&gt; and n8n will retry — but repeated retries can cascade into longer delays.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. "Get All" has a default limit of 250 records — pagination is manual
&lt;/h3&gt;

&lt;p&gt;The Shopify node's Get All operation fetches up to 250 records per call and doesn't auto-paginate. For stores with thousands of orders or customers, you need to implement cursor-based pagination manually using &lt;code&gt;page_info&lt;/code&gt; from the response headers or switch to the &lt;strong&gt;Shopify GraphQL Admin API&lt;/strong&gt; (via the HTTP Request node) which supports proper pagination. For most small-to-mid stores, 250 is enough for recent records.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Order financial_status vs fulfillment_status — they're independent
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;financial_status&lt;/code&gt; (pending, paid, refunded, partially_refunded) and &lt;code&gt;fulfillment_status&lt;/code&gt; (fulfilled, partial, unfulfilled) are separate fields. A common mistake: filtering for &lt;code&gt;status=any&lt;/code&gt; to get "all paid unfulfilled orders" — this requires filtering on &lt;em&gt;both&lt;/em&gt; fields. Use Get All Orders with &lt;code&gt;financial_status=paid&lt;/code&gt; and then a Filter node to check &lt;code&gt;fulfillment_status != 'fulfilled'&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Webhooks need a public URL — use ngrok or a cloud n8n instance for development
&lt;/h3&gt;

&lt;p&gt;The Shopify Trigger node registers a webhook with Shopify, which requires a publicly reachable URL. &lt;code&gt;localhost:5678&lt;/code&gt; won't work — Shopify can't reach it. Options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;n8n Cloud&lt;/strong&gt;: URL is already public&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-hosted on a VPS&lt;/strong&gt;: public IP + the n8n webhook path works&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local dev&lt;/strong&gt;: use ngrok (&lt;code&gt;ngrok http 5678&lt;/code&gt;) — but the tunnel URL changes on restart, which re-registers the webhook&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Webhook deduplication is your responsibility
&lt;/h3&gt;

&lt;p&gt;Shopify guarantees &lt;strong&gt;at-least-once delivery&lt;/strong&gt;, not exactly-once. During high-traffic events (flash sales, Black Friday), the same &lt;code&gt;order.paid&lt;/code&gt; webhook can fire twice. Build deduplication into your workflow: use a Redis Set node or a Google Sheet to check &lt;code&gt;order_id&lt;/code&gt; before processing. Without this, you'll send duplicate fulfillment emails, double-count revenue in sheets, or create duplicate CRM records.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Customer email is not always present
&lt;/h3&gt;

&lt;p&gt;Guest checkouts in Shopify can produce orders where &lt;code&gt;customer.email&lt;/code&gt; is null (the email lives at &lt;code&gt;order.email&lt;/code&gt; instead). If your workflow sends confirmation or fulfillment emails, always fall back to &lt;code&gt;{{ $json.order.email || $json.customer.email }}&lt;/code&gt; — never assume &lt;code&gt;customer.email&lt;/code&gt; is populated.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 1: Order → Google Sheets Revenue Sync
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; Append every paid Shopify order to a Google Sheet for daily revenue tracking without exporting CSVs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trigger:&lt;/strong&gt; Shopify Trigger node → Event: &lt;code&gt;order/paid&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shopify Trigger (order/paid)
  → Set node: extract order_id, created_at, total_price, customer.email, line_items count, financial_status
  → Google Sheets (Append Row): sheet "Orders Log", columns: Date | Order ID | Customer Email | Total | Items | Status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; The &lt;code&gt;order/paid&lt;/code&gt; webhook fires the instant payment is confirmed — not when the order is created (which may be unpaid). This avoids syncing abandoned checkouts or pending orders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dedup guard:&lt;/strong&gt; Add a Google Sheets lookup (Get Row) before the append to check if &lt;code&gt;order_id&lt;/code&gt; already exists in column B. If it does, stop execution. This handles Shopify's at-least-once delivery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free JSON:&lt;/strong&gt; [included at end of article]&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 2: Low-Stock Alert → Slack + Supplier Email
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; When a product variant drops below a stock threshold, alert the team on Slack and email the supplier automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trigger:&lt;/strong&gt; Schedule Trigger → every 6 hours (or Shopify Trigger → &lt;code&gt;product/updated&lt;/code&gt; if you want real-time)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Schedule Trigger (every 6h)
  → Shopify (Get All Products): limit=250
  → Split Out (operation: Split Out, field: variants)
  → Filter: inventory_quantity &amp;lt; 10 AND inventory_management == "shopify"
  → IF: no items pass filter → stop
  → Set node: build alert message with title, SKU, quantity, product URL
  → Slack (Send Message): channel #ops, message "⚠️ Low stock: {title} SKU {sku} — {qty} remaining"
  → Gmail (Send Email): to supplier@example.com, subject "Reorder needed: {title}", body with qty + Shopify admin link
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Threshold config tip:&lt;/strong&gt; Store the threshold (10) in a Set node at the top so you can change it without editing the Filter. Or use a Google Sheet as a config store — one row per SKU with its reorder point.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 3: New Order → CRM Contact Sync + Tag Enrichment
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; When a customer places their first order, create or update their record in HubSpot (or Airtable), add a "buyer" tag in Shopify, and trigger a post-purchase email sequence in Brevo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trigger:&lt;/strong&gt; Shopify Trigger → &lt;code&gt;order/paid&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shopify Trigger (order/paid)
  → IF: customer.orders_count == 1  (first-time buyer)
    YES →
      → HubSpot (Create/Update Contact): email, first/last name, "buyer" lifecycle stage
      → Shopify (Update Customer): add tag "buyer-first-order"
      → Brevo (Add Contact to List): list "post-purchase-sequence-new"
    NO → (repeat buyer path)
      → HubSpot (Update Contact): increment purchase_count property
      → IF: orders_count == 3
        YES → Shopify (Update Customer): add tag "loyal-customer"
        YES → Brevo (Add to List): "vip-sequence"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why tag in Shopify too:&lt;/strong&gt; Tags in Shopify make it easy to segment customers for future discount campaigns directly inside Shopify without needing to query your CRM.&lt;/p&gt;




&lt;h2&gt;
  
  
  Shopify Node vs HTTP Request Node (GraphQL)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Shopify Node&lt;/th&gt;
&lt;th&gt;HTTP Request (GraphQL)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup complexity&lt;/td&gt;
&lt;td&gt;Low — credential + select operation&lt;/td&gt;
&lt;td&gt;Medium — GraphQL query syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pagination&lt;/td&gt;
&lt;td&gt;Manual (250/page limit)&lt;/td&gt;
&lt;td&gt;Cursor-based (proper pagination)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bulk exports (10K+ records)&lt;/td&gt;
&lt;td&gt;Impractical&lt;/td&gt;
&lt;td&gt;✅ via Bulk Operations API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metafields&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;✅ Full access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom storefronts&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ Storefront API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Triggers + small CRUD ops&lt;/td&gt;
&lt;td&gt;Large catalog syncs, complex queries&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For most automation workflows (order events, customer tagging, stock checks), the native Shopify node is sufficient. Switch to GraphQL via HTTP Request only when you need bulk exports or metafield depth.&lt;/p&gt;




&lt;h2&gt;
  
  
  Free Workflow JSON
&lt;/h2&gt;

&lt;p&gt;Import this directly into n8n (Settings → Import from clipboard):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Shopify Order Revenue Sync + Low Stock Alert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"order/paid"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Shopify Trigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.shopifyTrigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"assignments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"assignments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"order_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.id }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.created_at }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"total_price"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.total_price }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer_email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.email }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"item_count"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.line_items.length }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"financial_status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.financial_status }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Extract Fields"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;450&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"append"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"documentId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"__rl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_SHEET_ID"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sheetName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"__rl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Orders Log"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"columns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"mappingMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"defineBelow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.created_at }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Order ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.order_id }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Customer Email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.customer_email }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.total_price }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.item_count }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.financial_status }}"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Log to Google Sheets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.googleSheets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;650&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Shopify Trigger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Extract Fields"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Extract Fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Log to Google Sheets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace &lt;code&gt;YOUR_SHEET_ID&lt;/code&gt; with your Google Sheets document ID. Add a Google Sheets credential and a Shopify API credential before running.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Where to Go From Here
&lt;/h2&gt;

&lt;p&gt;The Shopify node handles the most common e-commerce automations well. For production stores, consider these additions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inventory webhooks&lt;/strong&gt; (&lt;code&gt;inventory_levels/update&lt;/code&gt;) for real-time stock alerts instead of polling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fulfillment automation&lt;/strong&gt;: when an order is marked paid, trigger a fulfillment request to your 3PL via HTTP Request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abandoned checkout recovery&lt;/strong&gt;: use the &lt;code&gt;checkouts/create&lt;/code&gt; trigger to start a Brevo sequence for carts left behind&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metafield sync&lt;/strong&gt;: use the HTTP Request node (GraphQL) to read/write product metafields for custom attribute management&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Get the Pre-Built n8n Workflow Pack
&lt;/h2&gt;

&lt;p&gt;The free JSON above gives you the basics. If you want a complete &lt;strong&gt;Shopify automation bundle&lt;/strong&gt; — order sync, customer tagging, low-stock alerts, and post-purchase sequences — all pre-wired and documented:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;n8n Workflow Starter Pack ($29) — pirateprentice.gumroad.com/l/sxcoe&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Or if you'd rather have it built specifically for your store:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://buy.stripe.com/4gMdRaetT3Yv3yxbOs2go03" rel="noopener noreferrer"&gt;Done-For-You n8n Workflow Build ($99) — buy.stripe.com/4gMdRaetT3Yv3yxbOs2go03&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Send your store setup and what you want automated — I'll deliver a working n8n workflow within 48 hours.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>shopify</category>
      <category>automation</category>
      <category>webdev</category>
    </item>
    <item>
      <title>n8n HubSpot Node: Sync Contacts, Deals, and Companies in Your Workflows (Free Workflow JSON)</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Tue, 14 Jul 2026 07:22:40 +0000</pubDate>
      <link>https://dev.to/pirateprentice/n8n-hubspot-node-sync-contacts-deals-and-companies-in-your-workflows-free-workflow-json-5fn9</link>
      <guid>https://dev.to/pirateprentice/n8n-hubspot-node-sync-contacts-deals-and-companies-in-your-workflows-free-workflow-json-5fn9</guid>
      <description>&lt;h1&gt;
  
  
  n8n HubSpot Node: Sync Contacts, Deals, and Companies in Your Workflows (Free Workflow JSON)
&lt;/h1&gt;

&lt;p&gt;If you're managing a CRM in HubSpot and wish your automation workflows could reach in and pull, push, or update records without manual exports — the n8n HubSpot node is your answer.&lt;/p&gt;

&lt;p&gt;This guide covers every operation you'll actually use, six gotchas that bite people, three real-world workflow patterns, and a free downloadable JSON so you can start in minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the HubSpot Node Does
&lt;/h2&gt;

&lt;p&gt;The HubSpot node connects n8n to the HubSpot CRM API. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read, create, update, and delete&lt;/strong&gt; contacts, companies, deals, and tickets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search&lt;/strong&gt; any CRM object with filters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manage associations&lt;/strong&gt; between objects (e.g., link a contact to a deal)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upsert&lt;/strong&gt; records — create if not found, update if found&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Authentication is via a HubSpot Private App token (OAuth2 is also supported for multi-account setups).&lt;/p&gt;




&lt;h2&gt;
  
  
  Operations Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Contact Operations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Create&lt;/td&gt;
&lt;td&gt;Add a new contact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get&lt;/td&gt;
&lt;td&gt;Fetch by ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get Many&lt;/td&gt;
&lt;td&gt;List contacts with optional filters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update&lt;/td&gt;
&lt;td&gt;Update any property&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;Archive contact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search&lt;/td&gt;
&lt;td&gt;Filter by email, name, property value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upsert&lt;/td&gt;
&lt;td&gt;Create or update by email&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Company Operations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create, Get, Get Many, Update, Delete, Search&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deal Operations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create, Get, Get Many, Update, Delete, Search&lt;/li&gt;
&lt;li&gt;Set deal stage, associate with contacts/companies&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ticket Operations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create, Get, Get Many, Update, Delete, Search&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Association Operations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create Association (link contact ↔ deal, company ↔ deal, etc.)&lt;/li&gt;
&lt;li&gt;Get Associations&lt;/li&gt;
&lt;li&gt;Delete Association&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Authentication: Private App Token
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In HubSpot → Settings → Integrations → Private Apps&lt;/li&gt;
&lt;li&gt;Create a new app, select the scopes you need (contacts read/write, deals read/write, etc.)&lt;/li&gt;
&lt;li&gt;Copy the token → paste into n8n as a HubSpot credential (Bearer Token type)&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scope tip:&lt;/strong&gt; Request only the scopes your workflow needs. Least-privilege reduces blast radius if the token leaks.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Six Gotchas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Property names are internal names, not labels.&lt;/strong&gt;&lt;br&gt;
HubSpot displays "First Name" in the UI but the API property name is &lt;code&gt;firstname&lt;/code&gt;. Use HubSpot's property settings page to look up internal names before building your workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Search returns max 100 results per page.&lt;/strong&gt;&lt;br&gt;
The Search operation supports pagination via &lt;code&gt;after&lt;/code&gt; cursor. For large lists, loop with a Code node until &lt;code&gt;paging.next.after&lt;/code&gt; is null.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Upsert deduplicates by email only.&lt;/strong&gt;&lt;br&gt;
The built-in Upsert uses email as the unique key. If you need to deduplicate by phone or custom ID, use Search → IF → Create/Update manually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Associations require separate requests.&lt;/strong&gt;&lt;br&gt;
Creating a deal doesn't automatically associate it with a contact. You must use the Association operation as a follow-up step — plan for two nodes per new deal creation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Rate limits are per-second AND per-day.&lt;/strong&gt;&lt;br&gt;
HubSpot's API enforces 100 requests/10 seconds for Private Apps and 250K daily. For bulk syncs, add a Wait node (1s) between batches to avoid 429 errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Deleted records are archived, not permanently gone.&lt;/strong&gt;&lt;br&gt;
HubSpot's "Delete" archives records. They can be restored. If you're building a GDPR deletion flow, verify the record is permanently deleted via HubSpot's UI or a separate API call — n8n's Delete maps to the archive endpoint.&lt;/p&gt;




&lt;h2&gt;
  
  
  Workflow Pattern 1: Typeform Lead → HubSpot Contact + Deal
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Trigger:&lt;/strong&gt; Typeform Webhook (new form submission)&lt;br&gt;
&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Typeform Trigger node → extract name, email, company, budget&lt;/li&gt;
&lt;li&gt;HubSpot node → Upsert Contact (email dedup)&lt;/li&gt;
&lt;li&gt;HubSpot node → Create Deal (associate with contact ID from step 2)&lt;/li&gt;
&lt;li&gt;HubSpot node → Create Association (contact ↔ deal)&lt;/li&gt;
&lt;li&gt;Gmail/Resend → send confirmation email to lead&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Every inbound lead lands in HubSpot automatically, with a deal created and associated in the same workflow — no manual CRM entry.&lt;/p&gt;




&lt;h2&gt;
  
  
  Workflow Pattern 2: HubSpot Deal Stage Change → Slack Alert + Google Sheet Log
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Trigger:&lt;/strong&gt; Schedule Trigger (every 15 min) or HubSpot webhook (requires HubSpot subscription webhook setup)&lt;br&gt;
&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;HubSpot node → Search Deals where &lt;code&gt;dealstage&lt;/code&gt; changed in last 15 minutes (use &lt;code&gt;lastmodifieddate&lt;/code&gt; filter)&lt;/li&gt;
&lt;li&gt;IF node → filter for deals moving to "Closed Won"&lt;/li&gt;
&lt;li&gt;Slack node → post to #sales channel ("🎉 Deal closed: {deal name} — ${amount}")&lt;/li&gt;
&lt;li&gt;Google Sheets node → append row to revenue tracker&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Sales team gets instant Slack notifications on closed deals without HubSpot notifications being buried in email.&lt;/p&gt;




&lt;h2&gt;
  
  
  Workflow Pattern 3: Stripe Payment → HubSpot Contact Update + Note
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Trigger:&lt;/strong&gt; Stripe Webhook (charge.succeeded)&lt;br&gt;
&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stripe Webhook Trigger → extract customer email, amount, invoice ID&lt;/li&gt;
&lt;li&gt;HubSpot node → Search Contact by email&lt;/li&gt;
&lt;li&gt;HubSpot node → Update Contact (set custom property &lt;code&gt;last_payment_amount&lt;/code&gt;, &lt;code&gt;last_payment_date&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;HubSpot node → Create Engagement (Note) on contact: "Stripe payment received: ${amount} on {date}, invoice {id}"&lt;/li&gt;
&lt;li&gt;IF node → if amount &amp;gt; $500, set contact property &lt;code&gt;high_value_customer = true&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Your CRM stays in sync with real payment data — support reps can see payment history directly in HubSpot without switching to Stripe.&lt;/p&gt;




&lt;h2&gt;
  
  
  Free Workflow JSON
&lt;/h2&gt;

&lt;p&gt;Download the pre-built workflow for Pattern 1 (Typeform → HubSpot Contact + Deal):&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;Get the free n8n HubSpot workflow JSON&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Import it: n8n → top-right menu → Import from URL or paste JSON → connect your HubSpot credentials → activate.&lt;/p&gt;




&lt;h2&gt;
  
  
  HubSpot Node vs. HTTP Request Node
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;HubSpot Node&lt;/th&gt;
&lt;th&gt;HTTP Request to HubSpot API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Auth setup&lt;/td&gt;
&lt;td&gt;Credential picker&lt;/td&gt;
&lt;td&gt;Manual Bearer header&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Available ops&lt;/td&gt;
&lt;td&gt;Standard CRM ops&lt;/td&gt;
&lt;td&gt;Full API including v3 custom objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pagination&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Standard contact/deal/company workflows&lt;/td&gt;
&lt;td&gt;Custom objects, bulk imports, advanced filters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use the HubSpot node for 90% of cases. Use HTTP Request when you need HubSpot's v3 custom objects API or undocumented endpoints.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;HubSpot node covers contacts, companies, deals, tickets, and associations&lt;/li&gt;
&lt;li&gt;Authenticate with a Private App token scoped to exactly what you need&lt;/li&gt;
&lt;li&gt;Property names are internal names — check HubSpot's property settings before building&lt;/li&gt;
&lt;li&gt;Upsert by email is the cleanest dedup for inbound leads&lt;/li&gt;
&lt;li&gt;Always create associations as a separate step after creating related objects&lt;/li&gt;
&lt;li&gt;Rate limit: 100 req/10s — add Wait nodes in bulk workflows&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What to Read Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/pirateprentice/n8n-stripe-webhooks-handle-payment-events-subscriptions-and-refunds-automatically-free-workflow-2m7n"&gt;n8n Stripe Webhooks guide&lt;/a&gt; — complete payment event handling&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/pirateprentice/n8n-google-sheets-node-read-write-and-sync-spreadsheet-data-in-your-workflows-free-workflow-2d4n"&gt;n8n Google Sheets guide&lt;/a&gt; — sync CRM data to sheets&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/pirateprentice/n8n-airtable-node-read-create-update-and-delete-records-free-json-5462"&gt;n8n Airtable guide&lt;/a&gt; — lightweight CRM alternative&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Need this HubSpot sync built for you? The &lt;a href="https://buy.stripe.com/4gMdRaetT3Yv3yxbOs2go03" rel="noopener noreferrer"&gt;Done-For-You n8n Workflow Build service&lt;/a&gt; ($99 one-time) delivers a custom, tested workflow to your inbox within 48 hours.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>hubspot</category>
      <category>automation</category>
      <category>crm</category>
    </item>
    <item>
      <title>n8n Notion Node: Sync Databases, Manage Pages, and Build Automated Workflows (Free Workflow JSON)</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Tue, 14 Jul 2026 03:22:38 +0000</pubDate>
      <link>https://dev.to/pirateprentice/n8n-notion-node-sync-databases-manage-pages-and-build-automated-workflows-free-workflow-json-2fh4</link>
      <guid>https://dev.to/pirateprentice/n8n-notion-node-sync-databases-manage-pages-and-build-automated-workflows-free-workflow-json-2fh4</guid>
      <description>&lt;p&gt;Notion has become the default workspace for thousands of teams — but its real power unlocks when you connect it to the rest of your stack. The n8n Notion node lets you read and write Notion databases, create and update pages, manage blocks, and trigger workflows from Notion events. This guide covers the operations you'll use most, the gotchas that trip up new users, and three production patterns that actually ship.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;n8n (self-hosted or cloud)&lt;/li&gt;
&lt;li&gt;A Notion account with at least one database&lt;/li&gt;
&lt;li&gt;A Notion integration (internal or public) with the right capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setting up the Notion integration:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://www.notion.so/my-integrations" rel="noopener noreferrer"&gt;notion.so/my-integrations&lt;/a&gt; → &lt;strong&gt;New integration&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Give it a name, select your workspace, set capabilities: &lt;strong&gt;Read content&lt;/strong&gt;, &lt;strong&gt;Update content&lt;/strong&gt;, &lt;strong&gt;Insert content&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Copy the &lt;strong&gt;Internal Integration Secret&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;In your Notion database/page: &lt;strong&gt;⋯ menu → Add connections → your integration name&lt;/strong&gt; (Notion requires explicit page-level connection grants)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In n8n: &lt;strong&gt;Credentials → New → Notion API&lt;/strong&gt; → paste your Internal Integration Secret.&lt;/p&gt;




&lt;h2&gt;
  
  
  Operations at a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database: Get&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fetch database metadata (schema, properties)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database: Get Many&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;List databases the integration has access to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database: Query&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Filter and sort records in a database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Page: Create&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Add a new page/row to a database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Page: Get&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fetch a page by its ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Page: Update&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Update page properties&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Page: Archive&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Archive (soft-delete) a page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Block: Get Many&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Read the content blocks of a page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Block: Append&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Add new blocks to a page&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Key Configuration Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Finding Your Database ID
&lt;/h3&gt;

&lt;p&gt;Your Notion database URL looks like:&lt;br&gt;
&lt;code&gt;https://www.notion.so/myworkspace/abc123def456...?v=...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The database ID is the alphanumeric string between the last &lt;code&gt;/&lt;/code&gt; and the &lt;code&gt;?v=&lt;/code&gt;. In n8n, you can paste the full URL — the node parses it automatically.&lt;/p&gt;
&lt;h3&gt;
  
  
  Property Types Matter
&lt;/h3&gt;

&lt;p&gt;Notion properties have types (title, rich_text, number, select, multi_select, date, checkbox, url, email, phone_number, relation, formula, rollup). The n8n Notion node exposes these as typed fields. A common error: trying to set a &lt;code&gt;formula&lt;/code&gt; or &lt;code&gt;rollup&lt;/code&gt; field — those are computed by Notion, not writable via API.&lt;/p&gt;
&lt;h3&gt;
  
  
  Filtering in Database: Query
&lt;/h3&gt;

&lt;p&gt;Filters use Notion's compound filter syntax. In n8n's UI, you build these with the visual filter builder. For complex compound filters, use the &lt;strong&gt;JSON&lt;/strong&gt; input mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"property"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"select"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"equals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Active"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"property"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MRR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"greater_than"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3 Production Workflow Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pattern 1: CRM-to-Notion Database Sync
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; New leads come in from a Typeform or webhook. You want them in a Notion CRM database — deduplicated, enriched, and linked to the right company page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Webhook Trigger&lt;/strong&gt; — receives lead data (name, email, company, source).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notion – Database: Query&lt;/strong&gt; — filter: &lt;code&gt;{ "property": "Email", "email": { "equals": "{{ $json.email }}" } }&lt;/code&gt;. Returns existing record if the lead exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IF node&lt;/strong&gt; — &lt;code&gt;{{ $json.results.length &amp;gt; 0 }}&lt;/code&gt; → true (existing) or false (new).

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;False branch (new lead):&lt;/strong&gt; Notion – &lt;strong&gt;Page: Create&lt;/strong&gt; — database ID: your CRM DB, properties: name (title), email, company, source, Status (select: "New"), Created (date: today).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;True branch (existing lead):&lt;/strong&gt; Notion – &lt;strong&gt;Page: Update&lt;/strong&gt; — page ID from query results, update &lt;code&gt;Last Seen&lt;/code&gt; date and append to &lt;code&gt;Notes&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge node&lt;/strong&gt; — rejoin and send a Slack notification either way.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key config on Page: Create:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Properties → Title → set to lead name&lt;/li&gt;
&lt;li&gt;Use the "Additional Fields" section for non-title properties&lt;/li&gt;
&lt;li&gt;Date fields require ISO 8601 format: &lt;code&gt;{{ $now.toISO() }}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Pattern 2: Automated Weekly KPI Report to Notion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; Every Monday morning, pull KPI data from Stripe (revenue, new customers, refunds) and write it to a Notion database as a new weekly report page — so the team has a running log of weekly metrics in Notion without manual copy-paste.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Schedule Trigger&lt;/strong&gt; — every Monday at 8:00 AM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Request node&lt;/strong&gt; — Stripe API: &lt;code&gt;GET /v1/charges?created[gte]={{ $now.startOf('week').subtract(1, 'week').toUnixInteger() }}&amp;amp;created[lte]={{ $now.startOf('week').toUnixInteger() }}&lt;/code&gt; to get last week's charges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code node&lt;/strong&gt; — aggregate: sum amounts, count customers, sum refunds, compute net revenue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notion – Page: Create&lt;/strong&gt; — in your KPI Reports database:

&lt;ul&gt;
&lt;li&gt;Title: &lt;code&gt;Week of {{ $now.startOf('week').subtract(1, 'week').toFormat('MMM d') }}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;MRR (number): net revenue&lt;/li&gt;
&lt;li&gt;New Customers (number): customer count&lt;/li&gt;
&lt;li&gt;Refunds (number): refund total&lt;/li&gt;
&lt;li&gt;Status (select): "Published"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notion – Block: Append&lt;/strong&gt; — append a rich text block to the new page with a formatted summary paragraph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack node&lt;/strong&gt; — notify #metrics channel with the Notion page URL.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Gotcha:&lt;/strong&gt; Notion page IDs from Page: Create are returned in the response as &lt;code&gt;id&lt;/code&gt;. Use &lt;code&gt;{{ $json.id }}&lt;/code&gt; in Block: Append to reference the just-created page.&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 3: Notion-Triggered Fulfillment Workflow
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; Your team manages client project approvals in Notion. When a project page's &lt;code&gt;Status&lt;/code&gt; property changes to "Approved", automatically send the client a welcome email, create a Trello card for the team, and update a Google Sheet.&lt;/p&gt;

&lt;p&gt;Notion doesn't support real-time webhooks natively — you'll poll with the Schedule Trigger and check for recently-updated records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Schedule Trigger&lt;/strong&gt; — every 5 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notion – Database: Query&lt;/strong&gt; — filter for pages where &lt;code&gt;Status = "Approved"&lt;/code&gt; AND &lt;code&gt;Last Edited Time&lt;/code&gt; is after your last-check timestamp (store this in a Set node or a simple n8n static variable).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IF node&lt;/strong&gt; — skip if results are empty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split In Batches node&lt;/strong&gt; — process each newly-approved project one at a time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resend node&lt;/strong&gt; — send welcome email to &lt;code&gt;{{ $json.properties.Client_Email.email }}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Request node&lt;/strong&gt; — Trello API: create a card in the "Active Projects" list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets node&lt;/strong&gt; — append a row to the project tracker sheet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notion – Page: Update&lt;/strong&gt; — set &lt;code&gt;Automation_Sent&lt;/code&gt; checkbox to &lt;code&gt;true&lt;/code&gt; so re-runs skip this record.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key config:&lt;/strong&gt; The &lt;code&gt;Automation_Sent&lt;/code&gt; checkbox flag is the dedup mechanism. Without it, every poll cycle re-processes all "Approved" records.&lt;/p&gt;




&lt;h2&gt;
  
  
  6 Common Gotchas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. You must connect the integration to each page/database explicitly&lt;/strong&gt;&lt;br&gt;
Even if your integration has the right capabilities, Notion requires you to manually share each database or page with the integration via &lt;strong&gt;⋯ → Add connections&lt;/strong&gt;. "Not found" errors are almost always this. Fix: share the database with your integration in Notion's UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Title property is always required for Page: Create&lt;/strong&gt;&lt;br&gt;
Every Notion page has exactly one title property. You must set it — its name varies by database (could be "Name", "Title", "Task", etc.). Map it under the &lt;code&gt;title&lt;/code&gt; input, not as an additional field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Rich text values are arrays of text objects, not plain strings&lt;/strong&gt;&lt;br&gt;
When reading page content, rich text properties return &lt;code&gt;[{ "plain_text": "...", "annotations": {...} }]&lt;/code&gt;. To get the plain string value: &lt;code&gt;{{ $json.properties.Notes.rich_text[0].plain_text }}&lt;/code&gt;. For multi-block rich text: map and join.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Formula and rollup fields are read-only&lt;/strong&gt;&lt;br&gt;
You can read computed values from formula/rollup fields, but writing to them throws an error. Only update fields whose source data lives in that page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Notion rate limits: 3 requests/second per integration&lt;/strong&gt;&lt;br&gt;
Loops over large databases will hit rate limits quickly. Add a &lt;strong&gt;Wait node&lt;/strong&gt; (400ms) between iterations, or use Database: Query pagination (page_size max 100) and the &lt;code&gt;has_more&lt;/code&gt; / &lt;code&gt;next_cursor&lt;/code&gt; response fields to paginate manually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Archived pages don't appear in Database: Query by default&lt;/strong&gt;&lt;br&gt;
If a page was archived, it won't show up in query results (Notion excludes archived records). To fetch archived pages, you need to hit the Search endpoint directly via HTTP Request. For most use cases, just make sure you're not accidentally archiving records you still need.&lt;/p&gt;


&lt;h2&gt;
  
  
  Free Workflow JSON
&lt;/h2&gt;

&lt;p&gt;This implements Pattern 1 (Webhook → Notion CRM sync with dedup). Import via n8n → Import from JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Webhook → Notion CRM Sync (Dedup)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"httpMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"notion-crm-intake"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"responseMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseNode"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Webhook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.webhook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"databaseId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_NOTION_DATABASE_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"filterJson"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={ &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;property&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Email&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: { &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;equals&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;{{ $json.body.email }}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; } }"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search Existing Lead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.notion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;460&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"conditions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.results.length }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"larger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lead Exists?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;680&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"databaseId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_NOTION_DATABASE_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $('Webhook').item.json.body.name }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"propertiesUi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"propertyValues"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"emailValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $('Webhook').item.json.body.email }}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Company"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"textValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $('Webhook').item.json.body.company }}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"selectValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Create New Lead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.notion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"pageId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.results[0].id }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"propertiesUi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"propertyValues"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Last Seen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"dateValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $now.toISO() }}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Update Last Seen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.notion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Webhook"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search Existing Lead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Search Existing Lead"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lead Exists?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Lead Exists?"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Update Last Seen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Create New Lead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_NOTION_DATABASE_ID&lt;/code&gt; with your database ID and configure your Notion API credential.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The n8n Notion node covers the full database lifecycle — querying, creating, updating, and archiving pages. The patterns with the highest leverage are the sync patterns: pulling data from other systems into Notion as a structured record store, and using Notion as a lightweight approval layer that triggers downstream automation. The gotcha that bites most new users: sharing the integration with each database in Notion's UI — "not found" errors are almost always a missing connection grant, not a credential problem.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Want the full n8n Workflow Starter Pack?&lt;/strong&gt; 25+ production-ready workflow templates covering lead capture, Stripe automation, AI pipelines, and more — &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;$29 on Gumroad&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Need a custom n8n workflow built for your business?&lt;/strong&gt; &lt;a href="https://buy.stripe.com/4gMdRaetT3Yv3yxbOs2go03" rel="noopener noreferrer"&gt;Done-for-you service — $99&lt;/a&gt;. Describe your automation, get a working workflow delivered.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>notion</category>
      <category>automation</category>
      <category>productivity</category>
    </item>
    <item>
      <title>n8n Airtable Node: Advanced Patterns for CRM Sync, Approval Flows, and Multi-Table Joins (Free Workflow JSON)</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Mon, 13 Jul 2026 07:21:07 +0000</pubDate>
      <link>https://dev.to/pirateprentice/n8n-airtable-node-advanced-patterns-for-crm-sync-approval-flows-and-multi-table-joins-free-2dhj</link>
      <guid>https://dev.to/pirateprentice/n8n-airtable-node-advanced-patterns-for-crm-sync-approval-flows-and-multi-table-joins-free-2dhj</guid>
      <description>&lt;h1&gt;
  
  
  n8n Airtable Node: Advanced Patterns for CRM Sync, Approval Flows, and Multi-Table Joins (Free Workflow JSON)
&lt;/h1&gt;

&lt;p&gt;Airtable sits at a sweet spot between spreadsheet and database — flexible enough for non-technical teams to manage, structured enough for automation. The n8n Airtable node connects your Airtable bases directly to your automation stack, letting you read records, write new ones, update fields, and delete stale data programmatically.&lt;/p&gt;

&lt;p&gt;This guide goes beyond basic CRUD to cover the patterns that actually show up in production: CRM sync, record approval flows, and joining data across multiple Airtable tables.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;n8n (self-hosted or cloud)&lt;/li&gt;
&lt;li&gt;An Airtable account with at least one base&lt;/li&gt;
&lt;li&gt;An Airtable Personal Access Token (PAT) — create one at &lt;a href="https://airtable.com/create/tokens" rel="noopener noreferrer"&gt;airtable.com/create/tokens&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scopes you need on the PAT:&lt;/strong&gt; &lt;code&gt;data.records:read&lt;/code&gt;, &lt;code&gt;data.records:write&lt;/code&gt;, &lt;code&gt;schema.bases:read&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In n8n: &lt;strong&gt;Credentials → New → Airtable Token API&lt;/strong&gt; → paste your PAT.&lt;/p&gt;




&lt;h2&gt;
  
  
  Operations at a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Create Record&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Add a new row to a table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Get Record&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fetch one record by its Airtable record ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Get Many Records&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;List records with optional filtering, sorting, and field selection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Update Record&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Overwrite specified fields on an existing record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Upsert Record&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Create or update based on a field match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delete Record&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Remove a record by ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Search&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Find records matching a formula filter&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Key Configuration Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Finding Your Base ID and Table Name
&lt;/h3&gt;

&lt;p&gt;Your Base ID is in the Airtable URL: &lt;code&gt;https://airtable.com/appXXXXXXXXXXXX/...&lt;/code&gt; — the &lt;code&gt;appXXXXXXXXXXXX&lt;/code&gt; part. Your Table Name is the tab label in the interface.&lt;/p&gt;

&lt;p&gt;In n8n v4+, the Airtable node has a dropdown to select Base and Table. Use it — it validates your credential scope and saves you from typo bugs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filter Formulas
&lt;/h3&gt;

&lt;p&gt;Airtable uses its own formula syntax for filtering, identical to what you'd use in Airtable views. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;{Status} = "Active"&lt;/code&gt; — string match&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AND({Status} = "Active", {MRR} &amp;gt; 100)&lt;/code&gt; — compound filter&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IS_AFTER({Created}, "2026-06-01")&lt;/code&gt; — date comparison&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SEARCH("n8n", {Tags})&lt;/code&gt; — substring search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pass these as the &lt;strong&gt;Filter by Formula&lt;/strong&gt; field in the Get Many Records operation.&lt;/p&gt;




&lt;h2&gt;
  
  
  3 Production Workflow Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pattern 1: Webhook-to-Airtable CRM Sync
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; A prospect fills out a Typeform, Tally, or your own form. You want them in an Airtable CRM base — but only once (no duplicates), and you want to route new vs. returning leads differently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Webhook Trigger&lt;/strong&gt; — receives the form submission (name, email, company, message).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Airtable – Search Records&lt;/strong&gt; — filter formula: &lt;code&gt;{Email} = "{{ $json.email }}"&lt;/code&gt;. Returns matching records if this lead already exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IF node&lt;/strong&gt; — checks &lt;code&gt;{{ $json.length &amp;gt; 0 }}&lt;/code&gt; (lead exists) vs. empty result (new lead).

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;True branch (existing lead):&lt;/strong&gt; Airtable – &lt;strong&gt;Update Record&lt;/strong&gt; — set &lt;code&gt;Last Contact&lt;/code&gt; to today, append to a &lt;code&gt;Notes&lt;/code&gt; field with the new message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;False branch (new lead):&lt;/strong&gt; Airtable – &lt;strong&gt;Create Record&lt;/strong&gt; — write all fields; then send a Slack notification or welcome email via Resend.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge node&lt;/strong&gt; — rejoin both branches for any shared post-processing (e.g., log to a Sheet, notify the sales owner).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key config on Search:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operation: &lt;strong&gt;Search&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Filter Formula: &lt;code&gt;{Email} = "{{ $json.email }}"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fields to Retrieve: &lt;code&gt;id, Email, Name, Last Contact, Notes&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Pattern 2: Multi-Table Record Join
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; You have an Orders table and a Customers table in Airtable. An order comes in via webhook; you need to enrich it with customer data from the Customers table before writing it to a reporting sheet.&lt;/p&gt;

&lt;p&gt;Airtable's linked record fields give you related record IDs, not values — you have to fetch those values yourself in n8n.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Webhook Trigger&lt;/strong&gt; — receives order data (order ID, customer record ID, amount, SKU).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Airtable – Get Record&lt;/strong&gt; (Customers table) — fetch the customer record using the linked record ID from step 1: &lt;code&gt;{{ $json.customer_record_id }}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge node&lt;/strong&gt; (Combine mode: Keep All) — merges the order item with the customer record fields. Now you have &lt;code&gt;{ order_id, amount, sku, customer_name, customer_email, customer_tier }&lt;/code&gt; in one item.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set node&lt;/strong&gt; — normalize and rename fields as needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets – Append Row&lt;/strong&gt; — write the enriched row to your reporting sheet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Airtable – Update Record&lt;/strong&gt; (Orders table) — mark the order as &lt;code&gt;Synced: true&lt;/code&gt; so re-runs skip it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Airtable's native automations can't easily pull linked record fields across tables and push them elsewhere. n8n fills this gap cleanly.&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 3: Airtable Record Approval Flow
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; A team member creates a record in Airtable (e.g., a vendor invoice, a content piece, a PTO request). A manager needs to approve it before a downstream action fires (pay the invoice, publish the content, update the HR system).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Airtable Trigger&lt;/strong&gt; — listen for records where &lt;code&gt;Status&lt;/code&gt; changes to &lt;code&gt;Pending Approval&lt;/code&gt;. (Use the Airtable Trigger node — it polls every minute by default.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack node&lt;/strong&gt; (or Gmail) — send the manager a message: "New approval request: [Title]. Approve: &lt;code&gt;{{ $env.N8N_WEBHOOK_URL }}/approve?id={{ $json.id }}&lt;/code&gt; | Reject: &lt;code&gt;{{ $env.N8N_WEBHOOK_URL }}/reject?id={{ $json.id }}&lt;/code&gt;"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wait node&lt;/strong&gt; — pause execution waiting for the webhook callback (set a timeout, e.g., 48h).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook node&lt;/strong&gt; (resume trigger) — the manager clicks Approve or Reject, which hits the webhook.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Switch node&lt;/strong&gt; — routes on &lt;code&gt;{{ $json.query.action }}&lt;/code&gt;: &lt;code&gt;approve&lt;/code&gt; or &lt;code&gt;reject&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Approve branch:&lt;/strong&gt; Airtable – &lt;strong&gt;Update Record&lt;/strong&gt; — set &lt;code&gt;Status = "Approved"&lt;/code&gt;. Fire the downstream action (e.g., send the invoice to accounting via email).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reject branch:&lt;/strong&gt; Airtable – &lt;strong&gt;Update Record&lt;/strong&gt; — set &lt;code&gt;Status = "Rejected"&lt;/code&gt;, write the rejection reason. Notify the submitter.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Gotcha on Wait node:&lt;/strong&gt; n8n's Wait node supports resuming via webhook. In self-hosted n8n, your instance must be publicly accessible for the manager to click the resume URL. Use a tunnel (ngrok/Cloudflare Tunnel) in development.&lt;/p&gt;




&lt;h2&gt;
  
  
  6 Common Gotchas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Field names are case-sensitive and space-sensitive&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;{First Name}&lt;/code&gt; is not the same as &lt;code&gt;{firstname}&lt;/code&gt;. If your filter returns no results, double-check the exact field name in Airtable — include spaces and capitalization exactly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Linked record fields return arrays of record IDs, not values&lt;/strong&gt;&lt;br&gt;
If a field is a linked record type (e.g., &lt;code&gt;Customer&lt;/code&gt; linked to a Customers table), the value you get is &lt;code&gt;["recXXXXXXXXXXXXXX"]&lt;/code&gt; — a record ID, not a name. Fetch the linked record separately with a Get Record operation to get its fields.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Rate limits: 5 requests/second per base&lt;/strong&gt;&lt;br&gt;
Airtable enforces 5 req/s per base. If you're looping over hundreds of records, add a &lt;strong&gt;Wait node&lt;/strong&gt; (200ms) between iterations, or batch your reads with Get Many Records + an Item Lists node to avoid 429 errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Airtable Trigger polls, not webhooks&lt;/strong&gt;&lt;br&gt;
The n8n Airtable Trigger uses polling (every 1–60 minutes configurable), not true push webhooks. If you need near-real-time triggers, use Airtable's native automations to call an n8n Webhook Trigger instead — far faster than polling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Record IDs are unstable across base copies&lt;/strong&gt;&lt;br&gt;
If you duplicate an Airtable base, record IDs change. Don't use record IDs as business keys in external systems — use a dedicated unique field (e.g., a UUID you generate on creation, or a formula field).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Empty select/multiselect fields return null, not an empty array&lt;/strong&gt;&lt;br&gt;
Check for null before trying to access select values: &lt;code&gt;{{ $json.Tags ? $json.Tags.join(", ") : "" }}&lt;/code&gt;. Failing to null-check crashes the expression.&lt;/p&gt;


&lt;h2&gt;
  
  
  Free Workflow JSON
&lt;/h2&gt;

&lt;p&gt;This implements Pattern 1 (Webhook → Airtable CRM sync with dedup). Import via n8n → Import from JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Webhook → Airtable CRM Sync (Dedup)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"httpMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"crm-intake"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"responseMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseNode"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Webhook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.webhook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_BASE_ID"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Leads"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"filterByFormula"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={Email} = &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;{{ $json.body.email }}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search Existing Lead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.airtable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;460&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"conditions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.length }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"larger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lead Exists?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;680&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_BASE_ID"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Leads"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.id }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"columns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"mappingMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"defineBelow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Last Contact"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $now.toISO() }}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Update Last Contact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.airtable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_BASE_ID"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Leads"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"columns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"mappingMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"autoMapInputData"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Create New Lead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.airtable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Webhook"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search Existing Lead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Search Existing Lead"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lead Exists?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Lead Exists?"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Update Last Contact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Create New Lead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_BASE_ID&lt;/code&gt; with your Airtable base ID and configure your Airtable Token credential.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The n8n Airtable node handles the full record lifecycle — but the patterns that deliver the most value are the ones that bridge Airtable to other systems: enriching records from linked tables, deduplicating inbound webhook data, and building approval flows that Airtable's native automations can't express. The gotcha to internalize: linked record fields give you IDs, not values — always fetch the linked record when you need its data.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Want the full n8n Workflow Starter Pack?&lt;/strong&gt; 25+ production-ready workflow templates covering lead capture, Stripe automation, AI pipelines, and more — &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;$29 on Gumroad&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Need a custom n8n workflow built for your business?&lt;/strong&gt; &lt;a href="https://buy.stripe.com/4gMdRaetT3Yv3yxbOs2go03" rel="noopener noreferrer"&gt;Done-for-you service — $99&lt;/a&gt;. Describe your automation, get a working workflow delivered.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>airtable</category>
      <category>automation</category>
      <category>nocode</category>
    </item>
    <item>
      <title>n8n Google Sheets Node: Read, Write, and Sync Spreadsheet Data in Your Workflows (Free Workflow JSON)</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Sun, 12 Jul 2026 19:27:47 +0000</pubDate>
      <link>https://dev.to/pirateprentice/n8n-google-sheets-node-read-write-and-sync-spreadsheet-data-in-your-workflows-free-workflow-2d4n</link>
      <guid>https://dev.to/pirateprentice/n8n-google-sheets-node-read-write-and-sync-spreadsheet-data-in-your-workflows-free-workflow-2d4n</guid>
      <description>&lt;p&gt;Google Sheets is the default data store for small teams everywhere — lead lists, inventory trackers, project logs, CRM lite, budget sheets. The n8n Google Sheets node connects those sheets directly to your automation stack, letting you read rows, append new records, update cells, and delete stale data without ever opening a browser.&lt;/p&gt;

&lt;p&gt;This guide covers every operation, common gotchas, three production-ready workflow patterns, and a free workflow JSON you can import today.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;n8n (self-hosted or cloud)&lt;/li&gt;
&lt;li&gt;A Google Cloud project with the &lt;strong&gt;Google Sheets API&lt;/strong&gt; enabled&lt;/li&gt;
&lt;li&gt;A Google OAuth2 credential configured in n8n (or a Service Account for server-to-server access)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Google OAuth2 setup: in n8n go to &lt;strong&gt;Credentials → New → Google Sheets OAuth2 API&lt;/strong&gt;. You'll need a Client ID and Secret from your Google Cloud Console. Authorize the credential; n8n stores the refresh token for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Operations at a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Append or Update Row&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Write new row, or update if a matching key exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Append Row&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Always add a new row at the end of the sheet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Clear&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Clear a defined range of cells&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Create&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Create a new spreadsheet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delete&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Delete rows matching a filter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Get Row(s)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Read one or more rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Update Row&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Overwrite specific columns in existing rows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Operation Deep-Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Append Row
&lt;/h3&gt;

&lt;p&gt;The simplest write operation. Adds one row per input item to the sheet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key parameters:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spreadsheet ID&lt;/strong&gt; — the ID from the sheet URL (everything between &lt;code&gt;/d/&lt;/code&gt; and &lt;code&gt;/edit&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sheet Name&lt;/strong&gt; — the tab name (case-sensitive; defaults to "Sheet1")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Columns&lt;/strong&gt; — map your n8n data fields to sheet column headers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use &lt;strong&gt;Mapping Column Mode: Map Each Column Manually&lt;/strong&gt; when your column headers differ from your workflow field names. Use &lt;strong&gt;Auto-Map&lt;/strong&gt; when they match exactly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gotcha:&lt;/strong&gt; Append Row always adds a new row even if the record already exists. Use "Append or Update Row" when you want upsert behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Append or Update Row
&lt;/h3&gt;

&lt;p&gt;The upsert operation. Searches for a row where a specified column matches a value; if found, updates it; if not, appends a new row.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key parameter: Column to Match On&lt;/strong&gt; — pick a unique identifier (email, order ID, customer ID). Without a unique key, every run appends duplicates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get Row(s)
&lt;/h3&gt;

&lt;p&gt;Reads rows from a sheet. Two sub-modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Get Many Rows&lt;/strong&gt; — returns all rows (or up to a limit). Great for reading an entire list for processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get Row by Row Number&lt;/strong&gt; — fetch a specific row by index (1-based).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Filters:&lt;/strong&gt; You can add a filter to return only rows where a column equals/contains a value. This runs client-side in n8n (all rows are fetched, then filtered) — not a server-side query. For large sheets (10,000+ rows) this can be slow; consider a more structured data store for high-volume lookups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt; Each row becomes one n8n item. Column headers become field names.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update Row
&lt;/h3&gt;

&lt;p&gt;Updates specific columns in a row you identify by row number. Useful when you already know the row index from a previous "Get Row(s)" step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delete Row
&lt;/h3&gt;

&lt;p&gt;Deletes rows matching a filter. Supports "Delete Row" by row number or by filter match.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Deletions shift row numbers. If you delete row 5 in a loop, the old row 6 becomes row 5. Process deletions in reverse order (bottom to top) or collect all row numbers first, then delete in one batch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clear
&lt;/h3&gt;

&lt;p&gt;Clears a defined range (e.g., &lt;code&gt;A2:Z1000&lt;/code&gt;) without deleting the header row. Useful for resetting a reporting sheet before rewriting it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Authentication: OAuth2 vs Service Account
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;OAuth2&lt;/strong&gt; (user credential): easiest setup, tied to your personal Google account. Works fine for personal or small-team use. Token expires and refreshes automatically; if the authorizing user's account is deleted, automations break.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service Account&lt;/strong&gt;: a non-human Google identity. Better for production. Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Service Account in Google Cloud Console.&lt;/li&gt;
&lt;li&gt;Download the JSON key file.&lt;/li&gt;
&lt;li&gt;In Google Sheets, share the target spreadsheet with the service account's email address (looks like &lt;code&gt;name@project.iam.gserviceaccount.com&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;In n8n, create a &lt;strong&gt;Google Sheets Service Account&lt;/strong&gt; credential and paste the JSON key.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Service accounts don't expire and aren't tied to a person — strongly preferred for server-to-server automation.&lt;/p&gt;




&lt;h2&gt;
  
  
  3 Production Workflow Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pattern 1: Typeform → Google Sheets Lead Capture with Deduplication
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; A Typeform captures leads. You want them in Sheets, but re-submissions shouldn't create duplicate rows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Typeform Trigger&lt;/strong&gt; — fires on new submission.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets – Get Row(s)&lt;/strong&gt; — read the lead list, filter by email column matching the submission email.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IF node&lt;/strong&gt; — if the email already exists, skip; if not, continue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets – Append Row&lt;/strong&gt; — write the new lead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gmail / Resend&lt;/strong&gt; — send a welcome email.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key config:&lt;/strong&gt; In step 2, use Filters → Column &lt;code&gt;email&lt;/code&gt; equals &lt;code&gt;{{ $json.email }}&lt;/code&gt;. The IF node checks &lt;code&gt;{{ $json["Google Sheets"].length }} &amp;gt; 0&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 2: Daily Stripe Revenue Sync to Google Sheets
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; Every morning, pull the prior day's Stripe charges and append them to a running revenue log in Sheets. Finance team reads the Sheet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Schedule Trigger&lt;/strong&gt; — runs daily at 7 AM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe – Get Charges&lt;/strong&gt; — filter by &lt;code&gt;created&lt;/code&gt; &amp;gt;= yesterday 00:00 UTC, &lt;code&gt;&amp;lt;&lt;/code&gt; today 00:00 UTC. Paginate if needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set node&lt;/strong&gt; — normalize fields: &lt;code&gt;date&lt;/code&gt;, &lt;code&gt;amount_usd&lt;/code&gt; (divide cents by 100), &lt;code&gt;customer_email&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets – Append Row&lt;/strong&gt; — write each charge to the revenue log tab.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets – Update Row&lt;/strong&gt; — update a summary tab: total yesterday, running month total (calculate with a Code node before writing).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use a separate "Summary" tab with formulas referencing the raw log. Let Sheets do the aggregation; n8n just feeds the data.&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 3: Google Sheets as a Workflow Config Store
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; Non-technical team members need to control workflow behavior (e.g., which keywords to monitor, which clients are active, what email subject lines to use) without touching n8n.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Schedule Trigger&lt;/strong&gt; (or webhook trigger at workflow start).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets – Get Row(s)&lt;/strong&gt; — read a "Config" tab. Each row is a config key-value pair (&lt;code&gt;key&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code node&lt;/strong&gt; — convert rows into a lookup object: &lt;code&gt;{ keyword: "n8n", recipient: "ops@company.com", ... }&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subsequent nodes&lt;/strong&gt; use &lt;code&gt;{{ $node["Code"].json.keyword }}&lt;/code&gt; etc. as dynamic parameters.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; It gives stakeholders a UI (Sheets) without giving them n8n access. You update logic by changing a cell, not deploying code.&lt;/p&gt;




&lt;h2&gt;
  
  
  6 Common Gotchas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Column headers must exist before writing&lt;/strong&gt;&lt;br&gt;
The Sheets node maps data to column headers. If a column header doesn't exist in row 1, data for that field is silently dropped. Always set up your header row first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Spreadsheet ID vs Sheet name&lt;/strong&gt;&lt;br&gt;
The Spreadsheet ID is the long string in the URL. The Sheet Name is the tab label (e.g., "Sheet1", "Leads", "Config"). These are different — confusing them breaks authentication silently (wrong spreadsheet) or returns empty data (wrong tab).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Rate limits: 300 requests/minute per project&lt;/strong&gt;&lt;br&gt;
Google Sheets API enforces a 300 req/min limit. If you're processing hundreds of rows in a loop, add a &lt;strong&gt;Wait node&lt;/strong&gt; (0.2s) between iterations, or switch to batch writes. Hitting the limit returns a 429 and n8n retries with exponential backoff, but this can slow workflows significantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Empty rows are returned as items with no fields&lt;/strong&gt;&lt;br&gt;
"Get Row(s)" returns empty rows as items with no fields. Add an &lt;strong&gt;IF node&lt;/strong&gt; after reading: &lt;code&gt;{{ Object.keys($json).length &amp;gt; 0 }}&lt;/code&gt; to filter them out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Type coercion: everything comes back as a string&lt;/strong&gt;&lt;br&gt;
Sheets stores numbers and dates as text when read via API without explicit type coercion. If you need a numeric value for math, parse it: &lt;code&gt;{{ parseInt($json.amount) }}&lt;/code&gt; or &lt;code&gt;{{ Number($json.price) }}&lt;/code&gt;. Dates come back as strings like "7/12/2026" — parse with Luxon in a Code node if you need proper date objects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Concurrent writes cause race conditions&lt;/strong&gt;&lt;br&gt;
If two workflow executions try to append rows at the same time, you can get duplicate rows or missed writes. For high-concurrency scenarios, use an n8n queue (execution mode set to "Queue") or write to a staging area and merge with a single scheduled consolidation workflow.&lt;/p&gt;


&lt;h2&gt;
  
  
  Free Workflow JSON
&lt;/h2&gt;

&lt;p&gt;This workflow implements Pattern 2 (daily Stripe → Sheets revenue sync). Import via n8n → Import from JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Daily Stripe Revenue → Google Sheets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"interval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"triggerAtHour"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Schedule Trigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.scheduleTrigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"charge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"getAll"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"returnAll"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"filters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"gte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $now.minus({days:1}).startOf('day').toUnixInteger() }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"lt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $now.startOf('day').toUnixInteger() }}"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stripe - Get Yesterday Charges"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.stripe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;460&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"values"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.created_formatted }}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer_email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.billing_details.email }}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.description }}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.status }}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amount_usd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.amount / 100 }}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Normalize Fields"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;680&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"append"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"documentId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_SPREADSHEET_ID"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sheetName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Revenue Log"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"columns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"mappingMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"autoMapInputData"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Google Sheets - Append Revenue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.googleSheets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Schedule Trigger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stripe - Get Yesterday Charges"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stripe - Get Yesterday Charges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Normalize Fields"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Normalize Fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Google Sheets - Append Revenue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_SPREADSHEET_ID&lt;/code&gt; with your actual spreadsheet ID and configure your Google Sheets and Stripe credentials.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The n8n Google Sheets node is one of the most-used integrations in the ecosystem for good reason — Sheets is where small-team data already lives. The key patterns that hold up in production: use &lt;strong&gt;Append or Update Row&lt;/strong&gt; with a unique key to prevent duplicates, authenticate with a &lt;strong&gt;Service Account&lt;/strong&gt; for stability, batch your writes to stay under rate limits, and keep a "Config" tab pattern in your back pocket for ops-managed dynamic parameters.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Want the full n8n Workflow Starter Pack?&lt;/strong&gt; 25+ production-ready workflow templates covering lead capture, Stripe automation, AI pipelines, and more — &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;$29 on Gumroad&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Need a custom n8n workflow built for your business?&lt;/strong&gt; &lt;a href="https://buy.stripe.com/4gMdRaetT3Yv3yxbOs2go03" rel="noopener noreferrer"&gt;Done-for-you service — $99&lt;/a&gt;. Describe your automation, get a working workflow delivered.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>googlesheets</category>
      <category>webdev</category>
    </item>
    <item>
      <title>n8n Stripe Webhooks: Handle Payment Events, Subscriptions, and Refunds Automatically (Free Workflow JSON)</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Sun, 12 Jul 2026 19:22:37 +0000</pubDate>
      <link>https://dev.to/pirateprentice/n8n-stripe-webhooks-handle-payment-events-subscriptions-and-refunds-automatically-free-workflow-2m7n</link>
      <guid>https://dev.to/pirateprentice/n8n-stripe-webhooks-handle-payment-events-subscriptions-and-refunds-automatically-free-workflow-2m7n</guid>
      <description>&lt;p&gt;Stripe fires webhooks for every meaningful payment event: a charge succeeds, a subscription renews, a payment fails, a refund is issued. If your n8n workflow isn't listening to these, you're missing real-time signals you need to run your business.&lt;/p&gt;

&lt;p&gt;This guide covers everything you need to build a production-ready Stripe webhook handler in n8n — from wiring up the endpoint to handling the five events that matter most.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You'll Learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How to create a Stripe webhook endpoint in n8n&lt;/li&gt;
&lt;li&gt;How to verify the webhook signature (so only Stripe can trigger your workflow)&lt;/li&gt;
&lt;li&gt;How to handle the five most important Stripe events&lt;/li&gt;
&lt;li&gt;Three real workflow patterns: payment receipt, failed payment recovery, and subscription lifecycle&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;n8n (self-hosted or cloud) with a publicly accessible URL&lt;/li&gt;
&lt;li&gt;A Stripe account with API access&lt;/li&gt;
&lt;li&gt;Stripe secret key and webhook signing secret (from Stripe Dashboard → Developers → Webhooks)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Create the Webhook Trigger in n8n
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open n8n and create a new workflow&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;Webhook&lt;/strong&gt; node as the trigger&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;HTTP Method&lt;/strong&gt; to &lt;code&gt;POST&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy the generated webhook URL — you'll add it to Stripe next&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Response Mode&lt;/strong&gt; to &lt;code&gt;Immediately&lt;/code&gt; (Stripe expects a 200 within 30 seconds)&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; If you're running n8n locally, Stripe can't reach &lt;code&gt;localhost&lt;/code&gt;. Use &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt; or deploy to a cloud instance for testing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 2: Register the Endpoint in Stripe
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Stripe Dashboard → Developers → Webhooks&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add endpoint&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Paste your n8n webhook URL&lt;/li&gt;
&lt;li&gt;Select the events you want to handle (start with the five below)&lt;/li&gt;
&lt;li&gt;Save — Stripe will show you the &lt;strong&gt;Signing Secret&lt;/strong&gt; (starts with &lt;code&gt;whsec_&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Events to select:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;payment_intent.succeeded&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;payment_intent.payment_failed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;invoice.payment_succeeded&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;invoice.payment_failed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;customer.subscription.deleted&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 3: Verify the Webhook Signature
&lt;/h2&gt;

&lt;p&gt;Stripe signs every webhook with an HMAC-SHA256 signature. Verifying it ensures your workflow only processes real Stripe events — not spoofed requests.&lt;/p&gt;

&lt;p&gt;After the Webhook trigger node, add a &lt;strong&gt;Code&lt;/strong&gt; node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Stripe signature verification&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// raw body as string&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stripe-signature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;whsec_YOUR_SIGNING_SECRET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// store in n8n credential&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sigHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signedPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expectedSig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signedPayload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedSig&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid Stripe signature — request rejected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Parse the event&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Gotcha:&lt;/strong&gt; n8n's Webhook node parses the body by default. You need the &lt;strong&gt;raw&lt;/strong&gt; body for signature verification. Set &lt;strong&gt;Binary Data&lt;/strong&gt; to &lt;code&gt;false&lt;/code&gt; and &lt;strong&gt;Raw Body&lt;/strong&gt; to &lt;code&gt;true&lt;/code&gt; in the Webhook node's options, then access &lt;code&gt;$input.first().json.rawBody&lt;/code&gt; or the header equivalent depending on your n8n version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practice: for non-critical internal workflows, you can skip signature verification. For production customer-facing workflows, always verify.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Route Events with a Switch Node
&lt;/h2&gt;

&lt;p&gt;Add a &lt;strong&gt;Switch&lt;/strong&gt; node after verification to route different event types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output 1:&lt;/strong&gt; &lt;code&gt;{{ $json.type }}&lt;/code&gt; equals &lt;code&gt;payment_intent.succeeded&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output 2:&lt;/strong&gt; &lt;code&gt;{{ $json.type }}&lt;/code&gt; equals &lt;code&gt;payment_intent.payment_failed&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output 3:&lt;/strong&gt; &lt;code&gt;{{ $json.type }}&lt;/code&gt; equals &lt;code&gt;invoice.payment_succeeded&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output 4:&lt;/strong&gt; &lt;code&gt;{{ $json.type }}&lt;/code&gt; equals &lt;code&gt;invoice.payment_failed&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output 5:&lt;/strong&gt; &lt;code&gt;{{ $json.type }}&lt;/code&gt; equals &lt;code&gt;customer.subscription.deleted&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallback output:&lt;/strong&gt; everything else (log and ignore)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Five Events That Matter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;payment_intent.succeeded&lt;/code&gt; — One-Time Payment Confirmed
&lt;/h3&gt;

&lt;p&gt;Fires when a customer completes a one-time charge. Use this to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trigger product delivery (email a download link, provision access)&lt;/li&gt;
&lt;li&gt;Write the sale to Google Sheets or Airtable&lt;/li&gt;
&lt;li&gt;Send a Slack notification to your team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;           &lt;span class="c1"&gt;// amount in cents&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt;         &lt;span class="c1"&gt;// e.g., "usd"&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;receipt_email&lt;/span&gt;    &lt;span class="c1"&gt;// customer email&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;         &lt;span class="c1"&gt;// your custom fields&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;code&gt;payment_intent.payment_failed&lt;/code&gt; — Charge Failed
&lt;/h3&gt;

&lt;p&gt;Fires when a card is declined or a payment attempt fails. Use this to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send a "payment failed" email to the customer with a retry link&lt;/li&gt;
&lt;li&gt;Pause their service access after N failed attempts&lt;/li&gt;
&lt;li&gt;Alert your team for high-value orders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;last_payment_error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;   &lt;span class="c1"&gt;// e.g., "Your card was declined"&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;last_payment_error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;      &lt;span class="c1"&gt;// e.g., "card_declined"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;code&gt;invoice.payment_succeeded&lt;/code&gt; — Subscription Renewal Paid
&lt;/h3&gt;

&lt;p&gt;Fires every time a subscription invoice is paid — both the first charge and renewals. Use this to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extend the customer's access period in your database&lt;/li&gt;
&lt;li&gt;Send a renewal receipt&lt;/li&gt;
&lt;li&gt;Update your MRR tracking spreadsheet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer_email&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount_paid&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscription&lt;/span&gt;     &lt;span class="c1"&gt;// subscription ID&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;period_end&lt;/span&gt;       &lt;span class="c1"&gt;// Unix timestamp of next renewal&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. &lt;code&gt;invoice.payment_failed&lt;/code&gt; — Subscription Payment Failed
&lt;/h3&gt;

&lt;p&gt;Fires when a recurring charge fails (expired card, insufficient funds). This is critical for retention — most involuntary churn is recoverable if you act fast.&lt;/p&gt;

&lt;p&gt;Use this to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send a "please update your card" email immediately&lt;/li&gt;
&lt;li&gt;Trigger a dunning sequence (Day 1 email, Day 4 SMS, Day 7 final notice)&lt;/li&gt;
&lt;li&gt;Flag the customer in your CRM for manual follow-up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer_email&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next_payment_attempt&lt;/span&gt;   &lt;span class="c1"&gt;// Unix timestamp Stripe will retry&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attempt_count&lt;/span&gt;          &lt;span class="c1"&gt;// how many times Stripe has tried&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. &lt;code&gt;customer.subscription.deleted&lt;/code&gt; — Subscription Cancelled
&lt;/h3&gt;

&lt;p&gt;Fires when a subscription ends (cancellation at period end resolves here, not at cancel time). Use this to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revoke access&lt;/li&gt;
&lt;li&gt;Send a cancellation confirmation + offboarding email&lt;/li&gt;
&lt;li&gt;Trigger a win-back sequence after 7 days&lt;/li&gt;
&lt;li&gt;Write to a churn tracking sheet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;          &lt;span class="c1"&gt;// customer ID&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cancel_at_period_end&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ended_at&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Three Complete Workflow Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pattern 1: Automated Payment Receipt + Delivery
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook → Verify Signature → Switch (event type)
  └─ payment_intent.succeeded
       → Extract: email, amount, metadata.product_id
       → Gmail/Resend: send receipt + download link
       → Google Sheets: append row (date, email, amount, product)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Every successful payment triggers a branded receipt and fulfillment email within seconds, with every sale recorded automatically.&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 2: Failed Payment Recovery Sequence
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook → Verify Signature → Switch
  └─ invoice.payment_failed
       → IF attempt_count == 1: send "Heads up, card failed" email
       → IF attempt_count == 2: send "Last chance" email with update link
       → IF attempt_count &amp;gt;= 3: send to CRM as churned + pause access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Automated dunning that recovers 20–40% of failed subscription payments without manual intervention.&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 3: Subscription Lifecycle Tracker
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook → Verify Signature → Switch
  ├─ invoice.payment_succeeded → Sheets: upsert row (customer, status=active, last_paid=now)
  ├─ invoice.payment_failed    → Sheets: update row (status=past_due, attempts=N)
  └─ subscription.deleted      → Sheets: update row (status=cancelled, ended_at=now)
                                → Wait 7 days → Send win-back email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; A real-time subscription dashboard in Google Sheets with zero manual updates, plus automated win-back outreach.&lt;/p&gt;




&lt;h2&gt;
  
  
  Free Workflow JSON
&lt;/h2&gt;

&lt;p&gt;Download the complete workflow JSON (Webhook trigger → Signature verification → Switch node routing → all 5 event handlers with Gmail/Sheets stubs):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;Download free n8n Stripe Webhook workflow JSON →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Import it in n8n: Settings → Import from file. Add your Stripe credentials and webhook signing secret, activate, and you're live.&lt;/p&gt;




&lt;h2&gt;
  
  
  6 Gotchas to Know
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Raw body requirement:&lt;/strong&gt; Stripe signature verification requires the exact raw request body. If n8n parses the JSON before you can read it, the HMAC won't match. Check your n8n version's Webhook node options for "Raw Body."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;30-second response window:&lt;/strong&gt; Stripe marks a webhook as failed if it doesn't receive a 200 within 30 seconds. Use &lt;code&gt;Response Mode: Immediately&lt;/code&gt; on the Webhook node and do the heavy work (Sheets writes, emails) after responding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Idempotency:&lt;/strong&gt; Stripe retries failed webhooks for up to 3 days. Your workflow will receive duplicate events. Add a check node that looks up the &lt;code&gt;event.id&lt;/code&gt; in a Sheets or database table and skips processing if already handled.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;livemode&lt;/code&gt; flag:&lt;/strong&gt; Stripe sends both test and live events to the same endpoint if you use the same URL. Check &lt;code&gt;$json.livemode === true&lt;/code&gt; at the top of your Switch node to avoid processing test events in production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;payment_intent.succeeded&lt;/code&gt; fires for subscriptions too:&lt;/strong&gt; The first subscription charge fires both &lt;code&gt;payment_intent.succeeded&lt;/code&gt; and &lt;code&gt;invoice.payment_succeeded&lt;/code&gt;. Handle only one to avoid double-processing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Metadata is your friend:&lt;/strong&gt; Add &lt;code&gt;metadata&lt;/code&gt; fields to your Stripe Payment Intents (product ID, user ID, plan name). They come through in webhook payloads and let you route fulfillment without additional API calls.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Want This Built For You?
&lt;/h2&gt;

&lt;p&gt;If you'd rather have a working Stripe webhook workflow built to your exact spec — event types, routing logic, email templates, Sheets schema — I'll build it for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://buy.stripe.com/4gMdRaetT3Yv3yxbOs2go03" rel="noopener noreferrer"&gt;$99 Done-For-You n8n Workflow →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You fill out a short intake form describing what you need (which events, what actions, what integrations). I build and deliver the workflow JSON within 48 hours, tested and ready to import.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Also publishing free n8n workflow guides and automation templates at &lt;a href="https://pirateprentice.gumroad.com" rel="noopener noreferrer"&gt;pirateprentice.gumroad.com&lt;/a&gt; — check there for the full library.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>stripe</category>
      <category>javascript</category>
    </item>
    <item>
      <title>n8n Question and Answer Chain Node: Build Retrieval-Augmented Workflows with Any Document [Free Workflow JSON]</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Sun, 05 Jul 2026 01:28:15 +0000</pubDate>
      <link>https://dev.to/pirateprentice/n8n-question-and-answer-chain-node-build-retrieval-augmented-workflows-with-any-document-free-3h0k</link>
      <guid>https://dev.to/pirateprentice/n8n-question-and-answer-chain-node-build-retrieval-augmented-workflows-with-any-document-free-3h0k</guid>
      <description>&lt;p&gt;The &lt;strong&gt;Question and Answer Chain node&lt;/strong&gt; is n8n's built-in RAG (Retrieval-Augmented Generation) node. You connect it to a vector store, give it a user question, and it retrieves relevant document chunks, passes them to a language model, and returns a grounded answer — all without writing code.&lt;/p&gt;

&lt;p&gt;This guide covers how the node works, the full wiring pattern, configuration options, the 6 gotchas that trip people up, and 3 production-ready workflow patterns with free JSON.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Question and Answer Chain Node Does
&lt;/h2&gt;

&lt;p&gt;The QA Chain node implements the classic RAG pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Retrieve&lt;/strong&gt; — embed the user's question and query a vector store for semantically similar chunks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Augment&lt;/strong&gt; — inject the retrieved chunks into a prompt as context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate&lt;/strong&gt; — pass the augmented prompt to a language model and return the answer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is an answer that's grounded in your actual documents — not just a hallucinated LLM response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical inputs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User questions (from a webhook, form, or chat trigger)&lt;/li&gt;
&lt;li&gt;A populated vector store (Pinecone, Qdrant, Weaviate, in-memory)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Typical outputs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A text answer grounded in retrieved context&lt;/li&gt;
&lt;li&gt;Optionally: source chunk metadata (which document the answer came from)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Node Wiring
&lt;/h2&gt;

&lt;p&gt;The QA Chain has two mandatory sub-node connections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Question source — Webhook / Chat Trigger / Form Trigger]
          ↓
[Question and Answer Chain]
    ↑              ↑
[Chat Model]  [Vector Store Retriever]
                    ↑
              [Vector Store node]
              (Pinecone / Qdrant / Weaviate / In-Memory)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Required connections:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chat Model&lt;/strong&gt; — the LLM that generates the answer (OpenAI, Anthropic, Gemini, Ollama, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector Store Retriever&lt;/strong&gt; — a retriever sub-node wrapping a populated vector store&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to connect the Vector Store Retriever:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a Vector Store node (e.g., Pinecone Vector Store)&lt;/li&gt;
&lt;li&gt;Set it to &lt;strong&gt;Retrieve Documents (For Agent/Chain)&lt;/strong&gt; mode&lt;/li&gt;
&lt;li&gt;Connect it to the QA Chain's Vector Store Retriever input&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Configuration Options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Query field
&lt;/h3&gt;

&lt;p&gt;The field on the incoming item that contains the user's question. Default is &lt;code&gt;query&lt;/code&gt; — if your question is in a different field (e.g., &lt;code&gt;message&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;question&lt;/code&gt;), change this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Number of documents to retrieve (Top K)
&lt;/h3&gt;

&lt;p&gt;How many chunks to pull from the vector store per query. Default is typically 4. Higher values give the LLM more context but increase token cost and can dilute relevance. For most use cases, 3–6 works well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Return source documents
&lt;/h3&gt;

&lt;p&gt;When enabled, the node includes the source chunk metadata in the output — which document, page, or chunk the answer came from. Essential for citation-backed answers and debugging.&lt;/p&gt;




&lt;h2&gt;
  
  
  6 Gotchas
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Your vector store must be pre-populated
&lt;/h3&gt;

&lt;p&gt;The QA Chain retrieves from an existing vector store — it does not ingest documents. You need a separate ingestion workflow that embeds your documents and stores them. The QA Chain is read-only at query time.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Embeddings model must match the ingestion model
&lt;/h3&gt;

&lt;p&gt;When you query, n8n embeds the question using the Embeddings model connected to your Vector Store Retriever. This &lt;strong&gt;must be the same model&lt;/strong&gt; (and same dimensions) used when you originally ingested the documents. Mismatched embeddings produce garbage retrieval — the cosine similarity scores will be meaningless.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Query field mismatch returns empty answers
&lt;/h3&gt;

&lt;p&gt;If your incoming question is in &lt;code&gt;message&lt;/code&gt; but the node is configured to read &lt;code&gt;query&lt;/code&gt;, it passes an empty string to the vector store. The retriever returns no chunks, and the LLM answers from its training data alone (or returns "I don't know"). Always verify the Query field setting matches your actual data structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Chunk size affects answer quality
&lt;/h3&gt;

&lt;p&gt;If chunks are too small (&amp;lt; 100 tokens), retrieved context is fragmented and the LLM can't synthesize a complete answer. If chunks are too large (&amp;gt; 1000 tokens), you hit context limits faster and pay more per query. 300–500 tokens per chunk with ~50-token overlap is a good default for most document types.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. The LLM still hallucinates without guardrails
&lt;/h3&gt;

&lt;p&gt;The QA Chain grounds the LLM in retrieved context, but it can still hallucinate if the retrieved chunks don't fully answer the question. Add an explicit instruction in your prompt to respond with "I don't have enough information to answer that" when context is insufficient. Some vector store + chain combos support this natively; check the node's prompt template options.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. In-Memory Vector Store doesn't persist across executions
&lt;/h3&gt;

&lt;p&gt;n8n's built-in in-memory vector store resets every workflow execution. It's useful for testing and single-run batch jobs but not for a live QA system where you want to query the same corpus repeatedly. For production, use Pinecone, Qdrant, or Weaviate.&lt;/p&gt;




&lt;h2&gt;
  
  
  3 Workflow Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pattern 1: Internal Knowledge Base Bot
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Your team has a Notion wiki, internal docs, or a Confluence space. You want a Slack or Teams bot that answers employee questions by searching the actual docs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ingestion workflow (runs once or on schedule):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP Request (fetch docs from Notion/Confluence API)
→ Code node (chunk text into ~400-token segments)
→ Embeddings node (OpenAI text-embedding-3-small)
→ Pinecone Vector Store (upsert chunks with metadata: doc_title, url, updated_at)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Query workflow (runs on each question):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook Trigger (Slack slash command or Events API)
→ Question and Answer Chain
    ↑ Chat Model (GPT-4o or Claude)
    ↑ Pinecone Vector Store Retriever (Top K: 5, return source docs: on)
→ Code node (format answer + source links)
→ HTTP Request (POST reply to Slack)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; The ingestion workflow keeps the vector store fresh. The query workflow is stateless and fast — each Slack question triggers a live retrieval + generation cycle. Source doc metadata lets you include links in the answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free JSON:&lt;/strong&gt; &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;Download the Knowledge Base Bot workflow →&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 2: PDF Document Q&amp;amp;A (Customer-Facing)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; You have product manuals, compliance documents, or legal agreements as PDFs. Customers or internal users need to ask questions about specific documents without reading them end to end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ingestion workflow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP Request (download PDF from URL or S3)
→ Extract From File node (read PDF as text)
→ Code node (chunk into 400-token segments, tag with doc_id metadata)
→ OpenAI Embeddings
→ Qdrant Vector Store (upsert with namespace = doc_id)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Query workflow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook Trigger (POST: { doc_id: "...", question: "..." })
→ Question and Answer Chain
    ↑ Chat Model
    ↑ Qdrant Vector Store Retriever (filter by doc_id namespace, Top K: 4)
→ HTTP Response (return answer JSON)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Namespacing by &lt;code&gt;doc_id&lt;/code&gt; lets you host thousands of documents in one vector store and scope each question to the relevant document. Users get answers grounded in the actual text, not a generic LLM response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free JSON:&lt;/strong&gt; &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;Download the PDF Q&amp;amp;A workflow →&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 3: Support Ticket Auto-Responder
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Your support team has hundreds of resolved tickets with proven answers. When new tickets arrive, you want to auto-draft a response based on similar past answers, then route to a human for review before sending.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ingestion workflow (batch, run periodically):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP Request (fetch resolved tickets from Zendesk/Linear API)
→ Filter node (only tickets with "resolved" + agent_rating &amp;gt; 4)
→ Code node (format as "Problem: ... Solution: ..." chunks)
→ Embeddings → Pinecone (upsert with ticket_id, category metadata)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Query workflow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook (new ticket created)
→ Question and Answer Chain
    ↑ Chat Model (with system prompt: "Draft a support reply based only on the provided context")
    ↑ Pinecone Retriever (Top K: 3, filter by ticket category)
→ HTTP Request (POST draft reply to Zendesk internal note)
→ Slack notification (agent review required)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; The LLM drafts based on proven past answers, not generic training data. Filtering by category improves retrieval precision. The human-in-the-loop step before sending keeps quality high.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free JSON:&lt;/strong&gt; &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;Download the Support Auto-Responder workflow →&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  QA Chain vs Other n8n AI Nodes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Node&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Question and Answer Chain&lt;/td&gt;
&lt;td&gt;Grounded answers from a pre-built document corpus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Basic LLM Chain&lt;/td&gt;
&lt;td&gt;Free-form text generation with no retrieval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Agent&lt;/td&gt;
&lt;td&gt;Multi-step reasoning with dynamic tool calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Information Extractor&lt;/td&gt;
&lt;td&gt;Pulling specific fields from a single piece of text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Summarization Chain&lt;/td&gt;
&lt;td&gt;Condensing long documents into shorter summaries&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use the QA Chain when you need answers &lt;strong&gt;grounded in your own documents&lt;/strong&gt;. Use Basic LLM Chain when you're fine with the model's training knowledge alone.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Node: Question and Answer Chain
Required sub-nodes:
  - Chat Model (mandatory)
  - Vector Store Retriever (mandatory — wraps a populated vector store)

Key settings:
  - Query field: must match incoming item field name
  - Top K: 3–6 chunks (tune for quality vs cost)
  - Return source documents: on for citation-backed answers

Gotchas:
  - Pre-populate your vector store in a separate ingestion workflow
  - Embeddings model must match between ingestion and query
  - In-memory vector store resets between executions
  - Add "I don't know" fallback instruction to your prompt
  - Verify Query field matches actual data structure
  - Chunk size: 300–500 tokens with overlap is a good default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Get the Free Workflow JSON
&lt;/h2&gt;

&lt;p&gt;All three patterns above are included in the &lt;strong&gt;n8n Workflow Packs&lt;/strong&gt; available on Gumroad. One download, instant access, plug the JSON into your n8n instance and go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;Download the n8n Workflow Pack&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Drop a comment below — I'm especially curious what vector store you're using and what document corpus you're building QA over.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>ai</category>
      <category>automation</category>
      <category>javascript</category>
    </item>
    <item>
      <title>n8n Information Extractor Node: Extract Structured Data from Text Using AI — Free Workflow JSON</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Sat, 04 Jul 2026 23:25:22 +0000</pubDate>
      <link>https://dev.to/pirateprentice/n8n-information-extractor-node-extract-structured-data-from-text-using-ai-free-workflow-json-380k</link>
      <guid>https://dev.to/pirateprentice/n8n-information-extractor-node-extract-structured-data-from-text-using-ai-free-workflow-json-380k</guid>
      <description>&lt;p&gt;The &lt;strong&gt;Information Extractor node&lt;/strong&gt; is one of n8n's most practical AI nodes. You give it unstructured text — an email, a PDF snippet, a support ticket, a scraped web page — and it uses a language model to pull out exactly the fields you define. No regex. No brittle parsing. Just structured JSON out.&lt;/p&gt;

&lt;p&gt;This guide covers everything: how the node works, schema definition, output modes, the 6 gotchas that trip people up, and 3 production-ready workflow patterns with free JSON.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Information Extractor Node Does
&lt;/h2&gt;

&lt;p&gt;The Information Extractor node wraps LangChain's structured output capability. You define a JSON schema (field names + types + descriptions), pass in a text input, and the connected Chat Model fills in the schema. The output is a single n8n item with your extracted fields as top-level keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical inputs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Email body text&lt;/li&gt;
&lt;li&gt;PDF or document content (after passing through Extract From File)&lt;/li&gt;
&lt;li&gt;Support ticket descriptions&lt;/li&gt;
&lt;li&gt;Web page text (after HTML Extract or HTTP Request)&lt;/li&gt;
&lt;li&gt;Form submission free-text fields&lt;/li&gt;
&lt;li&gt;API response descriptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Typical outputs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structured records ready for a database insert&lt;/li&gt;
&lt;li&gt;Enriched lead data&lt;/li&gt;
&lt;li&gt;Classified + tagged items&lt;/li&gt;
&lt;li&gt;Parsed addresses, dates, names, amounts&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Node Wiring
&lt;/h2&gt;

&lt;p&gt;The Information Extractor is an &lt;strong&gt;AI node&lt;/strong&gt; — it sits inside n8n's LangChain sub-graph and requires a &lt;strong&gt;Chat Model&lt;/strong&gt; sub-node connected to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Your text source]
       ↓
[Information Extractor]
       ↑ (Chat Model connection)
[OpenAI / Anthropic / Gemini Chat Model]
       ↓
[Next node — uses extracted fields]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Required connection:&lt;/strong&gt; Chat Model (any: OpenAI, Anthropic Claude, Google Gemini, Ollama, etc.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optional connections:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory node (rarely needed for extraction tasks)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Schema Definition
&lt;/h2&gt;

&lt;p&gt;This is where most of the work happens. You define your extraction schema inside the node's &lt;strong&gt;Attributes&lt;/strong&gt; section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Attributes
&lt;/h3&gt;

&lt;p&gt;Each attribute has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; — the output field key (e.g., &lt;code&gt;customer_name&lt;/code&gt;, &lt;code&gt;invoice_total&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type&lt;/strong&gt; — String, Number, Boolean, or Object (nested)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt; — critical: the LLM uses this to understand what to extract. Be specific.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Good vs Bad Descriptions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Bad description&lt;/th&gt;
&lt;th&gt;Good description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;amount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"The amount"&lt;/td&gt;
&lt;td&gt;"Total invoice amount as a number, excluding currency symbols and commas (e.g. 1250.00)"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sentiment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"Sentiment"&lt;/td&gt;
&lt;td&gt;"Customer sentiment: exactly one of positive, negative, or neutral"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;date&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"The date"&lt;/td&gt;
&lt;td&gt;"Invoice date in ISO 8601 format (YYYY-MM-DD). If no year, assume current year."&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; Write descriptions as if you're telling a smart intern exactly what to write in that field. Ambiguous descriptions produce inconsistent outputs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optional vs Required
&lt;/h3&gt;

&lt;p&gt;By default, all attributes are optional — the LLM will return &lt;code&gt;null&lt;/code&gt; for fields it can't find. You can mark fields as required; the model will try harder but may hallucinate if the data isn't there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Output Mode
&lt;/h2&gt;

&lt;p&gt;The node has one primary output mode: it emits one item per input item, with your defined fields added to (or replacing) the item's JSON.&lt;/p&gt;

&lt;p&gt;The extracted fields appear at the top level of the output item's &lt;code&gt;json&lt;/code&gt; object, accessible as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;customer_name&lt;/span&gt; &lt;span class="k"&gt;}}&lt;/span&gt;
&lt;span class="k"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;invoice_total&lt;/span&gt; &lt;span class="k"&gt;}}&lt;/span&gt;
&lt;span class="k"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;sentiment&lt;/span&gt; &lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6 Gotchas
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The Chat Model connection is mandatory — and often forgotten
&lt;/h3&gt;

&lt;p&gt;If you drop the Information Extractor node and run without wiring a Chat Model sub-node, you'll get a connection error. Always add the Chat Model sub-node first. It appears as a small input handle at the bottom of the node.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Input field selection matters
&lt;/h3&gt;

&lt;p&gt;The node has an &lt;strong&gt;Input&lt;/strong&gt; field where you specify which field from the incoming item contains the text to extract from. Default is often &lt;code&gt;text&lt;/code&gt;. If your text is in &lt;code&gt;body&lt;/code&gt;, &lt;code&gt;content&lt;/code&gt;, or &lt;code&gt;description&lt;/code&gt;, change this — otherwise the model gets an empty string and returns nulls.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Long texts hit context limits
&lt;/h3&gt;

&lt;p&gt;The LLM must read your entire input text. Very long documents (PDFs, long emails) can exceed the model's context window. Pre-chunk with the &lt;strong&gt;Summarization Chain&lt;/strong&gt; node or truncate with a &lt;strong&gt;Code node&lt;/strong&gt; before feeding into the extractor. For multi-page PDFs, extract page by page.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Hallucination on missing fields
&lt;/h3&gt;

&lt;p&gt;If a field genuinely doesn't exist in the text and you've marked it required, the model may fabricate a plausible-sounding value. For required fields where absence is meaningful, use Optional + check for null downstream rather than Required.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Nested objects need careful schema design
&lt;/h3&gt;

&lt;p&gt;You can define Object-type attributes with nested keys, but the deeper the nesting, the more the model struggles to fill it correctly. Flatten your schema where possible; one level of nesting is usually fine, two levels degrades reliability.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Temperature and model choice affect consistency
&lt;/h3&gt;

&lt;p&gt;Higher-temperature models produce more variable extractions. For structured extraction, use a model/temperature that favors precision over creativity. With OpenAI, &lt;code&gt;gpt-4o-mini&lt;/code&gt; at temperature 0 is a reliable cost-effective choice. With Anthropic, &lt;code&gt;claude-haiku-4-5&lt;/code&gt; works well for simple schemas; use &lt;code&gt;claude-sonnet-4-6&lt;/code&gt; for complex ones.&lt;/p&gt;




&lt;h2&gt;
  
  
  3 Workflow Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pattern 1: Email Lead Extractor
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Inbound emails arrive at a support inbox. You want to extract lead data (name, company, use case, budget range) and write it to a CRM or Google Sheet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gmail Trigger (new email)
  → Information Extractor
      Schema: sender_name (String), company (String), use_case (String), budget_range (String), urgency (String: low/medium/high)
      Input field: text (email body)
  → Google Sheets (append row) OR HTTP Request (POST to CRM API)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Email bodies are unstructured but the signal is there. The extractor turns "Hi, I'm Jane from Acme Corp, we need to automate our invoice reconciliation, budget around $5k" into a clean structured record in one step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free JSON:&lt;/strong&gt; &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;Download the Email Lead Extractor workflow →&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 2: Support Ticket Classifier and Router
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Support tickets come in via webhook (Typeform, Jotform, or your app). You want to extract the issue category, severity, and affected product, then route to the right Slack channel or Linear project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook Trigger (new ticket)
  → Information Extractor
      Schema: issue_category (String: billing/technical/account/feature-request), severity (String: low/medium/high/critical), affected_product (String), customer_tier (String: free/pro/enterprise)
      Input field: description
  → Switch node (route on issue_category)
      → Slack #billing-support / #tech-support / etc.
      → Linear (create issue with extracted fields)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Routing logic that used to require keyword matching or a full classification model becomes a 2-node pattern. The extracted &lt;code&gt;severity&lt;/code&gt; and &lt;code&gt;customer_tier&lt;/code&gt; drive SLA assignment downstream.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free JSON:&lt;/strong&gt; &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;Download the Support Ticket Router workflow →&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 3: Invoice Data Extractor (PDF → Database)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; You receive invoices as PDF attachments via email. You want to extract invoice number, vendor, date, line items total, and due date, then insert them into Postgres or Airtable for AP tracking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gmail Trigger (attachment: PDF)
  → Extract From File node (Read PDF as text)
  → Information Extractor
      Schema: invoice_number (String), vendor_name (String), invoice_date (String: YYYY-MM-DD), total_amount (Number), due_date (String: YYYY-MM-DD), currency (String)
      Input field: text
  → Postgres node (INSERT) OR Airtable node (Create Record)
  → Gmail (Send confirmation reply to sender)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; PDF invoice parsing used to require paid OCR services or brittle regex. With n8n's Extract From File + Information Extractor, you get structured AP data from any invoice format in seconds — and it handles vendor-to-vendor format differences automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free JSON:&lt;/strong&gt; &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;Download the Invoice Extractor workflow →&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Information Extractor vs Other AI Nodes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Node&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Information Extractor&lt;/td&gt;
&lt;td&gt;Structured field extraction from unstructured text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Basic LLM Chain&lt;/td&gt;
&lt;td&gt;Free-form text generation, classification, summarization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Summarization Chain&lt;/td&gt;
&lt;td&gt;Condensing long documents into shorter summaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Agent&lt;/td&gt;
&lt;td&gt;Multi-step reasoning, tool use, decisions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sentiment Analysis (via LLM Chain)&lt;/td&gt;
&lt;td&gt;Single-dimension classification&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use Information Extractor when you need &lt;strong&gt;specific named fields&lt;/strong&gt; out of text. Use Basic LLM Chain when you need a &lt;strong&gt;free-form text response&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Node: Information Extractor
Requires: Chat Model sub-node (mandatory)
Input field: configure to match your data field
Output: extracted fields added to item JSON

Schema tips:
- Name: snake_case, descriptive
- Type: String / Number / Boolean / Object
- Description: be specific — the LLM reads this

Gotchas:
- Wire Chat Model first
- Set correct Input field
- Chunk long texts before extracting
- Use Optional fields for data that may be absent
- Flatten nested schemas
- Low temperature = more consistent extraction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Get the Free Workflow JSON
&lt;/h2&gt;

&lt;p&gt;All three patterns above are included in the &lt;strong&gt;n8n Workflow Packs&lt;/strong&gt; available on Gumroad. One download, instant access, plug the JSON into your n8n instance and go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;Download the n8n Workflow Pack&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Drop a comment below — I'm especially curious which extraction use case you're tackling and which model you're using.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>ai</category>
      <category>automation</category>
      <category>javascript</category>
    </item>
    <item>
      <title>n8n Summarization Chain Node: Summarize Long Documents and Web Pages in Your Workflows [Free Workflow JSON]</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Sat, 04 Jul 2026 19:27:25 +0000</pubDate>
      <link>https://dev.to/pirateprentice/n8n-summarization-chain-node-summarize-long-documents-and-web-pages-in-your-workflows-free-1fm</link>
      <guid>https://dev.to/pirateprentice/n8n-summarization-chain-node-summarize-long-documents-and-web-pages-in-your-workflows-free-1fm</guid>
      <description>&lt;h1&gt;
  
  
  n8n Summarization Chain Node: Summarize Long Documents and Web Pages in Your Workflows [Free Workflow JSON]
&lt;/h1&gt;

&lt;p&gt;The &lt;strong&gt;Summarization Chain&lt;/strong&gt; node in n8n lets you condense long text — PDFs, emails, web pages, transcripts, knowledge-base articles — into concise summaries using any supported language model, without writing a single line of code. This guide covers every setting, the three summarization strategies, common gotchas, and three ready-to-use workflow patterns you can copy today.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Summarization Chain Node Does
&lt;/h2&gt;

&lt;p&gt;The Summarization Chain node wraps LangChain's summarization chain and exposes it as a native n8n node. You feed it a Document (from a Document Loader or a Convert-to-Document node), attach a Chat Model, and get back a summary string.&lt;/p&gt;

&lt;p&gt;It handles chunking, token management, and multi-pass summarization automatically — no manual splitting required.&lt;/p&gt;




&lt;h2&gt;
  
  
  Required Inputs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chat Model&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Any n8n-supported LLM (OpenAI, Anthropic, Mistral, Ollama, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;One or more Document objects (from Document Loader sub-nodes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The node accepts &lt;strong&gt;LangChain Document objects&lt;/strong&gt;, not raw text strings. You must pass data through a Document Loader (Default Data Loader, PDF Loader, etc.) or use the &lt;code&gt;convertToDocument&lt;/code&gt; expression helper.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summarization Type (Strategy)
&lt;/h2&gt;

&lt;p&gt;The node offers three strategies. Select in the &lt;strong&gt;Summarization Type&lt;/strong&gt; dropdown.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Map Reduce (default — recommended for long documents)
&lt;/h3&gt;

&lt;p&gt;Splits the document into chunks, summarizes each chunk independently ("map"), then summarizes all chunk summaries together ("reduce").&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Best for: PDFs, long transcripts, knowledge-base articles, multi-page docs&lt;/li&gt;
&lt;li&gt;Token cost: Moderate (each chunk + final reduce)&lt;/li&gt;
&lt;li&gt;Quality: High — each chunk gets full model attention&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Settings exposed:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Combine Map Summaries Prompt&lt;/strong&gt; — prompt used in the reduce step (customize to add structure)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Refine
&lt;/h3&gt;

&lt;p&gt;Processes chunks sequentially: summarizes the first chunk, then passes that summary + the next chunk to the model to produce a refined summary, repeating until done.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Best for: Narrative text where context flows chronologically (meeting transcripts, stories, emails)&lt;/li&gt;
&lt;li&gt;Token cost: Higher — each step sees growing context&lt;/li&gt;
&lt;li&gt;Quality: Coherent narrative output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Settings exposed:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Refine Prompt&lt;/strong&gt; — the instruction given at each refinement step&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Stuff
&lt;/h3&gt;

&lt;p&gt;Stuffs all document content into a single prompt and summarizes in one pass.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Best for: Short documents that fit within the model's context window&lt;/li&gt;
&lt;li&gt;Token cost: Lowest&lt;/li&gt;
&lt;li&gt;Gotcha: &lt;strong&gt;Fails silently or hallucinates if the document exceeds the model's context limit.&lt;/strong&gt; Only use for documents you know are short.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prompt Customization
&lt;/h2&gt;

&lt;p&gt;All three strategies expose prompt fields. The default prompts are generic ("Write a concise summary of the following…"). Override them to get structured output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Summarize the following customer support ticket in 2–3 sentences.
Focus on: (1) the customer's core problem, (2) what was tried, (3) the resolution status.
Reply in plain text only.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prompts use &lt;code&gt;{text}&lt;/code&gt; as the placeholder for document content. Do not remove it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Output
&lt;/h2&gt;

&lt;p&gt;The node outputs a single item with one field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your summary text here..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access it downstream with &lt;code&gt;{{ $json.response }}&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  6 Common Gotchas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Passing raw text instead of a Document object&lt;/strong&gt;&lt;br&gt;
The node rejects plain strings. Wrap raw text using a Document Loader node set to "Default Data Loader" with your text in the Data field, or use &lt;code&gt;convertToDocument()&lt;/code&gt; in a Code node.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Stuff strategy context-window overflow&lt;/strong&gt;&lt;br&gt;
There is no automatic guard. If your text exceeds the model's context limit, the model truncates silently or errors. Use Map Reduce for anything longer than ~3,000 words.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. No &lt;code&gt;{text}&lt;/code&gt; placeholder in custom prompts&lt;/strong&gt;&lt;br&gt;
The node injects document content via &lt;code&gt;{text}&lt;/code&gt;. If you remove this placeholder from a custom prompt, the model summarizes an empty string and returns generic filler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Slow execution on Map Reduce with many chunks&lt;/strong&gt;&lt;br&gt;
Each chunk is a separate API call. A 50-page PDF with 1,500-token chunks and a Map Reduce strategy may make 20+ API calls. Watch rate limits and latency. Use a faster/cheaper model (e.g., GPT-4o-mini, claude-haiku) for the map step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Document Loader node required — not an HTTP Request node output&lt;/strong&gt;&lt;br&gt;
You cannot directly connect an HTTP Request node to the Document input. You need a Document Loader (e.g., the "Default Data Loader" or "PDF Loader" sub-node) between them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Summarization Chain vs Basic LLM Chain for summarization&lt;/strong&gt;&lt;br&gt;
You &lt;em&gt;can&lt;/em&gt; summarize with a Basic LLM Chain by passing text in the prompt — but you must handle chunking yourself. The Summarization Chain handles chunking automatically. For documents that reliably fit in context, Basic LLM Chain is simpler; for variable-length documents, always use Summarization Chain.&lt;/p&gt;


&lt;h2&gt;
  
  
  3 Workflow Patterns
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Pattern 1: PDF Inbox Summarizer → Slack
&lt;/h3&gt;

&lt;p&gt;Trigger: Gmail Attachment Trigger (PDF received)&lt;br&gt;
→ Download attachment (HTTP Request or Gmail node)&lt;br&gt;
→ Default Data Loader (PDF Loader)&lt;br&gt;
→ Summarization Chain (Map Reduce, custom prompt: "Summarize this document in 5 bullet points, focusing on action items.")&lt;br&gt;
→ Slack node (post to #documents channel with &lt;code&gt;{{ $json.response }}&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Use case: Legal, finance, and ops teams get instant Slack summaries of every inbound PDF without opening the file.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pattern 2: Customer Support Ticket Summarizer
&lt;/h3&gt;

&lt;p&gt;Trigger: Webhook (new ticket from Zendesk/Freshdesk)&lt;br&gt;
→ Default Data Loader (ticket body + thread history as text)&lt;br&gt;
→ Summarization Chain (Stuff — tickets are short; Map Reduce for long threads)&lt;br&gt;
→ HTTP Request (PATCH ticket — write summary to custom field)&lt;/p&gt;

&lt;p&gt;Use case: Agents see a 2-sentence summary at the top of every ticket before reading the thread. Cuts handle time.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pattern 3: Nightly News Digest
&lt;/h3&gt;

&lt;p&gt;Trigger: Schedule Trigger (daily at 6 AM)&lt;br&gt;
→ RSS Feed Read node (10+ tech feeds)&lt;br&gt;
→ Split In Batches (process each article URL)&lt;br&gt;
→ HTTP Request (fetch article HTML)&lt;br&gt;
→ HTML Extract (extract body text)&lt;br&gt;
→ Default Data Loader&lt;br&gt;
→ Summarization Chain (Map Reduce, prompt: "Summarize this article in 3 sentences. Include the main claim and any data points.")&lt;br&gt;
→ Aggregate (collect all summaries)&lt;br&gt;
→ Basic LLM Chain (combine into a single digest email)&lt;br&gt;
→ Send Email (Gmail node)&lt;/p&gt;

&lt;p&gt;Use case: Daily digest of 10 articles in under 30 minutes of reading — all AI-summarized.&lt;/p&gt;


&lt;h2&gt;
  
  
  Free Workflow JSON
&lt;/h2&gt;

&lt;p&gt;Here is a minimal Summarization Chain workflow you can import directly into n8n:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Summarization Chain — Starter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"When clicking 'Test workflow'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.manualTrigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"dataType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"defineBelow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"jsonData"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"=Your long text goes here. Replace this with {{ $json.body }} or any text field from an upstream node."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Default Data Loader"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@n8n/n8n-nodes-langchain.documentDefaultDataLoader"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;420&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gpt-4o-mini"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OpenAI Chat Model"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@n8n/n8n-nodes-langchain.lmChatOpenAi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"summarizationMethodAndPrompts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"summarizationMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mapReduce"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Summarization Chain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@n8n/n8n-nodes-langchain.chainSummarization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;750&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"When clicking 'Test workflow'"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Summarization Chain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Default Data Loader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ai_document"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Summarization Chain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ai_document"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"OpenAI Chat Model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ai_languageModel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Summarization Chain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ai_languageModel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Want a complete implementation of any of the three patterns above?&lt;/strong&gt; The &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;n8n Workflow Starter Pack&lt;/a&gt; includes production-ready versions of all three patterns with error handling, retry logic, and Slack/email delivery built in.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summarization Chain vs Other n8n Nodes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Node&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Chunking&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Summarization Chain&lt;/td&gt;
&lt;td&gt;Long documents of any length&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;td&gt;Summary string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Basic LLM Chain&lt;/td&gt;
&lt;td&gt;Short text, custom prompts&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Any LLM output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Agent node&lt;/td&gt;
&lt;td&gt;Multi-step reasoning, tool calls&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Agent response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Information Extractor&lt;/td&gt;
&lt;td&gt;Structured data from text&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;JSON schema&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Map Reduce&lt;/strong&gt; for anything over ~3,000 words&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Refine&lt;/strong&gt; for narrative content where sequence matters&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Stuff&lt;/strong&gt; only for short, known-length documents&lt;/li&gt;
&lt;li&gt;Always customize the prompt — the default is generic&lt;/li&gt;
&lt;li&gt;Output is always &lt;code&gt;{{ $json.response }}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Pair with Document Loaders, not raw text nodes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Summarization Chain is the cleanest way to add document intelligence to any n8n workflow without managing chunking, token limits, or multi-step prompts yourself.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building document processing pipelines with n8n? The &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;n8n Workflow Starter Pack&lt;/a&gt; includes production-ready summarization workflows for PDF inboxes, support ticket summarizers, and nightly news digests — $29, instant download.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>n8n Basic LLM Chain Node: Add Language Model Text Generation to Any Workflow [Free Workflow JSON]</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Sat, 04 Jul 2026 19:23:32 +0000</pubDate>
      <link>https://dev.to/pirateprentice/n8n-basic-llm-chain-node-add-language-model-text-generation-to-any-workflow-free-workflow-json-44op</link>
      <guid>https://dev.to/pirateprentice/n8n-basic-llm-chain-node-add-language-model-text-generation-to-any-workflow-free-workflow-json-44op</guid>
      <description>&lt;h1&gt;
  
  
  n8n Basic LLM Chain Node: Add Language Model Text Generation to Any Workflow [Free Workflow JSON]
&lt;/h1&gt;

&lt;p&gt;tags: n8n, automation, ai, javascript&lt;/p&gt;




&lt;p&gt;The &lt;strong&gt;n8n Basic LLM Chain node&lt;/strong&gt; is the simplest way to add language model text generation to an n8n workflow. You wire it to any Chat Model node (OpenAI, Anthropic, Google, Ollama, etc.), pass it a prompt, and get a text response back — no agent loop, no tool calls, no memory overhead.&lt;/p&gt;

&lt;p&gt;This guide covers every configuration option, the gotchas that catch people out, three production-ready patterns, and a free workflow JSON you can import today.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Basic LLM Chain node does
&lt;/h2&gt;

&lt;p&gt;The Basic LLM Chain is a LangChain wrapper that executes a single prompt → response cycle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Takes one or more input items (text, data fields, whatever your workflow has produced)&lt;/li&gt;
&lt;li&gt;Builds a prompt from a template you define (with access to all input fields via &lt;code&gt;{{ $json.fieldName }}&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Sends it to a connected Chat Model sub-node&lt;/li&gt;
&lt;li&gt;Returns the model's text response as &lt;code&gt;{ "text": "..." }&lt;/code&gt; on each output item&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; loop, call tools, use memory, or reason across steps. For those, use the AI Agent node. The Basic LLM Chain is for single-shot generation: summarise, classify, extract, rewrite, translate, score.&lt;/p&gt;




&lt;h2&gt;
  
  
  Node anatomy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Required sub-node: Chat Model
&lt;/h3&gt;

&lt;p&gt;The Basic LLM Chain has no built-in model. You must attach a Chat Model node as a child:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI Chat Model&lt;/strong&gt; — GPT-4o, GPT-4o-mini, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anthropic Chat Model&lt;/strong&gt; — claude-opus-4, claude-sonnet-4-6, claude-haiku-4-5&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Gemini Chat Model&lt;/strong&gt; — gemini-2.5-flash, gemini-2.5-pro&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ollama Chat Model&lt;/strong&gt; — local models (llama3, mistral, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure OpenAI Chat Model&lt;/strong&gt; — enterprise Azure deployments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mistral Cloud Chat Model&lt;/strong&gt;, &lt;strong&gt;Cohere&lt;/strong&gt;, &lt;strong&gt;Groq&lt;/strong&gt;, and others&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drag the Chat Model node onto the canvas, then connect it to the Basic LLM Chain's &lt;strong&gt;AI Language Model&lt;/strong&gt; input (the purple connector).&lt;/p&gt;

&lt;h3&gt;
  
  
  Optional sub-node: Output Parser
&lt;/h3&gt;

&lt;p&gt;By default, the chain returns raw text in &lt;code&gt;{ "text": "..." }&lt;/code&gt;. If you need structured output (JSON, a list, a specific schema), attach an Output Parser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured Output Parser&lt;/strong&gt; — define a Zod/JSON schema; the model is instructed to return JSON matching it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-fixing Output Parser&lt;/strong&gt; — wraps another parser; if the model returns malformed JSON it sends a correction prompt automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;List Output Parser&lt;/strong&gt; — returns a simple array of strings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Item List Output Parser&lt;/strong&gt; — splits a comma/newline-separated list into individual items&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bytes Output Parser&lt;/strong&gt; — returns raw bytes (rare)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prompt template
&lt;/h3&gt;

&lt;p&gt;The main configuration field is the &lt;strong&gt;Prompt&lt;/strong&gt; (or &lt;strong&gt;User Message&lt;/strong&gt; if using a chat-style template). This is a Mustache/Handlebars-style template with access to the current item's fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Summarise the following support ticket in one sentence. Be concise.

Ticket subject: {{ $json.subject }}
Ticket body: {{ $json.body }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;n8n evaluates &lt;code&gt;{{ $json.fieldName }}&lt;/code&gt; at runtime for each item, so one chain node handles batches automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  System message
&lt;/h3&gt;

&lt;p&gt;An optional &lt;strong&gt;System Message&lt;/strong&gt; field sets the model's persona/instructions. Keep it short and direct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a helpful assistant. Respond only in plain text with no markdown formatting.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Temperature and other model params
&lt;/h3&gt;

&lt;p&gt;Temperature, max tokens, top-p, and stop sequences are configured on the &lt;strong&gt;Chat Model&lt;/strong&gt; sub-node, not on the Basic LLM Chain itself. Adjust them there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. You must wire a Chat Model — the node won't run without one&lt;/strong&gt;&lt;br&gt;
The Basic LLM Chain has no fallback model. If the AI Language Model input is unwired, execution fails immediately. Always connect a Chat Model sub-node first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;{{ $json }}&lt;/code&gt; in prompts — not &lt;code&gt;{{ $item.json }}&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Inside the Prompt field, n8n uses Mustache syntax. The correct reference is &lt;code&gt;{{ $json.fieldName }}&lt;/code&gt;, not the expression syntax &lt;code&gt;{{ $item.json.fieldName }}&lt;/code&gt; used in other node fields. Using expression syntax here produces literal text, not the value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Output is always &lt;code&gt;{ "text": "..." }&lt;/code&gt; unless you attach an Output Parser&lt;/strong&gt;&lt;br&gt;
Downstream nodes expecting a field like &lt;code&gt;summary&lt;/code&gt; or &lt;code&gt;category&lt;/code&gt; won't find it. Either attach an Output Parser, or add a Set node after the chain to rename &lt;code&gt;text&lt;/code&gt; to your preferred field name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. One LLM call per input item&lt;/strong&gt;&lt;br&gt;
The Basic LLM Chain runs once per input item. If your upstream node produces 500 items, you'll make 500 model calls. For large batches, add a Split In Batches node upstream and respect rate limits on the model sub-node.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Context window limits&lt;/strong&gt;&lt;br&gt;
If you're passing long documents into the prompt, watch token counts. The chain doesn't chunk automatically. For long-document summarisation, use the Summarization Chain node instead — it handles chunking and map-reduce strategies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. The chain returns a new item, not a merged item&lt;/strong&gt;&lt;br&gt;
The output item contains only &lt;code&gt;{ "text": "..." }&lt;/code&gt; (or your parsed output). Input fields are not automatically carried forward. Use a Merge node to join the chain's output back to the original item if you need both.&lt;/p&gt;


&lt;h2&gt;
  
  
  Three production-ready patterns
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Pattern 1: Support ticket classifier
&lt;/h3&gt;

&lt;p&gt;Classify incoming support tickets by category and urgency so they can be routed without human triage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;br&gt;
Webhook Trigger → Basic LLM Chain (+ OpenAI Chat Model + Structured Output Parser) → Switch node → Slack/email routing&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Classify this support ticket.

Subject: {{ $json.subject }}
Body: {{ $json.body }}

Return JSON with:
- category: one of ["billing", "technical", "account", "general"]
- urgency: one of ["low", "medium", "high"]
- one_line_summary: string (max 15 words)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Structured Output Parser schema:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"urgency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"one_line_summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Downstream:&lt;/strong&gt; Switch node routes on &lt;code&gt;urgency&lt;/code&gt; → Slack alert for "high", Zendesk ticket for "medium"/"low".&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 2: Blog post meta generator
&lt;/h3&gt;

&lt;p&gt;Given a draft blog post body, generate SEO title, meta description, and 5 tags automatically before publishing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;br&gt;
Google Drive Trigger (new file) → Google Drive node (read content) → Basic LLM Chain (+ Anthropic Chat Model + Structured Output Parser) → Google Sheets node (write metadata row) → Notify&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an SEO copywriter. Given the blog post below, generate:
- An SEO title (max 60 characters, front-load the main keyword)
- A meta description (max 155 characters, include a call to action)
- Five relevant tags (lowercase, hyphenated)

Blog post:
{{ $json.content }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Structured Output Parser schema:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"seo_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"meta_description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern runs zero-cost on haiku/flash for short posts; reserve Sonnet/GPT-4o for posts where quality matters most.&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 3: Customer review sentiment + response draft
&lt;/h3&gt;

&lt;p&gt;Process incoming product reviews: extract sentiment, key themes, and draft a personalised response — ready for a human to approve before sending.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;br&gt;
Schedule Trigger → HTTP Request (fetch reviews from API) → Split Out (one item per review) → Basic LLM Chain (+ Claude Haiku + Structured Output Parser) → Google Sheets append → Slack digest&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Analyse this product review and draft a response.

Review ({{ $json.rating }} stars):
{{ $json.review_text }}
Product: {{ $json.product_name }}

Return:
- sentiment: "positive" | "neutral" | "negative"
- themes: array of up to 3 key themes mentioned
- response_draft: a polite, personalised 2-3 sentence response from the brand
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response draft goes into Sheets; a Slack digest shows the day's reviews with one-click links to approve/edit responses. The human touches only the outliers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Free workflow JSON
&lt;/h2&gt;

&lt;p&gt;Import this into n8n (&lt;strong&gt;Workflows → Import from clipboard&lt;/strong&gt;) to get a working Basic LLM Chain skeleton with a Webhook trigger, chain, and Structured Output Parser wired up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Basic LLM Chain — Starter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"httpMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"llm-chain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"responseMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseNode"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"webhook-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Webhook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.webhook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.prompt }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"llm-chain-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Basic LLM Chain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@n8n/n8n-nodes-langchain.chainLlm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;460&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"respondWith"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"responseBody"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ { &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;result&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: $json.text } }}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"respond-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Respond to Webhook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.respondToWebhook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"typeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;680&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Webhook"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Basic LLM Chain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Basic LLM Chain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Respond to Webhook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; After importing, add your Chat Model sub-node (e.g. OpenAI Chat Model) connected to the Basic LLM Chain's AI Language Model input. The workflow won't run without it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  When to use Basic LLM Chain vs AI Agent
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Basic LLM Chain&lt;/th&gt;
&lt;th&gt;AI Agent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Single prompt → text response&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;overkill&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Classify / summarise / extract&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;overkill&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured JSON output&lt;/td&gt;
&lt;td&gt;✅ (+ Output Parser)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-step reasoning&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool calls (web search, APIs)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversation memory&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cheaper / faster&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Rule of thumb: if you can write the task as a single prompt, use the Basic LLM Chain. If the task requires the model to decide &lt;em&gt;what to do next&lt;/em&gt;, use an AI Agent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick-start checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Add a Chat Model sub-node and connect it to the Basic LLM Chain's purple input&lt;/li&gt;
&lt;li&gt;[ ] Write your prompt using &lt;code&gt;{{ $json.fieldName }}&lt;/code&gt; for dynamic values&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.toOptional"&gt; &lt;/a&gt; Add an Output Parser sub-node for structured output&lt;/li&gt;
&lt;li&gt;[ ] Add a Set node downstream if you need to rename &lt;code&gt;text&lt;/code&gt; to a specific field&lt;/li&gt;
&lt;li&gt;[ ] Test with a single item before running on large batches&lt;/li&gt;
&lt;li&gt;[ ] Set temperature and max tokens on the Chat Model sub-node&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The Basic LLM Chain is n8n's workhorse for single-shot AI tasks. Wire it once, feed it your data, and every item in your workflow gets an LLM pass — classification, summarisation, extraction, drafting — without touching the agent complexity. Start here, move to the AI Agent only when you genuinely need it.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>n8n Execute Workflow Node: Call Sub-Workflows and Build Modular Automations [Free Workflow JSON]</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Sat, 04 Jul 2026 15:25:43 +0000</pubDate>
      <link>https://dev.to/pirateprentice/n8n-execute-workflow-node-call-sub-workflows-and-build-modular-automations-free-workflow-json-4c8d</link>
      <guid>https://dev.to/pirateprentice/n8n-execute-workflow-node-call-sub-workflows-and-build-modular-automations-free-workflow-json-4c8d</guid>
      <description>&lt;p&gt;The &lt;strong&gt;n8n Execute Workflow node&lt;/strong&gt; lets one workflow call another workflow as a sub-routine. It's the foundation of modular n8n architecture: instead of one giant workflow that does everything, you build small, tested, reusable workflows and compose them together.&lt;/p&gt;

&lt;p&gt;This guide covers every configuration option, the gotchas that trip people up, three production-ready patterns, and a free workflow JSON.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Execute Workflow node does
&lt;/h2&gt;

&lt;p&gt;The Execute Workflow node &lt;strong&gt;synchronously calls another n8n workflow&lt;/strong&gt; and waits for it to finish. The called workflow runs, produces output, and that output lands in the calling workflow as items — just like data from any other node.&lt;/p&gt;

&lt;p&gt;This unlocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Modular design&lt;/strong&gt; — shared logic lives in one place, called from many workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable sub-routines&lt;/strong&gt; — auth checks, data transforms, notification dispatchers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testable units&lt;/strong&gt; — test and debug each sub-workflow independently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loop control&lt;/strong&gt; — use Execute Workflow inside a Split In Batches loop to call logic per item&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Trigger requirement in the called workflow
&lt;/h2&gt;

&lt;p&gt;The called workflow must have an &lt;strong&gt;Execute Workflow Trigger&lt;/strong&gt; node as its entry point (not a Webhook or Schedule Trigger). This trigger receives the data passed by the parent workflow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Parent workflow → Execute Workflow node → Called workflow (starts at Execute Workflow Trigger)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the called workflow starts with any other trigger, n8n won't know where to inject the input data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Configuration parameters
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Where to find the workflow to call: &lt;em&gt;Database&lt;/em&gt; (by workflow ID from the same n8n instance), &lt;em&gt;Define below&lt;/em&gt; (inline workflow JSON, for testing). Use &lt;em&gt;Database&lt;/em&gt; in production.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Workflow ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The numeric ID of the workflow to execute. Find it in the URL when you open the workflow in the n8n editor: &lt;code&gt;/workflow/12345&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Wait for Sub-Workflow Completion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Toggle. On (default) = the parent waits for the child to finish and receives its output. Off = fire-and-forget, no output returned.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fields to pass&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Which fields from the current item to send to the child workflow. Leave empty to pass all fields.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Run Once with All Items&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Off (default) = the child workflow is called once per item (loops over your dataset). On = the child is called once with all items together (bulk mode).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Data flow
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Parent → Child:&lt;/strong&gt;&lt;br&gt;
The items flowing into the Execute Workflow node are passed to the Execute Workflow Trigger in the child. Use &lt;code&gt;{{ $json.fieldName }}&lt;/code&gt; expressions inside the child workflow to access them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Child → Parent:&lt;/strong&gt;&lt;br&gt;
Whatever the child workflow's last node outputs becomes the output of the Execute Workflow node in the parent. Shape the child's output with a Set node at the end if you need a consistent schema.&lt;/p&gt;


&lt;h2&gt;
  
  
  Gotchas &amp;amp; common errors
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. The called workflow must be active
&lt;/h3&gt;

&lt;p&gt;The Execute Workflow node calls the workflow by ID — but if the called workflow is deactivated (or has never been saved/activated), it will fail. Keep shared sub-workflows in "active" state even if they don't have a schedule or webhook.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Workflow ID changes on export/import
&lt;/h3&gt;

&lt;p&gt;If you export a workflow and re-import it (to a new n8n instance or environment), it gets a new ID. Hard-coded workflow IDs in Execute Workflow nodes will break. Use environment variables or a lookup mechanism for the ID in multi-environment setups.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Infinite loops
&lt;/h3&gt;

&lt;p&gt;If Workflow A calls Workflow B, and Workflow B calls Workflow A, you'll create an infinite loop that will exhaust memory. n8n doesn't detect this automatically. Sketch your workflow dependency graph before building.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Error propagation
&lt;/h3&gt;

&lt;p&gt;Errors thrown inside the child workflow propagate to the parent and cause the Execute Workflow node to error. If you want the parent to continue even when the child fails, wrap the Execute Workflow node in an Error Trigger or set up a Try/Catch pattern with the workflow's Error Trigger workflow.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Run Once with All Items changes the output shape
&lt;/h3&gt;

&lt;p&gt;In per-item mode (default), each child execution returns one item. In bulk mode (&lt;code&gt;Run Once with All Items&lt;/code&gt;), the child receives all items at once and returns all its output items. This can change the downstream item count unexpectedly if you're not aware of which mode is active.&lt;/p&gt;
&lt;h3&gt;
  
  
  6. Credentials are not inherited
&lt;/h3&gt;

&lt;p&gt;The child workflow runs with its own credentials. If the child calls an API, it needs its own credential configured — it won't use the parent's credentials.&lt;/p&gt;


&lt;h2&gt;
  
  
  3 workflow patterns
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Pattern 1: Shared notification dispatcher
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Any workflow] → Execute Workflow ("Send Notification" sub-workflow)
  Input: { channel: "slack", message: "...", level: "error" }

"Send Notification" sub-workflow:
  Execute Workflow Trigger
  → Switch node (route on {{ $json.channel }}: slack | email | telegram)
  → Branch A: Slack node (send to #alerts)
  → Branch B: Gmail node (send to ops@company.com)
  → Branch C: Telegram node (send to ops chat)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Use case: One notification workflow, called from 20 different workflows. Change the notification logic in one place.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pattern 2: Reusable data enrichment
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CRM webhook (new contact) → Execute Workflow ("Enrich Contact")
  Input: { email, company_name }

"Enrich Contact" sub-workflow:
  Execute Workflow Trigger
  → HTTP Request (fetch company data from Clearbit/Apollo)
  → HTTP Request (check LinkedIn via SerpApi)
  → Set node (normalise output schema)
  → [returns enriched contact object to parent]

Parent continues:
  → Airtable (update contact record with enriched data)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Use case: Reuse the same enrichment logic from your CRM webhook, your bulk import workflow, and your nightly refresh — without duplicating nodes.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pattern 3: Batch processor with per-item sub-workflow
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Schedule Trigger (nightly)
→ Postgres (SELECT id, email FROM users WHERE needs_report = true)
→ Split In Batches (batch size: 10)
  → Execute Workflow ("Generate User Report")
    Input: { user_id, email }

"Generate User Report" sub-workflow:
  Execute Workflow Trigger
  → Postgres (SELECT user's activity data)
  → Basic LLM Chain (Anthropic: generate personalised summary)
  → Resend (send email to user)
  → Postgres (UPDATE users SET report_sent_at = NOW())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Use case: Process 500 users nightly, each getting a personalised AI report, without building a 40-node monolith.&lt;/p&gt;


&lt;h2&gt;
  
  
  Free workflow JSON
&lt;/h2&gt;

&lt;p&gt;Shared notification dispatcher pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Notification Dispatcher (Sub-Workflow)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Execute Workflow Trigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.executeWorkflowTrigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"values"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"outputKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"slack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"conditions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"combinator"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"conditions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"leftValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.channel }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"rightValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"slack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"operator"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"equals"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"outputKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"conditions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"combinator"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"conditions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"leftValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.channel }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"rightValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"operator"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"equals"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Route by Channel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.switch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;460&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"assignments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"assignments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"={{ $json.channel }}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mark Sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n8n-nodes-base.set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;680&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Execute Workflow Trigger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Route by Channel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Route by Channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mark Sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mark Sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import via &lt;strong&gt;Settings → Import from URL/Clipboard&lt;/strong&gt;. Add your Slack/Gmail nodes between the Switch output and Mark Sent. Note the workflow ID after saving, then reference it in your parent workflows' Execute Workflow nodes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use with the &lt;a href="https://dev.to/pirateprentice/n8n-split-in-batches-node-process-large-datasets-without-crashing-free-json-4p0c"&gt;n8n Split In Batches node&lt;/a&gt; for scalable batch processing&lt;/li&gt;
&lt;li&gt;Pair with the &lt;a href="https://dev.to/pirateprentice/n8n-ai-agent-node-build-autonomous-ai-workflows-with-tools-memory-and-multi-step-reasoning-free-c90"&gt;n8n AI Agent node&lt;/a&gt; — the n8n Workflow Tool uses Execute Workflow under the hood&lt;/li&gt;
&lt;li&gt;Add &lt;a href="https://dev.to/pirateprentice/n8n-error-trigger-node-build-a-global-error-handler-for-your-workflows-free-json-jm"&gt;n8n Error Trigger&lt;/a&gt; to handle sub-workflow failures gracefully&lt;/li&gt;
&lt;li&gt;Browse the full &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;n8n Workflow Starter Pack&lt;/a&gt; for pre-built modular automation patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are you using Execute Workflow to build modular n8n architectures? Share your sub-workflow patterns in the comments — especially if you've solved the multi-environment ID problem.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
