Documentation

Deterministic Operators

Action nodes that bypass the LLM — HTTP, DB, transformations, computations.

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, or mapper (body composed by the mapping language, see below)
  • Authentication: bearer, basic, api_key_header (custom header), or oauth2 (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 PDF
  • pdf_to_images — rendering a PDF's pages as images
  • xlsx_to_json / xls_to_json — reading an Excel workbook as JSON rows
  • unzip — extracting an archive

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…

SettingRole
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 lifetime15 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 failed with 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 values
  • regex_extract — extracting values with a regular expression
  • select_by_substring — splitting a list into groups according to "contains / does not contain" predicates
  • list_compare — consistency of several lists by pivot key
  • multi_source_compliance — multi-dimension, multi-source compliance audit
  • contact_extract — email addresses and phone numbers found in a text, with their origin
  • free_slots — free time slots derived from busy periods
  • url_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_key in the payload (typically exchange_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.