Run custom Python code to transform data, implement complex logic, and extend workflow capabilities
The Python action executes custom Python code within your workflow. Use it when built-in actions can’t handle your specific transformation, calculation, or logic requirements—or when you prefer Python over JavaScript.
Access output from any previous node in your workflow:
Copy
# Access the start node (tool/play inputs)company_domain = nodes["start"]["company_domain"]contact_email = nodes["start"]["contact_email"]# Access output from an enrichment nodecompany_name = nodes["enrich"]["company_name"]employee_count = nodes["enrich"]["employee_count"]# Access results from a search nodecontacts = nodes["search"]["results"]# Access the full output dictionary from any nodeall_enrich_data = nodes["enrich"]
When your Python action is inside a Group node, use parentNodes to access data from nodes outside the group:
Copy
# Inside a Group: access the current item being iteratedcurrent_contact = nodes["group"]["item"]current_index = nodes["group"]["index"]# Access data from nodes OUTSIDE the grouporiginal_company = parentNodes["enrich"]["company_name"]all_settings = parentNodes["config"]["settings"]
Expression
Description
nodes["group"]["item"]
The current item in the loop
nodes["group"]["index"]
The zero-based index of the current item
parentNodes["node_name"]
Access nodes outside the current group
parentNodes["start"]
Access the original tool/play input
Use parentNodes when you need to reference data that was computed before the
Group started—like company-level data when iterating over contacts.
contacts = nodes["search"]["results"]# Transform list of contacts into a formatted listresult = [ { "full_name": f"{c['first_name']} {c['last_name']}", "email": c["email"].lower(), "domain": c["email"].split("@")[1] } for c in contacts]
When processing items inside a Group, combine item data with parent context:
Copy
# Inside a Group iterating over contactscontact = nodes["group"]["item"]index = nodes["group"]["index"]# Access company data from outside the groupcompany_name = parentNodes["enrich"]["company_name"]company_industry = parentNodes["enrich"]["industry"]result = { "contact_name": f"{contact['first_name']} {contact['last_name']}", "company": company_name, "industry": company_industry, "position": index + 1, "email_subject": f"{contact['first_name']}, a quick note about {company_name}"}
Packages are automatically loaded when you import them. Simply use import statements and Pyodide will fetch the required packages:
Copy
import numpy as npimport pandas as pd# Your code using numpy and pandasdata = nodes["search"]["results"]df = pd.DataFrame(data)result = df.describe().to_dict()
Popular packages available include:
Package
Description
Example use
numpy
Numerical computing
Array operations, math
pandas
Data analysis and manipulation
DataFrames, CSV processing
regex
Advanced regular expressions
Complex pattern matching
beautifulsoup4
HTML/XML parsing
Web scraping, HTML extraction
pyyaml
YAML parsing
Config file processing
python-dateutil
Date parsing utilities
Flexible date parsing
requests
HTTP library (limited)
Simple HTTP requests
Pyodide supports hundreds of packages. If an import fails, the package may not
be available in the Pyodide environment. Check the Pyodide packages
list for
availability.
email = nodes["start"].get("email")company_size = nodes["start"].get("company_size")# Validate required fieldsif not email or not isinstance(email, str): result = {"error": "Invalid email", "valid": False}elif "@" not in email: result = {"error": "Email must contain @", "valid": False}elif company_size is not None and not isinstance(company_size, (int, float)): result = {"error": "Company size must be a number", "valid": False}else: result = {"valid": True}
Always validate data when it comes from external sources or user input. Return
structured error dictionaries so downstream nodes can handle failures.
Each script should do one thing well. If your script is growing complex,
consider splitting it into multiple Python nodes or defining helper
functions within the script.
Return structured data
Always return dictionaries with clear key names. This makes it easier to
access data in downstream nodes and debug issues.
Handle None and missing keys
External data often contains None or missing keys. Use .get() with
default values to handle these gracefully.
Validate before processing
Check that required fields exist and have the expected types before
performing operations. Return early with error information when validation
fails.
Avoid side effects
Scripts should be pure functions—given the same inputs, they should always
return the same outputs. Don’t rely on external state.