Skip to main content

Pre-call functions

Pre-call functions run before the conversation — before the number is dialed on an outbound call, and after the connection but before the agent’s first line on an inbound one. Their result lands in the call’s variables, so the agent knows them from its first word. When exactly the block starts and what budget it has is on Pre- and post-call functions.

Adding a pre-call function

In the pre-call functions block, click + Add and pick a kind:
  • Custom function — an HTTP request to your system;
  • Code — a short Python script in a sandbox.
The card appears in the block and its form opens on the right. A new function is named by kind and number — “Function 1”, “Code 1” — and you can rename it.

Shared fields

  • Name — the heading of the card and of the panel. It is a label only; it does not affect variables.
  • Description — a note for you and your colleagues.
  • When to run — “After previous” or “Parallel with previous” (see Ordering and parallelism).

Custom function (HTTP)

  • Name — inside the function form this is a second, different field, and it is the prefix of the result variables. A function named crm writes crm_status, crm_success, crm_error, crm_result. Both fields are labeled “Name”: the one at the top of the panel is the card title, the one inside the function form is the prefix.
  • Execution mode — “Mock (fixed response)” or “HTTP (real call)”. While you are still building the flow, keep the mock: nothing leaves the platform.
  • Method, URL, headers, query parameters, body — every one of these fields supports {{variable}} substitution.
  • Response variables (variable ← JSONPath) — the variable name the flow will see is on the left, the path in the JSON response on the right, for example $.data.balance. Only what is listed here becomes a variable.
  • Timeout, s — 10 by default, 30 maximum.
  • Retries and Backoff, s — 0 by default, so there are no retries. A retry fires on a network error, a timeout, and a 5xx response; a 4xx is not retried. Remember that retries run inside the block timeout.
  • Successful response statuses2xx by default; you can list your own codes and ranges, for example 200-299, 404.
Custom function panel: the card Name on top and the function Name — the variable prefix — inside the Function section

Code

  • Code — a Python sandbox. It exposes vars (the call variables), metadata (call metadata), http(...) (a request under the same URL policy as a custom function), and now("Asia/Almaty"). There are no imports, no file access, and no print.
  • Output variables (comma-separated) — the names your code put into result; they become dynamic variables of the call. A name the code did not fill stays empty. The names vars, result, now, metadata, and http belong to the sandbox itself: an output variable named like one of them never reaches the call variables, and the builder warns about it.
  • Timeout, s — 10 by default, 30 maximum.
Code function panel: the sandbox, output variables and the timeout Below the form there is a hint about the shared budget — it names both values, 10 s and 60 s, and reminds you that a function that does not fit is skipped, and on inbound calls keeps running in the background.

The variables a function produces

A custom function always writes a service set, even with an empty response mapping: Plus everything you listed under response variables. On a name clash your mapped variable wins over the service one. A code function writes <id>_success and <id>_error — its prefix is not the name but the internal card id (cf1, cf2) — plus the result variables you declared.

Ordering and parallelism

Cards run top to bottom in the order the system executes them. One row is one step. To make two functions run at the same time, drag one card onto the other’s row; while dragging, the hint reads “Runs with this row”. A row of two or more cards is outlined with a dashed border and labeled “in parallel · N”. To restore the queue, drag the card into the gap between rows (“Gets a row of its own”). Without a mouse the same is done by:
  • the When to run field in the function form — “After previous” or “Parallel with previous”;
  • keys on the focused card: Alt+↑ and Alt+↓ move the card one row up or down, Alt+→ puts it into the row above, Alt+← returns it to its own row.
What matters about rows:
  • Variable visibility is by row, not by arrow. A function in row k sees the variables of every earlier row, not just of the ones it was placed after. The results of a row are merged into the shared set once the whole row has finished, in card order left to right — which is why http_* ends up belonging to the last card of the row.
  • Two functions in one row cannot write the same thing. The same prefix (function name) or overlapping result-variable names within one row is an error: the outcome would depend on who finished first. Across rows it is allowed, and the later row wins.
  • A failure does not cancel what follows. If a function fails or times out, the one placed after it still starts and sees <name>_success = "false".
  • Cards do not move between the pre and post blocks: a function has no phase switch.
Under the block there is a line “Longest chain: N s of M s”. That is the budget check: what counts is not the sum of all timeouts but the longest chain of consecutive rows. Three requests of 4 seconds in one row are 4 seconds, not 12. If the chain is longer than the budget, a warning appears naming the functions in it. Pre-call block: a batch row "in parallel · 2" and the longest-chain line

Testing without calling anyone

One function. Select the card. A custom function has two buttons at the bottom of the panel: Test — a real call to your URL — and Test with mock — the same run with the mock mode forced, so nothing leaves the platform. A code function has a single button, Run Code. Pre-call functions also carry a Trial budget selector with two values — “Before the first reply (inbound, web)” and “Before dialing (outbound)” — so you can see that a slow request misses the first budget and fits the second. The result appears under the buttons and on the card: Success, Error, Timeout, or Skipped (budget exhausted), plus the time in milliseconds, the status code, and the list of variables written. If a pre-call function returned a dial decision, a line right there reads “Decision: dial / cancel the dial / defer the contact”, along with a warning when the value was not recognized. The whole block. Once a block holds more than one function, a Test the block button appears: it runs them the way a real call would. The answer shows “Whole block — N ms” and a per-row breakdown: “Row 1:”, “Row 2:”, and the status of each function. This is the fastest way to confirm that parallelism is set up the way you intended. The trial result lives until the page is reloaded and is never saved into the flow. Trial run: the budget selector, the buttons and the result card Whole-block run broken down by rows

Late delivery of a result

On inbound and web calls, a function that misses the budget before the first reply is not cut off. The agent starts speaking on budget while the function keeps running in the background with its own timeout, and the result flows into the ongoing conversation — the next flow node that reads variables will see it. The history holds two events for such a function: first “waiting for the result”, then the final status. An utterance already spoken is not replayed: if the agent greeted the caller without a name, it will not retrofit one. Boundaries:
  • outbound calls have no late delivery — the 60 seconds before dialing are enough;
  • in realtime mode late data reaches variables and tools, but not the system prompt that was already assembled;
  • the chat test in the editor stays synchronous.