Which node for the job?
Code is one of several ways to do work in a workflow. Pick the simplest one that fits:Clean up incoming data
The job: a web form posts leads with messy names, emails in capitals and phone numbers full of spaces. Clean them before they reach your CRM.{{clean_lead.result.email}}.
Watch for:
- Use
body.get("field")rather thanbody["field"]. A form that leaves a field out would otherwise stop the step with aKeyError. - Return a
validflag and branch on it with a Condition, rather than failing the step on bad input.
Act on every row of a sheet
The job: every morning, post each qualified lead in a Google Sheet to Slack, and report any that failed.sheet_id is a workflow variable holding the spreadsheet’s ID.
Watch for:
- Google Sheets leaves out empty cells at the end of a row, so a row can be shorter than you expect.
(row + ["", "", ""])[:3]pads it. - Catching
IntegrationErrorinside the loop means one bad row doesn’t stop the rest.failedtells you which rows to look at. - Every message counts toward the limit of 200 app calls per run. For bigger sheets, handle a batch per run, as in the next scenario.
- Calls already made are not undone. If the step runs twice, the messages go out twice, so remember what you’ve done.
Handle only what’s new since the last run
The job: a sheet keeps growing. Each hour, handle only the rows added since the last run, at most 150 at a time.rows_done of type Number, starting at 0. It remembers how many rows are already handled:
set_variableruns once, at the end. If the step fails halfway,rows_donekeeps its old value and the next run sends that batch again. When a repeat would hurt, callset_variableafter each row instead. That’s up to 100 calls per run.- This counts rows, so it assumes rows are only ever added at the bottom. If people insert or delete rows, remember an ID or a date instead: store the newest one you’ve handled, and skip anything older.
Build a report and send it as a file
The job: every Monday, total last week’s orders by region into an Excel file and email it.{{weekly_report.files[0]}} into the attachment field. Use {{weekly_report.result.revenue}} in the message text.
Watch for:
- Save the file under a plain name, like
weekly-sales.xlsx. Files in subfolders are not attached. float(...)turns pandas’ number into a plain one, sooutput_datacan store it.- Charts work the same way:
matplotlibandplotlyare installed, andplt.savefig("chart.png")produces a file to attach.
Score a lead and route it
The job: score each new lead by fixed rules, then send hot leads to sales and the rest to a nurture list.- Form values often arrive as text.
int(...)makes"120"a number before you compare it. - Code gives the same answer for the same lead every time, and costs no model call. When the decision needs reading a free-text message, use an LLM instead, or let code do the arithmetic and an LLM the reading.
Pull facts out of a document
The job: invoices arrive as PDFs. Pull out the invoice number, date and total.- Let Read File turn the PDF into text. The code works on its
content. - Patterns fit documents from the same sender. When layouts vary a lot, an LLM reads them more reliably.
- Return
Nonefor a field you couldn’t find, and check it in a Condition, rather than guessing.
Call a web service that isn’t in the app list
The job: look up public data, such as an exchange rate, from a service without an app in the catalog.- Always pass a
timeout. Without one, a slow service can hold the step until the 30-minute limit. raise_for_status()stops the step on an error answer instead of returning bad data.- Everyone who can edit the workflow can read the code. For a service that needs a key or a login, connect it as an app, or use the HTTP Request node.
Habits that keep code steps reliable
Catch errors per item, and report them
Catch errors per item, and report them
Wrap the work for each record in
try / except, and collect what failed in the output. One bad record then costs one record, not the whole run.Make a repeat harmless
Make a repeat harmless
A step can run twice: a retry, a re-run, an overlapping schedule. Before creating something, check whether it exists, or remember what you’ve done in a workflow variable. Calls already made are never undone.
Stay under the limits
Stay under the limits
One run has 30 minutes, 200 app calls and 100
set_variable calls. For big jobs, handle a batch per run and remember where you stopped.Keep the error stream quiet
Keep the error stream quiet
Anything written to the error stream fails the step, warnings included. Silence the warnings you expect with
warnings.filterwarnings("ignore").Look before you parse
Look before you parse
Not sure what an earlier node or an app call returns?
print() it once and read stdOut after a test run. Then read the fields with .get() so a missing one doesn’t stop the step.One job per node
One job per node
A node that cleans, scores and sends is hard to fix. Split it into steps with clear names; each one’s output shows in the run history.
Related
Execute Code
Inputs, outputs, files, variables, the editor and limits.
Execute Code integrations
Bind apps and call them from Python.
Loop
Run several nodes for every item.
Condition
Branch on a value your code returned.
