<?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: Mark Piller</title>
    <description>The latest articles on DEV Community by Mark Piller (@flowrunner-mp).</description>
    <link>https://dev.to/flowrunner-mp</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%2F4182629%2F538bd0e9-7eac-45cc-9cb5-d10fc3376a50.png</url>
      <title>DEV Community: Mark Piller</title>
      <link>https://dev.to/flowrunner-mp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/flowrunner-mp"/>
    <language>en</language>
    <item>
      <title>How to Convert Pipedream Workflows to FlowRunner, With Real Examples</title>
      <dc:creator>Mark Piller</dc:creator>
      <pubDate>Wed, 14 Oct 2026 17:29:11 +0000</pubDate>
      <link>https://dev.to/flowrunner-mp/pipedream-to-flowrunner-real-workflow-conversions-26dm</link>
      <guid>https://dev.to/flowrunner-mp/pipedream-to-flowrunner-real-workflow-conversions-26dm</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://flowrunner.ai/blog/pipedream-to-flowrunner-conversions" rel="noopener noreferrer"&gt;flowrunner.ai&lt;/a&gt;. I run FlowRunner, so this is our product. The worked examples are Raymond Camden's workflows, used with his permission.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Pipedream Workflows shuts down on March 31, 2027, and everything in it, including the credentials for your connected accounts, is deleted by April 30. This guide is how to move your workflows to FlowRunner without rebuilding each one by hand. &lt;a href="https://docs.flowrunner.ai/build/prompt-to-flow" rel="noopener noreferrer"&gt;FlowRunner Prompt to Flow&lt;/a&gt; reads a workflow's files, code steps included, and builds the same workflow as a FlowRunner flow. What's left for you is reconnecting accounts and testing.&lt;/p&gt;

&lt;p&gt;It takes five steps, and after them are three real Pipedream workflows converted this way, with the files that went in and what came out. For the shutdown details and how each Pipedream piece maps to FlowRunner, see &lt;a href="https://flowrunner.ai/blog/pipedream-workflows-shutting-down-move-your-workflows-free" rel="noopener noreferrer"&gt;our Pipedream migration guide&lt;/a&gt;. If you'd rather not do it yourself, &lt;a href="https://flowrunner.ai/contact" rel="noopener noreferrer"&gt;send us your files&lt;/a&gt; and we'll convert them for free.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Get your workflows out of Pipedream
&lt;/h2&gt;

&lt;p&gt;There are two ways to get a workflow's files.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Export Workflows.&lt;/strong&gt; Open the project in Pipedream, select &lt;strong&gt;Settings&lt;/strong&gt;, and click &lt;strong&gt;Export Workflows&lt;/strong&gt; at the bottom. When it finishes, open &lt;strong&gt;File Store&lt;/strong&gt; and download &lt;code&gt;project_export-&amp;lt;project_id&amp;gt;.zip&lt;/code&gt;. It holds a &lt;code&gt;manifest.json&lt;/code&gt; and one JSON file per workflow with its triggers, steps, code, and settings. The export is written to the project's File Store, so Pipedream notes that your plan must include File Stores. One export holds up to 500 workflows or 256 MB, and workflows you never deployed are skipped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Sync.&lt;/strong&gt; If the project was ever synced to GitHub, your repo already has a &lt;code&gt;workflow.yaml&lt;/code&gt; for each workflow and one file per code step. The demos below started from these.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Either way, some things never come out, and you'll fill them in later:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;secrets and connected accounts (exported as &lt;code&gt;&amp;lt;redacted&amp;gt;&lt;/code&gt; or just an account ID)&lt;/li&gt;
&lt;li&gt;environment variables&lt;/li&gt;
&lt;li&gt;data store records and File Store files&lt;/li&gt;
&lt;li&gt;event history&lt;/li&gt;
&lt;li&gt;the code of private components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two things to do now, while Pipedream still runs. Copy your data store records somewhere safe, because they're deleted on April 30. And save a few real events from each workflow's event history, so you have something to test the new flow with.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Connect FlowRunner Prompt to Flow to your AI tool
&lt;/h2&gt;

&lt;p&gt;FlowRunner Prompt to Flow is an MCP server you add to the AI tool you already use. The server URL is &lt;code&gt;https://app.flowrunner.ai/mcp&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude (web and desktop):&lt;/strong&gt; open &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Connectors&lt;/strong&gt;, click &lt;strong&gt;+ Add&lt;/strong&gt;, then &lt;strong&gt;Add custom connector&lt;/strong&gt;. Enter &lt;code&gt;FlowRunner&lt;/code&gt; and the server URL, click &lt;strong&gt;Add&lt;/strong&gt;, then &lt;strong&gt;Connect&lt;/strong&gt;, and approve the FlowRunner sign-in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code:&lt;/strong&gt; run these two commands, and approve the sign-in page that opens:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;claude mcp add --transport http flowrunner https://app.flowrunner.ai/mcp
claude mcp login flowrunner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ChatGPT, Codex, and Cursor:&lt;/strong&gt; follow &lt;a href="https://docs.flowrunner.ai/build/prompt-to-flow" rel="noopener noreferrer"&gt;the setup in the docs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You sign in with your FlowRunner account. If you don't have one, the &lt;a href="https://flowrunner.ai/pricing" rel="noopener noreferrer"&gt;Free plan&lt;/a&gt; needs no card.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Convert one workflow at a time
&lt;/h2&gt;

&lt;p&gt;Start a new chat, paste the request that matches your files, then paste one workflow's files after it.&lt;/p&gt;

&lt;p&gt;For an Export Workflows file:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rebuild this Pipedream workflow as a FlowRunner flow. It comes from Pipedream's Export Workflows: the JSON lists the trigger, the steps in order with their settings, and the source of each code step. Values shown as  were removed by Pipedream. Keep the same behavior. Before you build, tell me anything you can't map one to one, and ask me about anything the file doesn't say.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For GitHub Sync files:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rebuild this Pipedream workflow as a FlowRunner flow. It comes from Pipedream's GitHub Sync: workflow.yaml lists the trigger and the steps in order, and the source of each code step follows it. Keep the same behavior. Before you build, tell me anything you can't map one to one, and ask me about anything the files don't say (for example the trigger's schedule or URL, which GitHub Sync doesn't record).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the workflow's entry in &lt;code&gt;manifest.json&lt;/code&gt; has a &lt;code&gt;warnings&lt;/code&gt; list, paste that too. It names every secret and connection you'll need to fill in.&lt;/p&gt;

&lt;p&gt;Answer its questions, and it builds the flow in your workspace. Code steps come across as Custom Cloud Code, or as a regular block where one does the same job, and Python steps are rewritten in JavaScript. One workflow per chat keeps the conversation short and the questions specific.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Finish what the files can't carry
&lt;/h2&gt;

&lt;p&gt;Each conversion ends with a short list from the builder. Expect some of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connect your accounts.&lt;/strong&gt; Each one is connected once and shared across your flows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fill in secrets and environment variables.&lt;/strong&gt; The builder asks for them or tells you where each one goes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Note the new address.&lt;/strong&gt; Every form, app, or service that calls your Pipedream webhook URL will need the flow's new URL when you switch over in step 5.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check workflows that answer their caller.&lt;/strong&gt; If a Pipedream workflow used &lt;code&gt;$.respond&lt;/code&gt; to send something back, the FlowRunner flow is called through the &lt;a href="https://docs.flowrunner.ai/api/call-flow-blocking.html" rel="noopener noreferrer"&gt;blocking Call Flow API&lt;/a&gt; instead. That call carries your workspace key, so make it from a server, not from a visitor's browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set the schedule.&lt;/strong&gt; GitHub Sync files don't record it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start the flow,&lt;/strong&gt; if the builder left it stopped.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Test, then switch over
&lt;/h2&gt;

&lt;p&gt;Run each flow with the events you saved in step 1 and compare the results with what Pipedream produced. When they match, point the callers at FlowRunner, watch the runs for a few days, and turn off the Pipedream workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three real conversions: a moderated guestbook
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.raymondcamden.com" rel="noopener noreferrer"&gt;Raymond Camden&lt;/a&gt; ran the guestbook on his site with three Pipedream workflows, and he keeps them public in his &lt;a href="https://github.com/cfjedimaster/General-Pipedream-Stuff" rel="noopener noreferrer"&gt;General-Pipedream-Stuff&lt;/a&gt; repo. Thanks to Raymond for letting us use them. Together they make a small moderation loop: a visitor's entry lands in a Google Sheet, Raymond gets an email with an approve link, and the site only shows entries he approved.&lt;/p&gt;

&lt;p&gt;The input for all three was his GitHub Sync files, exactly as they sit in the repo: each workflow's &lt;code&gt;workflow.yaml&lt;/code&gt; plus one JavaScript file per code step. Each conversion used the GitHub Sync request from step 3, followed by the files.&lt;/p&gt;

&lt;p&gt;Because the request asks for it, the builder listed what it couldn't map one to one and asked its questions before building anything. All three flows were then connected to a test spreadsheet and a Gmail account and tested end to end.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Guestbook Entry
&lt;/h3&gt;

&lt;p&gt;A visitor's form posts a name and a comment. The workflow checks both are there, adds a row to the sheet with Approved set to FALSE, and emails Raymond the entry with a "Click to Approve" link. (&lt;a href="https://github.com/cfjedimaster/General-Pipedream-Stuff/tree/production/add-guestbook-entry-p_yKCwGWl" rel="noopener noreferrer"&gt;Original files&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;workflow/2022.04&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Add Guestbook Entry&lt;/span&gt;
&lt;span class="na"&gt;settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;error_notification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;triggers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hi_mJH1wmn&lt;/span&gt;
&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;validate&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs20.x&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./validate/entry.js"&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;add_single_row&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs20.x&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;google_sheets-add-single-row@2.1.2&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;googleSheets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;authProvisionId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apn_DphexA&lt;/span&gt;
    &lt;span class="na"&gt;sheetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;__lv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Guestbook&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1RLspI8iuC-Ng8Fco8zV-ynpxdPLm7MgoXm5jnlo0Tpk&lt;/span&gt;
    &lt;span class="na"&gt;sheetName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;__lv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sheet1&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sheet1&lt;/span&gt;
    &lt;span class="na"&gt;hasHeaders&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Yes'&lt;/span&gt;
    &lt;span class="na"&gt;col_0000&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{steps.trigger.event.body.name.replace(/&amp;lt;.*?&amp;gt;/g,'')}}"&lt;/span&gt;
    &lt;span class="na"&gt;col_0001&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{steps.trigger.event.body.comment.replace(/&amp;lt;.*?&amp;gt;/g,'')}}"&lt;/span&gt;
    &lt;span class="na"&gt;col_0002&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{Date.now()}}"&lt;/span&gt;
    &lt;span class="na"&gt;col_0003&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;FALSE'&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create_email_body&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs20.x&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./create_email_body/entry.js"&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;send_email_to_self&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs20.x&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;email-send-email-to-self@0.4.4&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;New Guestbook Entry&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{steps.create_email_body.$return_value}}"&lt;/span&gt;
    &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{steps.create_email_body.$return_value}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsx6lwdfgbyhadkluiiux.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsx6lwdfgbyhadkluiiux.webp" alt="The Add Guestbook Entry flow on the FlowRunner canvas: Start, an HTTP trigger, a Validate: name and comment present? condition whose Yes branch leads to Prepare row, Add single row (Google Sheets), Create email body, and Send email to self (Gmail)." width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pipedream&lt;/th&gt;
&lt;th&gt;FlowRunner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTTP trigger&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://docs.flowrunner.ai/reference/external-callback.html" rel="noopener noreferrer"&gt;External Callback&lt;/a&gt;. The posted JSON body becomes the trigger data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;validate&lt;/code&gt; code step, which ends the run with &lt;code&gt;$.flow.exit&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A &lt;a href="https://docs.flowrunner.ai/reference/condition.html" rel="noopener noreferrer"&gt;Condition&lt;/a&gt;: name and comment both present. The No branch has nothing after it, so the run ends there.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code inside the column fields (strip HTML tags, &lt;code&gt;Date.now()&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;A small &lt;a href="https://docs.flowrunner.ai/reference/custom-cloud-code.html" rel="noopener noreferrer"&gt;Custom Cloud Code&lt;/a&gt; step that prepares the row. FlowRunner fields take references to earlier results, not code.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Sheets, Add Single Row&lt;/td&gt;
&lt;td&gt;Google Sheets, Add Row. It returns the range the row landed in, which the approve link needs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;create_email_body&lt;/code&gt; code step&lt;/td&gt;
&lt;td&gt;Custom Cloud Code, with Raymond's HTML almost unchanged.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The &lt;code&gt;APPROVE_GB&lt;/code&gt; environment variable&lt;/td&gt;
&lt;td&gt;The Approve flow's address, written into the email code step.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email, Send Email To Self&lt;/td&gt;
&lt;td&gt;Gmail, Send Email, addressed to the owner.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The two code steps carry Raymond's logic over. The first holds what Pipedream ran inside the column fields: stripping HTML tags from the name and comment, and the &lt;code&gt;Date.now()&lt;/code&gt; timestamp.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwbbco1i544v7hzaox9gp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwbbco1i544v7hzaox9gp.webp" alt="The Prepare row code step in the Code Editor: name and comment arguments bound to the incoming request, and four lines that strip HTML tags from both and return the row in sheet order, Name, Comment, Date.now(), FALSE." width="799" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second is Raymond's email HTML, with the approve flow's address passed in where his code read the &lt;code&gt;APPROVE_GB&lt;/code&gt; variable, and the row's range taken from the Add Row step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F291l4gwwc06vaqdhzzem.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F291l4gwwc06vaqdhzzem.webp" alt="The Create email body code step: arguments for name, comment, updatedRange from the Add Row step, and approveUrl, and Raymond's HTML template building a Click to Approve link from the approve URL and the encoded range." width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Left to finish by hand:&lt;/strong&gt; connect Google Sheets and Gmail, pick the spreadsheet, set the address the email goes to, and start the flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tested:&lt;/strong&gt; a test entry added the row, with Approved set to FALSE, and the email arrived with a working approve link.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5897a8sdxgtfpt6wy6xk.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5897a8sdxgtfpt6wy6xk.webp" alt="The Guestbook spreadsheet after the test entry: headers Name, Comment, Date, Approved, and one row reading Ada, Hello from the test, 1791494504814, FALSE." width="686" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt; the builder replaced the validation code with a Condition block, which shows on the canvas instead of hiding in code. Pipedream's Send Email To Self needed no setup; on FlowRunner the email goes through your own email account, so you connect one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approve Guestbook Entry
&lt;/h3&gt;

&lt;p&gt;The approve link in the email opens this workflow with the new row's range in the address. It sets that row's Approved cell to TRUE, and the entry shows up on the site. (&lt;a href="https://github.com/cfjedimaster/General-Pipedream-Stuff/tree/production/approve-guestbook-entry-p_MOCayaq" rel="noopener noreferrer"&gt;Original files&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fozewfqxfektv2cbc8njq.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fozewfqxfektv2cbc8njq.webp" alt="The Approve Guestbook Entry flow on the FlowRunner canvas: Start, an HTTP trigger, a Find Approved cell code step, and Update cell (Google Sheets)." width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pipedream&lt;/th&gt;
&lt;th&gt;FlowRunner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTTP trigger&lt;/td&gt;
&lt;td&gt;External Callback. The &lt;code&gt;range&lt;/code&gt; in the link becomes the trigger data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The cell expression &lt;code&gt;range.split(':').pop()&lt;/code&gt; in the Update Cell step&lt;/td&gt;
&lt;td&gt;A one-line Custom Cloud Code step that picks the Approved cell, for example D5.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Sheets, Update Cell&lt;/td&gt;
&lt;td&gt;Google Sheets, Update Cell by A1, set to TRUE.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A code step that redirects the browser to the guestbook page&lt;/td&gt;
&lt;td&gt;Not reproduced. See the notes.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Raymond's one-line cell expression became a one-line code step, and its result feeds the Google Sheets block's cell address.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbesr123ywhf8byc9n14j.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbesr123ywhf8byc9n14j.webp" alt="The Find Approved cell code step: one argument, range, bound to the approve request, and a single line returning the part of the range after the colon, with a comment showing 'Sheet1'!A5:D5 becoming D5." width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F07qpeqdnamfzneopmtmn.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F07qpeqdnamfzneopmtmn.webp" alt="The Update cell block configured as Google Sheets Update Cell by A1: Document Guestbook, Sheet 0, A1 Cell Address bound to the Approved Cell result, and Cell Value TRUE." width="800" height="627"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Left to finish by hand:&lt;/strong&gt; connect Google Sheets and pick the spreadsheet, then start the flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tested:&lt;/strong&gt; clicking the approve link in the test email set that row's Approved cell to TRUE.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt; Raymond's last step sends the browser back to his guestbook page with a redirect. Our version doesn't, so the browser shows FlowRunner's run ID instead of the guestbook page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get Guestbook Entries
&lt;/h3&gt;

&lt;p&gt;The guestbook page calls this workflow to get the approved entries. It reads the sheet, keeps the approved rows, and answers with them as JSON. (&lt;a href="https://github.com/cfjedimaster/General-Pipedream-Stuff/tree/production/get-guestbook-entries-p_D1Cza5O" rel="noopener noreferrer"&gt;Original files&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbacb6vkaqlg4fycwhrcu.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbacb6vkaqlg4fycwhrcu.webp" alt="The Get Guestbook Entries flow on the FlowRunner canvas: Start leads straight to Get values in range (Google Sheets), then Filter and map, then Return approved, with no trigger block, because the flow is called directly." width="799" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pipedream&lt;/th&gt;
&lt;th&gt;FlowRunner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTTP trigger plus a &lt;code&gt;$.respond&lt;/code&gt; step that answers the caller&lt;/td&gt;
&lt;td&gt;A flow with no trigger block that ends in &lt;a href="https://docs.flowrunner.ai/reference/return-result.html" rel="noopener noreferrer"&gt;Return Result&lt;/a&gt;, called through the &lt;a href="https://docs.flowrunner.ai/api/call-flow-blocking.html" rel="noopener noreferrer"&gt;blocking Call Flow API&lt;/a&gt;, so the caller gets the answer in the same request.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Sheets, Get Values in Range (&lt;code&gt;A2:D10000&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Google Sheets, Get Rows. Rows come back keyed by the header names instead of by position, with no row cap.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;filter_and_map&lt;/code&gt; code step&lt;/td&gt;
&lt;td&gt;Custom Cloud Code that reads the columns by name and returns name, comment, and date for each approved row.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;return_approved&lt;/code&gt; code step (&lt;code&gt;$.respond&lt;/code&gt; with JSON)&lt;/td&gt;
&lt;td&gt;Return Result, content type JSON.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Because the rows now come back keyed by their headers, the filter reads &lt;code&gt;r.Approved&lt;/code&gt; and &lt;code&gt;r.Name&lt;/code&gt; where Raymond's code read &lt;code&gt;a[3]&lt;/code&gt; and &lt;code&gt;a[0]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fet0k0d9n4u2s3sypr7gj.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fet0k0d9n4u2s3sypr7gj.webp" alt="The Filter and map code step: a rows argument bound to the Guestbook Rows result, keeping rows whose Approved value is TRUE and returning name, comment, and date for each." width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyngay9eoft35abfqz583.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyngay9eoft35abfqz583.webp" alt="The Return approved block: Return Result with Content Type JSON and Result set to the Approved Entries list from the previous step." width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Left to finish by hand:&lt;/strong&gt; connect Google Sheets and pick the spreadsheet, then start the flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tested:&lt;/strong&gt; the call returned exactly what Raymond's workflow returns, the approved entries as a JSON array:&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="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Ada"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Hello from the test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"1791494504814"&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;Notes:&lt;/strong&gt; this is the one workflow whose trigger changed shape. A Pipedream HTTP trigger can answer its caller directly; on FlowRunner, a flow that answers its caller is called through the blocking API instead. The calling address includes your workspace key, so call it from a server and pass the key in a header rather than from the visitor's browser.&lt;/p&gt;

</description>
      <category>pipedream</category>
      <category>automation</category>
      <category>javascript</category>
      <category>migration</category>
    </item>
  </channel>
</rss>
