<?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>How to run n8n workflows on a schedule (cron, daily, weekly — with free JSON)</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Thu, 25 Jun 2026 19:31:34 +0000</pubDate>
      <link>https://dev.to/pirateprentice/how-to-run-n8n-workflows-on-a-schedule-cron-daily-weekly-with-free-json-4cki</link>
      <guid>https://dev.to/pirateprentice/how-to-run-n8n-workflows-on-a-schedule-cron-daily-weekly-with-free-json-4cki</guid>
      <description>&lt;p&gt;Most n8n tutorials show you how to trigger a workflow from a webhook or button click. But some of the most useful automations run on their own — every morning, every Monday, the first of the month. No webhook needed. No human in the loop.&lt;/p&gt;

&lt;p&gt;Here's how to set that up in n8n with the Schedule Trigger node.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Schedule Trigger node
&lt;/h2&gt;

&lt;p&gt;In n8n, the node you want is called &lt;strong&gt;Schedule Trigger&lt;/strong&gt; (you might also see it called "Cron" in older versions). It has zero inputs and fires your workflow automatically at whatever interval you configure.&lt;/p&gt;

&lt;p&gt;To add it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a new workflow in n8n&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;+ Add node&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;Schedule Trigger&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select it as your workflow's starting node&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Setting the interval
&lt;/h2&gt;

&lt;p&gt;The node gives you several modes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every X minutes / hours&lt;/strong&gt;&lt;br&gt;
Good for monitoring tasks: check a Slack channel every 15 minutes, ping an API every hour to look for updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every day at a specific time&lt;/strong&gt;&lt;br&gt;
The most common setup. Example: send a daily summary email at 8 AM every weekday.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every week on a specific day&lt;/strong&gt;&lt;br&gt;
Good for reports: generate a weekly CSV every Monday at 9 AM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every month on a specific day&lt;/strong&gt;&lt;br&gt;
Good for billing checks, monthly digests, first-of-month resets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom cron expression&lt;/strong&gt;&lt;br&gt;
If you need fine-grained control: &lt;code&gt;0 8 * * 1-5&lt;/code&gt; runs at 8 AM Monday through Friday. n8n accepts standard 5-field cron syntax.&lt;/p&gt;


&lt;h2&gt;
  
  
  Real example: Daily digest email
&lt;/h2&gt;

&lt;p&gt;Here's a simple pattern that runs every morning at 8 AM and sends you a summary email:&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 day at 08:00)
  → HTTP Request (fetch your data)
  → Set (format the data)
  → Send Email (Gmail or SMTP)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow fires automatically. You wake up to a digest without touching anything.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real example: Weekly Slack report
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Schedule Trigger (every Monday at 09:00)
  → Google Sheets (pull last week's rows)
  → Code node (calculate totals)
  → Slack (post to #reports)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Free workflow JSON (copy-paste ready)
&lt;/h2&gt;

&lt;p&gt;Here's a minimal Schedule Trigger → Send Email 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;"Daily Schedule → 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;"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;"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;"hours"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"hoursInterval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="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;8&lt;/span&gt;&lt;span class="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="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;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;"fromEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"you@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"toEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"you@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"subject"&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 digest"&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;"Workflow ran at {{ $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="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;"Send 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;"n8n-nodes-base.emailSend"&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;"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;"Send 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;"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;&lt;strong&gt;To import:&lt;/strong&gt; In n8n → workflow list → &lt;strong&gt;Import from clipboard&lt;/strong&gt; → paste.&lt;/p&gt;




&lt;h2&gt;
  
  
  Timezone gotcha
&lt;/h2&gt;

&lt;p&gt;n8n runs in the timezone of your server. After setting up your Schedule Trigger, check the &lt;strong&gt;Next execution&lt;/strong&gt; preview. If it shows the wrong time, fix your server timezone or use a UTC cron expression adjusted manually.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want more pre-built workflows?
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;n8n Workflow Starter Pack&lt;/strong&gt; includes production-ready workflow JSONs — Stripe receipt automation, lead-capture-to-CRM, and form-to-Sheets — all documented and import-ready.&lt;/p&gt;

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




&lt;p&gt;Questions? Drop them in the comments — I reply same day.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to connect n8n to ChatGPT (OpenAI) — build your first AI automation in 10 minutes</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Thu, 25 Jun 2026 16:30:18 +0000</pubDate>
      <link>https://dev.to/pirateprentice/how-to-connect-n8n-to-chatgpt-openai-build-your-first-ai-automation-in-10-minutes-3emh</link>
      <guid>https://dev.to/pirateprentice/how-to-connect-n8n-to-chatgpt-openai-build-your-first-ai-automation-in-10-minutes-3emh</guid>
      <description>&lt;p&gt;If you've been using n8n for automations but haven't connected it to ChatGPT yet, this is the tutorial for you.&lt;/p&gt;

&lt;p&gt;n8n has a native &lt;strong&gt;OpenAI node&lt;/strong&gt; that makes it surprisingly easy to add AI to any workflow — no custom API code required. In this post I'll walk you through building a real working AI automation from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we're building
&lt;/h2&gt;

&lt;p&gt;A workflow that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receives a webhook (from a form, Slack, or anything)&lt;/li&gt;
&lt;li&gt;Passes the input to ChatGPT&lt;/li&gt;
&lt;li&gt;Returns the AI-generated response — to email, Slack, Google Sheets, wherever you need it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the foundation pattern for almost every AI automation in n8n. Once you have this wired up, you can swap in any trigger and any output.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;n8n running (cloud or self-hosted)&lt;/li&gt;
&lt;li&gt;An OpenAI API key (&lt;a href="https://platform.openai.com" rel="noopener noreferrer"&gt;platform.openai.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;10 minutes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1 — Create a new workflow and add a Webhook trigger
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In n8n, click &lt;strong&gt;+ New Workflow&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;Webhook&lt;/strong&gt; node as your trigger&lt;/li&gt;
&lt;li&gt;Set the HTTP method to &lt;strong&gt;POST&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Copy the webhook URL — you'll use it to send test data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The webhook will receive a JSON body with whatever input you want to send to ChatGPT. For this tutorial we'll use:&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;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Summarize the following in one sentence: n8n is an open-source workflow automation tool that lets you connect apps and automate tasks without writing code."&lt;/span&gt;&lt;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;
  
  
  Step 2 — Add the OpenAI node
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;After the Webhook node, click &lt;strong&gt;+&lt;/strong&gt; to add a new node&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;OpenAI&lt;/strong&gt; and select it&lt;/li&gt;
&lt;li&gt;Choose the &lt;strong&gt;Message a Model&lt;/strong&gt; operation&lt;/li&gt;
&lt;li&gt;Create a new credential: paste your OpenAI API key&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Model settings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model: &lt;code&gt;gpt-4o-mini&lt;/code&gt; (fast and cheap — ideal for automations)&lt;/li&gt;
&lt;li&gt;User Message: &lt;code&gt;{{ $json.message }}&lt;/code&gt; — this pulls the message from the webhook body&lt;/li&gt;
&lt;li&gt;System Prompt (optional): &lt;code&gt;You are a helpful assistant. Be concise.&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. n8n handles the API call, authentication, and response parsing automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Use the response
&lt;/h2&gt;

&lt;p&gt;The OpenAI node outputs the response text at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nv"&gt;json.message.content&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here you can pipe it anywhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Send Email&lt;/strong&gt; node → email the AI response&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack&lt;/strong&gt; node → post it to a channel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets&lt;/strong&gt; node → log it to a spreadsheet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respond to Webhook&lt;/strong&gt; node → return it to the caller as JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For testing, add a &lt;strong&gt;Respond to Webhook&lt;/strong&gt; node at the end with body &lt;code&gt;{{ $json.message.content }}&lt;/code&gt; — that way you can hit the webhook URL and get the AI response back in real time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Test it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Activate the workflow&lt;/li&gt;
&lt;li&gt;Send a POST request to your webhook URL with the JSON body above (use curl, Postman, or any HTTP tool)&lt;/li&gt;
&lt;li&gt;You should get back a clean one-sentence summary from ChatGPT in under 2 seconds&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Real-world uses for this pattern
&lt;/h2&gt;

&lt;p&gt;Once you have the base pattern working, here's what people actually build with it:&lt;/p&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;Trigger&lt;/th&gt;
&lt;th&gt;What ChatGPT does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Auto-classify support tickets&lt;/td&gt;
&lt;td&gt;Email receive&lt;/td&gt;
&lt;td&gt;Tags tickets by urgency/topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI email drafts&lt;/td&gt;
&lt;td&gt;New CRM lead&lt;/td&gt;
&lt;td&gt;Drafts a personalized outreach email&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Form response summarizer&lt;/td&gt;
&lt;td&gt;Tally/Typeform webhook&lt;/td&gt;
&lt;td&gt;Summarizes long-form answers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slack Q&amp;amp;A bot&lt;/td&gt;
&lt;td&gt;Slack mention&lt;/td&gt;
&lt;td&gt;Answers questions from a knowledge base&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invoice data extraction&lt;/td&gt;
&lt;td&gt;Email attachment&lt;/td&gt;
&lt;td&gt;Extracts line items into Google Sheets&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All of these use the same skeleton: trigger → OpenAI node → output node.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common issues
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"I get an authentication error"&lt;/strong&gt; — double-check your OpenAI API key in n8n credentials. Make sure you're using a key from the project you have billing set up for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"The response is empty"&lt;/strong&gt; — check your expression path. In newer n8n versions the response is at &lt;code&gt;{{ $json.message.content }}&lt;/code&gt;. In older versions it may be at &lt;code&gt;{{ $json.choices[0].message.content }}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"It's slow"&lt;/strong&gt; — switch from &lt;code&gt;gpt-4o&lt;/code&gt; to &lt;code&gt;gpt-4o-mini&lt;/code&gt;. For most automation tasks the quality difference is negligible and latency drops from ~3s to ~0.5s.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want pre-built workflows that already use this pattern?
&lt;/h2&gt;

&lt;p&gt;I put together an &lt;strong&gt;n8n Workflow Starter Pack&lt;/strong&gt; — 5 production-ready workflow JSONs you can import and run immediately, including a lead-capture AI workflow that uses OpenAI to score and categorize inbound leads automatically.&lt;/p&gt;

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

&lt;p&gt;Drop a comment below if you get stuck on any of the steps and I'll help you debug it.&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
    <item>
      <title>How to automatically add new leads to your CRM with n8n (free workflow JSON)</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Thu, 25 Jun 2026 13:29:24 +0000</pubDate>
      <link>https://dev.to/pirateprentice/how-to-automatically-add-new-leads-to-your-crm-with-n8n-free-workflow-json-42f3</link>
      <guid>https://dev.to/pirateprentice/how-to-automatically-add-new-leads-to-your-crm-with-n8n-free-workflow-json-42f3</guid>
      <description>&lt;p&gt;Every lead capture form should do three things automatically: save the lead somewhere, notify the right person, and send a welcome email. Most people build this once, badly, and then rebuild it every time they start a new project.&lt;/p&gt;

&lt;p&gt;I rebuilt it four times before I finally wrote it down properly as an n8n workflow. This is that workflow — free JSON at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this workflow does
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Receives a webhook POST from your form (Tally, Typeform, Webflow, or a plain HTML form)&lt;/li&gt;
&lt;li&gt;Saves the lead to Airtable (or Google Sheets — I'll show both)&lt;/li&gt;
&lt;li&gt;Posts a Slack message to your #leads channel&lt;/li&gt;
&lt;li&gt;Sends a personalized welcome email via Gmail or SMTP&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No paid n8n nodes. No external services beyond what you're probably already using.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;n8n running (self-hosted free, or n8n Cloud — the workflow works on both)&lt;/li&gt;
&lt;li&gt;An Airtable account (free tier works)&lt;/li&gt;
&lt;li&gt;Slack workspace with a #leads channel&lt;/li&gt;
&lt;li&gt;A form that can send a POST webhook (Tally is free and does this natively)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Set up your Airtable base
&lt;/h2&gt;

&lt;p&gt;Create a base called &lt;strong&gt;Leads&lt;/strong&gt; with these fields:&lt;/p&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;Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;Single line text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source&lt;/td&gt;
&lt;td&gt;Single line text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Created&lt;/td&gt;
&lt;td&gt;Date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;td&gt;Single select (New / Contacted / Qualified)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Copy your Airtable base ID from the URL: &lt;code&gt;https://airtable.com/appXXXXXXXXXXXXXX/...&lt;/code&gt; — that &lt;code&gt;appXXX...&lt;/code&gt; part is your base ID.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Build the n8n workflow
&lt;/h2&gt;

&lt;p&gt;Open n8n and create a new workflow. Add these nodes in order:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node 1 — Webhook (trigger)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication: None (or Header Auth if you want security)&lt;/li&gt;
&lt;li&gt;HTTP Method: POST&lt;/li&gt;
&lt;li&gt;Path: &lt;code&gt;/new-lead&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Response Mode: Immediately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives you a URL like &lt;code&gt;https://your-n8n.com/webhook/new-lead&lt;/code&gt; — that's what your form will POST to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node 2 — Set (normalize the data)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Map incoming form fields to consistent variable names. Form providers use different field names — Tally uses &lt;code&gt;fields[0].value&lt;/code&gt;, Typeform uses &lt;code&gt;answers[0].text&lt;/code&gt;. The Set node creates a clean intermediate shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;name  → &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$json.body.name&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;$json.body["Name"]&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Unknown"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
email → &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$json.body.email&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;$json.body["Email"]&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
source → &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$json.body.source&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Node 3 — Airtable (save the lead)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operation: Create Record&lt;/li&gt;
&lt;li&gt;Base ID: your base ID from Step 1&lt;/li&gt;
&lt;li&gt;Table: Leads&lt;/li&gt;
&lt;li&gt;Fields: map name, email, source, Created (use &lt;code&gt;{{ new Date().toISOString() }}&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Node 4 — Slack (notify your team)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Channel: #leads&lt;/li&gt;
&lt;li&gt;Message: &lt;code&gt;🎯 New lead: {{ $('Set').item.json.name }} ({{ $('Set').item.json.email }}) via {{ $('Set').item.json.source }}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Node 5 — Gmail / Send Email (welcome email)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To: &lt;code&gt;{{ $('Set').item.json.email }}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Subject: &lt;code&gt;Thanks for reaching out, {{ $('Set').item.json.name }}!&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Body: Write a short, warm reply. Don't make it look automated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Node 6 — Respond to Webhook&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Return: &lt;code&gt;{ "status": "ok" }&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Connect them 1 → 2 → 3, and from Node 2 also connect to 4 and 5 in parallel (right-click Node 2 → Add Output). Node 6 connects after Node 1 (immediate response) so the form doesn't time out waiting for your CRM writes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Connect your form
&lt;/h2&gt;

&lt;p&gt;In Tally (free):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a new block → Redirect / Webhook&lt;/li&gt;
&lt;li&gt;Paste your n8n webhook URL&lt;/li&gt;
&lt;li&gt;Test submit — you should see the execution appear in n8n immediately&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a plain HTML form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"lead-form"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Your name"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Get started&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lead-form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://your-n8n.com/webhook/new-lead&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&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="nf"&gt;stringify&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="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// show success message&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Google Sheets instead of Airtable (optional)
&lt;/h2&gt;

&lt;p&gt;Replace Node 3 with a Google Sheets node:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operation: Append Row&lt;/li&gt;
&lt;li&gt;Spreadsheet ID: your sheet ID from the URL&lt;/li&gt;
&lt;li&gt;Sheet: Sheet1&lt;/li&gt;
&lt;li&gt;Columns: Name, Email, Source, Created At&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same downstream nodes; everything else stays identical.&lt;/p&gt;

&lt;h2&gt;
  
  
  The free workflow JSON
&lt;/h2&gt;

&lt;p&gt;Drop a comment below and I'll share the full workflow JSON — import it directly into n8n with File → Import from JSON. It includes all 6 nodes pre-configured with placeholder credentials (swap in yours and it runs).&lt;/p&gt;

&lt;p&gt;I've also packaged this workflow alongside two others (Stripe payment fulfillment + form-to-Sheets) into a starter pack if you want all three ready to go:&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&lt;/a&gt;&lt;/strong&gt; — includes documented JSON + a walkthrough for each workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do if the webhook stops receiving data
&lt;/h2&gt;

&lt;p&gt;Two common failures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Form sends application/x-www-form-urlencoded instead of JSON&lt;/strong&gt; — add a Code node after the Webhook and parse &lt;code&gt;$input.item.binary&lt;/code&gt; manually, or switch your form to JSON mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;n8n is behind a firewall / localhost&lt;/strong&gt; — use &lt;a href="https://ngrok.com" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt; to expose it temporarily while testing: &lt;code&gt;ngrok http 5678&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;That's the full build. If you're already running this and hit a specific snag, post it in the comments — I check back.&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>How to save form submissions to Google Sheets with n8n (Tally, Typeform, or plain HTML)</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Thu, 25 Jun 2026 10:34:44 +0000</pubDate>
      <link>https://dev.to/pirateprentice/how-to-save-form-submissions-to-google-sheets-with-n8n-tally-typeform-or-plain-html-igi</link>
      <guid>https://dev.to/pirateprentice/how-to-save-form-submissions-to-google-sheets-with-n8n-tally-typeform-or-plain-html-igi</guid>
      <description>

&lt;p&gt;Every form tool promises "connect to Google Sheets" â€” and then charges you $30/month for it. n8n does the same thing in about 15 minutes, free, and you actually own the automation.&lt;/p&gt;

&lt;p&gt;Here's how to build a workflow that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receives any form submission via webhook&lt;/li&gt;
&lt;li&gt;Appends a timestamped row to a Google Sheet&lt;/li&gt;
&lt;li&gt;(Optional) fires a Slack alert so your team sees it instantly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Works with Tally, Typeform, Jotform, and plain HTML tags.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why webhook-based form handling beats native integrations
&lt;/h2&gt;

&lt;p&gt;Native "Sheets integrations" inside form tools are convenient until they're not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They break when the form tool updates their API&lt;/li&gt;
&lt;li&gt;They don't let you transform or filter the data before it hits the sheet&lt;/li&gt;
&lt;li&gt;They can't send a Slack alert at the same time&lt;/li&gt;
&lt;li&gt;They cost money&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With n8n you get one webhook URL that handles everything, and you can add more steps (CRM write, email confirmation, Slack DM) without rebuilding from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow
&lt;/h2&gt;

&lt;p&gt;Three steps in a straight line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Webhook -&amp;gt; Normalise payload -&amp;gt; [Google Sheets append]
                           \ [Build Slack message -&amp;gt; Slack post]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Normalise node handles the fact that Tally, Typeform, and raw POST bodies all structure their payloads differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-step setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Create the workflow in n8n
&lt;/h3&gt;

&lt;p&gt;Add a Webhook trigger node:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Method: POST&lt;/li&gt;
&lt;li&gt;Path: form-submit&lt;/li&gt;
&lt;li&gt;Response mode: Immediately (no waiting)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives you a URL like &lt;a href="https://your-n8n-instance.com/webhook/form-submit" rel="noopener noreferrer"&gt;https://your-n8n-instance.com/webhook/form-submit&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Add a Function node - normalise the payload
&lt;/h3&gt;

&lt;p&gt;Different form tools send different shapes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tally sends { body: [{ label: "Name", value: "Alice" }, ...] }&lt;/li&gt;
&lt;li&gt;Typeform sends { formResponse: { answers: [...] } }&lt;/li&gt;
&lt;li&gt;Plain HTML POS sends a flat object directly
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raw&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formResponse&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;answers&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;raw&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;fields&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;raw&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="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&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;flat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="k"&gt;for &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;f&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;fields&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&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;trim&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;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;??&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;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;flat&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="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flat&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;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&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="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;_submittedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Google Sheets - Append Row
&lt;/h3&gt;

&lt;p&gt;Add a Google Sheets node:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operation: Append&lt;/li&gt;
&lt;li&gt;Document: your sheet ID (from the URL: docs.google.com/spreadsheets/d/THIS_PART/)&lt;/li&gt;
&lt;li&gt;Sheet tab: Responses&lt;/li&gt;
&lt;li&gt;Column mapping: Auto-map input data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You'll need to connect Google Sheets OAuth2 credentials once - n8n walks you through it.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. (Optional) Slack alert
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&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="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;blocks&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;plain_text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New form submission&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;section&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mrkdwn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&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="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;context&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mrkdwn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Submitted at &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&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;_submittedAt&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="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Configure your form tool
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Tally: Settings -&amp;gt; Integrations -&amp;gt; Webhooks&lt;/li&gt;
&lt;li&gt;Typeform: Connect -&amp;gt; Webhooks&lt;/li&gt;
&lt;li&gt;Plain HTML: set your form action attribute to the webhook URL&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Test it
&lt;/h3&gt;

&lt;p&gt;Submit a test entry through your form. You should see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new row in your Google Sheet with all fields + a timestamp column&lt;/li&gt;
&lt;li&gt;A Slack message in your chosen channel&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What this handles that native integrations don't
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Multi-form fan-out: route different forms to different sheets with an IF node&lt;/li&gt;
&lt;li&gt;Data cleaning: strip whitespace, capitalise names, mask PII in the Function node&lt;/li&gt;
&lt;li&gt;Error alerting: wire error output to Slack&lt;/li&gt;
&lt;li&gt;Chaining: add more steps (Airtable, confirmation email, Trello) without rebuilding&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get the ready-to-import JSON
&lt;/h2&gt;

&lt;p&gt;I'll drop the complete workflow JSON in the comments - import it into n8n and you're five minutes from live.&lt;/p&gt;

&lt;p&gt;If you want all three production-ready workflows (this one + a Stripe payment receipt sender + a lead capture/welcome email flow) as a single pack, I keep them at pirateprentice.gumroad.com/l/sxcoe for $29.&lt;/p&gt;

&lt;p&gt;Drop a comment if you hit any issues with the Google Sheets OAuth or the payload normaliser - I'm watching.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>google</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to auto-send Stripe payment receipts using n8n (no paid nodes, free workflow)</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Thu, 25 Jun 2026 07:32:46 +0000</pubDate>
      <link>https://dev.to/pirateprentice/how-to-auto-send-stripe-payment-receipts-using-n8n-no-paid-nodes-free-workflow-pag</link>
      <guid>https://dev.to/pirateprentice/how-to-auto-send-stripe-payment-receipts-using-n8n-no-paid-nodes-free-workflow-pag</guid>
      <description>&lt;p&gt;Stripe's built-in receipts are fine. But the moment you want to send a branded confirmation email, add an order summary, trigger a download link, or write to your own database — you're out of luck without code.&lt;/p&gt;

&lt;p&gt;n8n fixes this cleanly. Here's the exact workflow I use to intercept every successful Stripe payment and fire a custom receipt, all with built-in nodes and zero paid integrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the workflow does
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stripe Trigger — payment_intent.succeeded&lt;/strong&gt; — listens for confirmed payments in real time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Request — fetch charge details&lt;/strong&gt; — pulls the full charge object (customer email, amount, description, metadata) via the Stripe API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set — format receipt data&lt;/strong&gt; — builds the receipt fields: formatted amount, item name, order ID, timestamp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gmail / Resend — send receipt email&lt;/strong&gt; — fires a personalised receipt to the customer email from the charge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets — log order&lt;/strong&gt; — appends a row to your orders sheet (timestamp, customer, amount, order ID, status)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error — notify on failure&lt;/strong&gt; — if any node fails, logs to an error sheet and optionally sends you a Slack DM&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The whole thing runs in under 2 seconds from payment confirmation to receipt in inbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just use Stripe's built-in receipt?
&lt;/h2&gt;

&lt;p&gt;Three reasons I switched:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Branding&lt;/strong&gt; — Stripe's receipt looks like Stripe's, not yours. Custom email = your logo, your tone, your support link.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Download delivery&lt;/strong&gt; — if you're selling digital products, you need to attach or link the file in the confirmation. Stripe can't do this natively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your own records&lt;/strong&gt; — the Sheets log gives you a single source of truth you own, outside Stripe's dashboard. Useful for refund tracking, VAT records, or feeding a CRM.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Import the JSON&lt;/strong&gt; — Workflows → Import from clipboard in n8n&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe credential&lt;/strong&gt; — create a Restricted Key in Stripe dashboard with &lt;code&gt;charges:read&lt;/code&gt; and &lt;code&gt;payment_intents:read&lt;/code&gt; permissions. Paste into n8n's Stripe credential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook endpoint&lt;/strong&gt; — copy the production webhook URL from the Stripe Trigger node. Add it in Stripe Dashboard → Developers → Webhooks → Add endpoint. Select &lt;code&gt;payment_intent.succeeded&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gmail or Resend credential&lt;/strong&gt; — connect whichever you use. The email node is pre-wired to &lt;code&gt;{{customerEmail}}&lt;/code&gt; from the Set node.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets&lt;/strong&gt; — point the Sheets node at a spreadsheet with columns: &lt;code&gt;timestamp&lt;/code&gt;, &lt;code&gt;customer_email&lt;/code&gt;, &lt;code&gt;amount&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;order_id&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Activate&lt;/strong&gt; — flip the workflow to Active. Every successful payment now triggers the full chain.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Total setup time: under 15 minutes the first time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing most tutorials miss: idempotency
&lt;/h2&gt;

&lt;p&gt;Stripe can fire the same webhook event more than once (retries on timeout, network blips). Without a deduplication check, one payment can generate two receipts and two log rows.&lt;/p&gt;

&lt;p&gt;The workflow handles this: before appending to Sheets, it looks up the &lt;code&gt;payment_intent_id&lt;/code&gt; in the log. If it already exists, it skips. One receipt per payment, guaranteed.&lt;/p&gt;

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

&lt;p&gt;Drop a comment below and I'll paste the full workflow JSON. Free to use, MIT-style — import it and adapt it to your setup.&lt;/p&gt;




&lt;p&gt;I packaged this workflow alongside two others (Lead Capture → CRM, Form → Sheets + Slack) into a &lt;strong&gt;&lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;Workflow Starter Pack&lt;/a&gt;&lt;/strong&gt; with full setup docs and test checklists for each. $29 one-time if you want the full set with docs.&lt;/p&gt;

&lt;p&gt;What do you use for post-payment automation? Curious what setups others are running.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>stripe</category>
      <category>automation</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I structured 50 legal AI prompts so they are actually reusable</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Wed, 24 Jun 2026 23:32:55 +0000</pubDate>
      <link>https://dev.to/pirateprentice/how-i-structured-50-legal-ai-prompts-so-they-are-actually-reusable-2e82</link>
      <guid>https://dev.to/pirateprentice/how-i-structured-50-legal-ai-prompts-so-they-are-actually-reusable-2e82</guid>
      <description>&lt;p&gt;I do a lot of contract drafting, NDA review, and client communication work. Started using AI for it about a year ago. The dirty secret: the quality gap between a bad prompt and a good one in legal work is enormous.&lt;/p&gt;

&lt;p&gt;A bad prompt produces generic legalese that needs total rewriting. A good prompt produces a usable first draft. The difference is almost never the model — it is the structure of the ask.&lt;/p&gt;

&lt;p&gt;Here is what I figured out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core pattern: role + constraints + output format
&lt;/h2&gt;

&lt;p&gt;Every legal prompt that works for me has three parts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ROLE]: You are a [practice area] attorney reviewing [document type].
[CONSTRAINTS]: Jurisdiction: [STATE/COUNTRY]. Client type: [INDIVIDUAL/BUSINESS].
Assume: [KEY ASSUMPTION]. Do not: [THING TO AVOID].
[FORMAT]: Output a [FORMAT] covering [SPECIFIC SCOPE]. 
Flag any [RISK TYPE] with a one-sentence plain-English explanation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "do not" line does most of the work. Without it, models default to maximally hedged, disclaimer-heavy output. Telling the model what to skip sharpens the response significantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 50 prompts by category
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Client intake (8 prompts)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conflict check memo (takes client name + matter description, flags potential conflicts)&lt;/li&gt;
&lt;li&gt;Non-engagement letter (3 variants: conflict, scope mismatch, capacity)&lt;/li&gt;
&lt;li&gt;Retainer clause explainer (translates fee language into plain English for the client)&lt;/li&gt;
&lt;li&gt;Initial consultation summary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Legal research (9 prompts)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Case summary (input: raw case text, output: issue/holding/reasoning in 200 words)&lt;/li&gt;
&lt;li&gt;Jurisdiction comparison (same legal question across two states)&lt;/li&gt;
&lt;li&gt;Deposition outline generator&lt;/li&gt;
&lt;li&gt;Statute plain-English summary&lt;/li&gt;
&lt;li&gt;Counter-argument spotter (reads your draft argument, surfaces the opposing view)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Drafting (10 prompts)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NDA redline reviewer (flags one-sided clauses with explanations)&lt;/li&gt;
&lt;li&gt;Contract summary (for clients who will not read the full agreement)&lt;/li&gt;
&lt;li&gt;Settlement agreement first draft&lt;/li&gt;
&lt;li&gt;Interrogatory response set&lt;/li&gt;
&lt;li&gt;Cease and desist (3 tones: firm, aggressive, de-escalating)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Client communication (8 prompts)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Case status update email&lt;/li&gt;
&lt;li&gt;Bad news delivery (structure: fact → context → options → next step)&lt;/li&gt;
&lt;li&gt;Fee dispute response&lt;/li&gt;
&lt;li&gt;Client boundary-setting email&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Practice management (15 prompts)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Business Profile bio&lt;/li&gt;
&lt;li&gt;LinkedIn post series (5 prompts, one per week of content)&lt;/li&gt;
&lt;li&gt;SOP draft (input: process description, output: numbered SOP with decision points)&lt;/li&gt;
&lt;li&gt;CLE tracker summary&lt;/li&gt;
&lt;li&gt;Year-end business review&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The most useful single prompt
&lt;/h2&gt;

&lt;p&gt;The contract summary for clients:&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 [PRACTICE AREA] attorney. A client has asked you to explain this contract 
in plain English. They are [SOPHISTICATED/NON-SOPHISTICATED]. 
Do not use legal jargon. Do not add hedges or disclaimers — 
I will add those separately. 

Summarise the following sections in 2-3 sentences each:
- Parties and purpose
- Key obligations (each party)
- Payment terms
- Termination triggers
- Anything unusual or one-sided

Flag any clause that a [CLIENT TYPE] should negotiate before signing, 
with a one-sentence plain-English reason.

[PASTE CONTRACT HERE]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clients read this. They do not read a 40-page agreement.&lt;/p&gt;

&lt;h2&gt;
  
  
  One important note
&lt;/h2&gt;

&lt;p&gt;None of this replaces legal judgment. Every output needs attorney review before it goes anywhere. The prompts are for generating usable first drafts, not final work product.&lt;/p&gt;




&lt;p&gt;Full pack of 50 prompts (PDF + Markdown, copy-paste ready with bracket placeholders): &lt;a href="https://pirateprentice.gumroad.com/l/sklcrpo" rel="noopener noreferrer"&gt;https://pirateprentice.gumroad.com/l/sklcrpo&lt;/a&gt; ($29 one-time).&lt;/p&gt;

&lt;p&gt;If you build legal tech or use AI for your own legal paperwork — what prompts have you found that actually work? Always looking for patterns I have not tried.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>discuss</category>
      <category>career</category>
    </item>
    <item>
      <title>40 prompt templates for e-commerce copy (the ones that actually ship)</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Wed, 24 Jun 2026 23:32:23 +0000</pubDate>
      <link>https://dev.to/pirateprentice/40-prompt-templates-for-e-commerce-copy-the-ones-that-actually-ship-3e8c</link>
      <guid>https://dev.to/pirateprentice/40-prompt-templates-for-e-commerce-copy-the-ones-that-actually-ship-3e8c</guid>
      <description>&lt;p&gt;Most AI copywriting guides give you principles. This is a cheat sheet.&lt;/p&gt;

&lt;p&gt;I spent a few weeks building and testing prompts specifically for e-commerce copy workflows — product pages, email flows, ad creative, site copy. The kind of work that fills a freelance pipeline or ships a DTC brand's calendar.&lt;/p&gt;

&lt;p&gt;Sharing the architecture here, plus a few samples.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with generic prompts
&lt;/h2&gt;

&lt;p&gt;Generic: &lt;em&gt;"Write a product description for a blue coffee mug."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What ships:&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 DTC copywriter. Product: [NAME]. Key benefit: [BENEFIT]. 
Target customer: [PERSONA]. Tone: [TONE].
Write a 3-sentence product description opening with the transformation, 
not the feature. No puns. No exclamation marks.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is constraint. Vague inputs produce average outputs. Bracketed placeholders force you to think before you prompt — and that thinking is where the quality comes from.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the 40 prompts are organised
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Product copy (8 prompts)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feature-to-benefit converter&lt;/li&gt;
&lt;li&gt;Long-form PDP (problem → agitation → resolution structure)&lt;/li&gt;
&lt;li&gt;Collection page descriptions&lt;/li&gt;
&lt;li&gt;Bundle/upsell copy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Email flows (10 prompts)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Welcome series (3-email sequence, single prompt chain)&lt;/li&gt;
&lt;li&gt;Abandoned cart 1 (urgency without pressure)&lt;/li&gt;
&lt;li&gt;Abandoned cart 2 (social proof + incentive)&lt;/li&gt;
&lt;li&gt;Win-back (6-month lapsed customer)&lt;/li&gt;
&lt;li&gt;Post-purchase upsell&lt;/li&gt;
&lt;li&gt;VIP early access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ad copy (8 prompts)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Facebook awareness (cold audience)&lt;/li&gt;
&lt;li&gt;Facebook retargeting (cart abandoners)&lt;/li&gt;
&lt;li&gt;Google search headline set (10 variants)&lt;/li&gt;
&lt;li&gt;UGC-style script (15-second video)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Site copy (7 prompts)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Homepage hero (above the fold)&lt;/li&gt;
&lt;li&gt;About page (founder story framework)&lt;/li&gt;
&lt;li&gt;FAQ generator from product specs&lt;/li&gt;
&lt;li&gt;Trust badge copy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Strategy + editing (7 prompts)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Brand voice audit&lt;/li&gt;
&lt;li&gt;A/B test variant generator&lt;/li&gt;
&lt;li&gt;Brief-to-copy pipeline (PDP + email + 2 FB ads from one input)&lt;/li&gt;
&lt;li&gt;Copy critique prompt (gives structured feedback on your draft)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sample: the brief-to-copy pipeline
&lt;/h2&gt;

&lt;p&gt;This one prompt generates coordinated copy across four formats from a single product brief:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Product brief:
- Product name: [NAME]
- Category: [CATEGORY]
- Key benefit: [BENEFIT]
- Target customer: [PERSONA, age, job, pain point]
- Price point: [PRICE]
- Differentiator vs. competitors: [DIFFERENTIATOR]
- Tone: [TONE]

Using this brief, produce:
1. A 120-word long-form product page description (hook → benefit → social proof placeholder → CTA)
2. A 3-email welcome sequence (subject + 150-word body each)
3. Two Facebook ad variants (hook + 3 body sentences + CTA each): 
   one benefit-led, one pain-led
4. One 15-second UGC-style video script
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output isn't always perfect. But it's a solid first draft across all four formats in one pass — which is the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on review
&lt;/h2&gt;

&lt;p&gt;Every prompt output needs a human pass. The bracket system forces you to think before generating, but the editing step is where your judgment adds value. These are starting points, not finished copy.&lt;/p&gt;




&lt;p&gt;The full 40-prompt pack is available here if you want the complete set: &lt;a href="https://pirateprentice.gumroad.com/l/uhckix" rel="noopener noreferrer"&gt;https://pirateprentice.gumroad.com/l/uhckix&lt;/a&gt; ($24 one-time, PDF + Markdown).&lt;/p&gt;

&lt;p&gt;What copy format do you find hardest to prompt effectively? Curious whether email flows or ad copy is more of a pain point.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>javascript</category>
    </item>
    <item>
      <title>I keep rebuilding the same n8n workflows so I documented them (free JSON inside)</title>
      <dc:creator>Pirate Prentice</dc:creator>
      <pubDate>Wed, 24 Jun 2026 23:27:51 +0000</pubDate>
      <link>https://dev.to/pirateprentice/i-keep-rebuilding-the-same-n8n-workflows-so-i-documented-them-free-json-inside-3jje</link>
      <guid>https://dev.to/pirateprentice/i-keep-rebuilding-the-same-n8n-workflows-so-i-documented-them-free-json-inside-3jje</guid>
      <description>&lt;p&gt;Every time I start a new project that needs any kind of lead capture, I spend an hour rebuilding the same n8n workflow. Webhook receiver, email validation, Google Sheets append, welcome email. Every. Single. Time.&lt;/p&gt;

&lt;p&gt;So I finally sat down and documented three of them properly — error handling, duplicate detection, null-safe field parsing, the works. Sharing the first one free here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow 1: Lead Capture → Google Sheets CRM → Welcome Email
&lt;/h2&gt;

&lt;p&gt;This one handles the full lead intake loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Webhook — New Lead&lt;/strong&gt; — receives a POST from any source (Typeform, Tally, plain HTML form, whatever)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate &amp;amp; Normalise&lt;/strong&gt; — trims whitespace, lowercases the email, checks it matches a basic regex before anything else runs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets — Append Lead&lt;/strong&gt; — checks for duplicates first (lookup by email), only appends if new&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gmail — Welcome Email&lt;/strong&gt; — sends a personalised welcome using the name field from the webhook payload&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error — Notify&lt;/strong&gt; — if anything in the chain fails, logs to a separate error sheet and optionally pings a Slack channel&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All built-in nodes. Zero paid integrations required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I bother with the validation step
&lt;/h2&gt;

&lt;p&gt;Most tutorials skip it. The problem: if you let garbage through to Sheets, you end up with rows like &lt;code&gt;undefined@undefined.com&lt;/code&gt; or &lt;code&gt;&lt;/code&gt; as a name, and your welcome email looks broken. The validate step costs one extra node and saves hours of cleanup.&lt;/p&gt;

&lt;p&gt;The duplicate check is the other thing people skip. Without it, a lead who submits twice gets two welcome emails. Small thing, looks unprofessional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup (under 10 minutes)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Import the JSON into n8n (Workflows → Import from clipboard)&lt;/li&gt;
&lt;li&gt;Connect your Google Sheets credential — point it at a sheet with columns: &lt;code&gt;timestamp&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Connect your Gmail credential (or swap to SMTP/Resend if you prefer)&lt;/li&gt;
&lt;li&gt;Activate the webhook and grab the production URL&lt;/li&gt;
&lt;li&gt;Point your form POST action at it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is it. The workflow handles the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The JSON
&lt;/h2&gt;

&lt;p&gt;Drop a comment below and I will share the JSON file directly. Happy to paste it in a comment or send via DM.&lt;/p&gt;




&lt;p&gt;I also put together a &lt;strong&gt;Starter Pack&lt;/strong&gt; with all three workflows (Lead CRM, Stripe Payment Fulfillment, and Form to Sheets + Slack) plus full setup docs and a testing checklist for each one. If you are tired of rebuilding this stuff from scratch: &lt;a href="https://pirateprentice.gumroad.com/l/sxcoe" rel="noopener noreferrer"&gt;https://pirateprentice.gumroad.com/l/sxcoe&lt;/a&gt; ($29 one-time).&lt;/p&gt;

&lt;p&gt;What workflows do you find yourself rebuilding most often? Curious what to build next.&lt;/p&gt;

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