An operator is a node that executes a deterministic action: no LLM, no randomness. For any given input, the output is predictable.
Why use them
Any logic that can be written explicitly should be. An LLM that "computes a total" or "formats a date" is an anti-pattern: it is slower, more expensive, and can make mistakes.
Practical rule: if you can write the function in 5 lines of Python, it is an operator — not an agent.
Available types
webhook_action (HTTP call)
Makes an outbound HTTP call. Configuration:
- URL, method, headers and query parameters (with placeholders from context)
- Body:
none,json,form,raw,multipart, ormapper(body composed by the mapping language, see below) - Authentication:
bearer,basic,api_key_header(custom header), oroauth2(see below) - Accepted HTTP statuses, maximum timeout, and number of retries (
retry_count, 0 by default, exponential backoff) - Output key where the response is published
database_query / database_manage
database_query reads rows from a betool no-code database or from an external database (Postgres, MySQL, MongoDB) registered in Applications → Databases → Connections: where filter, full-text search, limit. database_manage writes to it (create or update), the content being composed by the mapping language.
file_transform
Transforms a file. Built-in transformers:
pdf_extract_text— selectable text from a PDF (no OCR: a scanned PDF yields no text)pdf_split— splitting a PDFpdf_to_images— rendering a PDF's pages as imagesxlsx_to_json/xls_to_json— reading an Excel workbook as JSON rowsunzip— extracting an archive
file_link (download links)
Delivers files to a third party by link, without putting their content into the flow: for each file in a list from the context (the files of a download, received attachments, the photos of an inspection…), the Download links node produces a signed download link with a limited lifetime. A following step composes what the recipient receives: response of an inbound webhook, email…
| Setting | Role |
|---|---|
List of files to link (input_path) | Where to read the list in the flow — each entry carries path (the file's key in the organisation's storage) and optionally a file name. Required. |
Allowed sub-folders (path_scope) | Optional: restricts the signable files to certain sub-folders of the organisation's storage. |
| Link lifetime | 15 minutes by default, from 1 minute to 24 hours. |
Maximum number of files (max_files) | 20 by default, 100 at most. |
Output key (output_key) | Default linked.files. |
The output contains files (each original entry, enriched with url and expires_at), file_count, failed, requested and ttl_seconds.
- Never outside your organisation: a file outside its storage is refused.
- Existence checked before signing: a missing file is never signed.
- Nothing omitted: a file that is not linked (out of scope, not found, beyond the cap, signing impossible) appears in
failedwith its reason. - Links are recreated on every execution, and every link issued is logged.
The link is a bearer link: whoever holds it downloads the file until it expires. Keep the lifetime short, and fill in the allowed sub-folders as soon as the list comes from modifiable data (a table row) — otherwise anyone who can write that data could get a link signed to any file of the organisation.
compute
Deterministic computations on context data. Built-in operations:
column_stats— per column of a list of rows, how many equal a given value (count,total,pct)compose_text— composing a text from a template and flow valuesregex_extract— extracting values with a regular expressionselect_by_substring— splitting a list into groups according to "contains / does not contain" predicateslist_compare— consistency of several lists by pivot keymulti_source_compliance— multi-dimension, multi-source compliance auditcontact_extract— email addresses and phone numbers found in a text, with their originfree_slots— free time slots derived from busy periodsurl_expand— composing URLs from bases and paths
Mapping language
This is not a node: it is the language that composes an output payload from context data (functions $now, $uuid, $concat…). It is used by the mapper body of webhook_action, the content written by database_manage and the variables of an outbound phone_call.
data_to_file
Produces an Excel file (.xlsx) from context data — a list of rows, a grid, or several lists (one sheet each). Options: file name (templatable), sheet name, choice and order of columns, header row. The file is stored in your organisation's storage, ready to be sent by email or dropped on a Drive.
For security, cells starting with =, +, - or @ are written as text by default (no formula injected). When Excel's limits require cutting, the file is flagged as truncated.
web_fetch
Reads one or more web pages and publishes their readable text in the context (default web.pages). Settings: number of pages, concurrency, timeout, maximum size. The robots.txt verdict is always computed and reported; it is only enforced if you enable it (respect_robots).
Pages rendered only by JavaScript are not executed: only the served HTML is read. A failed page is reported in the result, without failing the pipeline.
On the agent side, two equivalent tools:
web_scrape.fetch_page— reads a page (title, text, email and phone links found).web_search.search— web search via a SearXNG engine self-hosted by the platform: up to 5 results (title, URL, snippet).
All web reads go through the same anti-SSRF protection: internal, private and cloud metadata addresses are refused after DNS resolution, and redirects are re-checked at each hop.
code_step
When deterministic logic exceeds what compute can handle, a code step executes custom code in a Starlark sandbox — editable directly, run as soon as it is saved, and bounded by a tool allowlist. See Code steps.
Authentication & secrets
Operators that communicate with an external system (HTTP, DB) reference an account registered within the organisation. Credentials are never visible inside the pipeline itself — only an account name is referenced.
As a result, rotating a password requires no edits to any pipeline.
When a secret is entered directly in a node (bearer token, basic password or api_key_header value of webhook_action, password of an encrypted PDF or archive for file_transform), it is masked as soon as it is saved: the editor, the API and the MCP tools only show a marker. See The visual editor, section Secrets.
Outbound OAuth2 (client_credentials)
For APIs that require an OAuth2 token (enterprise cloud, ERP, third-party providers), the HTTP operator supports oauth2 mode. You register an OAuth2 credential in the organisation — token_url, client_id, client_secret, scope — and the node references only a non-sensitive identifier.
At call time, betool obtains a token and reuses it as long as it remains valid; it lazily refreshes it on expiry, with no background task. The client_secret is never returned by the administration API (only a has_secret indicator is exposed), and the obtained token is placed as Authorization: Bearer … on the outbound request.
Rotating the secret after a provider-side rotation requires no pipeline edits: the next call refreshes automatically.
Idempotency
Any operator that produces a side effect (HTTP POST, DB INSERT, email send) must be designed for idempotency on the target side. Recommended practice:
- Include an
idempotency_keyin the payload (typicallyexchange_id). - Check on the target side whether the key has already been processed.
Apart from the retries you explicitly configure on webhook_action (retry_count), betool does not automatically retry a failed operator: it is your pipeline's responsibility to orchestrate the retry strategy. webhook_action is also protected against double sending during an internal re-dispatch: a request already delivered with an accepted status is not sent again.