Built-in steps: e-mail, HTTP, Transform, AI and Files
Some steps are so common that writing a script for them is a waste: sending an e-mail, calling an API, filtering a list. A flow has them built in. You configure each one in its side panel, bind its values like any other step’s inputs, and it runs inside the worker itself — with network access and credentials that never pass through your code.
- Send a published Email Studio template to a list of recipients
- Call an API with HTTP Request, with or without an API (HTTP) connection
- Filter, sort, summarise or combine a list with Transform
- Classify or summarise text with the AI step, and get named fields back
- Read a CSV, Excel or JSON file into rows, and write rows into a file
Steps without code
Open the + between two steps (or drag from the palette). Besides the script languages and the control nodes, the menu has Send email and an Apps & data group with HTTP Request and Transform. A built-in step is inserted at once; everything else — connection, address, rules — is chosen in its side panel.
They differ from a script step in two ways that matter. They run in the worker process, not in the script sandbox, so the HTTP step can reach the internet while your scripts cannot. And they take their secrets from a connection or a secret variable, which the run records as ••• — a password never sits in the flow in clear. Everything else is the same: their inputs use the four binding modes (bindings), their output feeds later steps, and they accept the run settings (retry, timeout, skip, cache…).
Send email
The Send email step sends an Email Studio template through one of the project’s e-mail connections.
- Pick the connection and the template
Connection defaults to Default connection, the project’s default sender. Under Template choose one; the step always sends the template’s published version, and says so when the template has unpublished changes.
- Bind the recipients
Recipients takes one address, several separated by commas, or a list from an earlier step — for example the
emailof each row inside a loop. - Fill the template values
Template values lists every value the template uses, each a binding row. A bound list stays a list, so the template can loop over it. Subject is optional: empty uses the template’s own subject, and it may use the template’s values, like
{{name}}. - Attach files and preview
Add attachment binds a file produced by an earlier step — up to 10 MB in total. Preview e-mail renders the message as it will be sent; values only known at run time are named, not invented.
{
"ok": true,
"historyId": "7c1e…",
"to": [
"ana@example.com"
],
"subject": "Your weekly report"
}HTTP Request
HTTP Request calls any API and hands its answer to the next step. Under Connection pick an API (HTTP) connection or leave No connection. With a connection the address may be relative — the panel shows Relative to https://api.example.com — and the sign-in is added for you.
- Request: the method (GET, POST, PUT, PATCH, DELETE or HEAD) and the address. Query and Headers are name/value rows; every value is a binding row.
- Body: None, JSON, Form, Text or File (a file from an earlier step).
- Response: Read the answer as Automatic, JSON, Text or File; Fail the step when the answer is an error (4xx/5xx) is on by default.
- Pagination: off, Next URL in the answer, Link header or Page number, with Items are in and At most this many pages. The items of every page are joined into one list.
The output is {status, headers, body}; with pagination body is the joined list and pages counts the pages read. A later step binds output.body.items, output.status and so on.
{
"status": 200,
"headers": {
"content-type": "application/json"
},
"body": {
"items": [
{
"sku": "A-1",
"stock": 4
}
]
}
}Import a cURL command
Import cURL takes a command copied from an API’s documentation or your browser’s network tab and fills the method, address, query, headers and body. A token or password it finds is listed under Secrets found and kept as a secret project variable (Store as names it), so it never sits in the flow in clear. Typing something that looks like a secret into a header shows the same offer: Store as a variable.
Transform
Transform reshapes a list without code. Bind Input to a list (a script’s return value, an HTTP body, a SQL step’s rows) and choose what to Do:
| Do | What you set |
|---|---|
| Filter | Keep items that match all / any of: rules such as is, is at least, contains, is empty — or Write an expression instead, where item is each element. |
| Sort | Sort by one or more fields, A→Z or Z→A. Empty values go last. |
| Limit | Keep the first N, optionally after skipping M. |
| Remove duplicates | Duplicates by a list of fields; the first one is kept. |
| Set fields | Set, rename, remove or keep only some fields of every item. |
| Summarise | Group by a field (or none) and compute count, sum, average, smallest, largest or a list. |
| Combine | Combine with a second list: only matches, every item of this list, or one list after the other. |
A field an item does not have counts as empty, never as an error. An input that is not a list fails the step with transform_not_a_list. The Preview tab runs the step on the last real input, the way a run will, and reports Kept 2 of 3; before any run you can paste a sample list instead.
AI step
The AI step asks one of the project’s AI connections a question and hands the answer on. Choose the AI connection and a Model (Connection default uses the one the connection runs), write the Instructions, and bind the Prompt — {{ name }} in its text is filled from the step’s other bindings.
Answer is Text (output {text, usage}) or JSON with fields you declare — name, type, required. The step checks the answer: a missing required field or a wrong type fails it with ai_answer_invalid. Declared fields can be bound by later steps before the first run, so a condition can branch on output.urgency at once.
{
"category": "billing",
"urgency": "high",
"usage": {
"model": "gpt-4o-mini",
"tokensIn": 212,
"tokensOut": 14
}
}File
File turns a file into rows, or rows into a file. Read a file binds a file from an earlier step (an HTTP Request answer read as a file, a Google Drive download) and reads CSV, Excel (XLSX) or JSON — Automatic by its name and content. CSV options set the separator, the encoding (UTF-8 or Latin-1) and whether the first row is the header; an Excel file reads its first sheet unless you pick one.
{
"rows": [
{
"order": 1041,
"date": "2026-10-01",
"total": 89.9
}
],
"count": 1,
"columns": [
"order",
"date",
"total"
],
"sheets": [
"Orders"
]
}Write a file binds Rows to a list and writes CSV, XLSX or JSON under the File name you give. The output is a file value with a download link, which a Send email step can attach and Google Drive can upload.
Testing one step
HTTP Request, AI, File, Google Sheets and Google Drive have a Run test tab. For HTTP Request it runs the request now, alone, with its constants and the Values for this test you type (or the values from the last run), and shows the Answer. It goes through the same worker path a real run uses, so what you see is what the flow will get.
Limits and error codes
- An answer with status 400 or above fails the step with
http_status_error(status and the first part of the body recorded) unless you turned the option off; a 429’sRetry-Afteris honoured by a step retry. - A request has 60 seconds by default (the step’s own timeout replaces it), follows at most 5 redirects, and reads at most 50 MB (
http_response_too_large). A network failure ishttp_request_failed. - Private and internal addresses are refused on every hop (
host_not_allowed); an answer over 5 MB is kept as a file the next step can read. Pagination reads at most 100 pages. - An e-mail step whose template was never published fails with
template_not_published; attachments are limited to 10 MB in total. - A File step reads at most 50 MB (
file_too_large); a file it cannot read as the chosen format fails withfile_parse_failed, and a sheet name that is not in the workbook withfile_sheet_not_found. An AI provider failure isai_step_failed. - A setting that cannot be used (an empty address, a relative address with no connection) fails with
invalid_node_config, naming the step and the field.