The integration contract
This section is for whoever builds the HTTP handler on the client side.The request from the platform
The URL, headers, parameters, and body are assembled from the form fields with{{variable}} substitution. Available are the call variables (from_number, to_number, direction, and everything configured in the agent and the campaign), the results of functions that already ran, and — in the post-call block only — call and analysis.
Secrets are never typed into the fields: reference them as {{env.VARIABLE_NAME}} and the value is substituted from the environment at execution time. In the call history the request is stored masked.
The request timeout is a field of the function, 10 s by default with a 30 s ceiling. The effective timeout is further clipped by what is left of the block budget: the remainder is taken once per row and becomes the ceiling for every function in it.
The response and the mapping
From the response the platform writes the service set (<name>_status, _success, _error, _result, plus the global http_*) and whatever the mapping lists.
The mapping is a dictionary of “variable name → JSONPath in the response”; the variable name is the key, not the value. The form shows it that way too: the name on the left, the path on the right (balance ← $.data.balance). The names are chosen by whoever configures the agent in the cabinet, not by you — agree in advance on which fields you return.
The dial decision
To stop a campaign dial, return the fields that the mapping puts intocall_decision, call_decision_reason, and call_retry_after (values and consequences are in Dial decisions in a campaign).
A minimal handler that cancels the call:
call_decision ← $.decision, call_decision_reason ← $.reason. Then run a campaign with a single contact: the phone stays silent, the contact is closed, and the outcome breakdown shows “Cancelled by function”.
The trial run
The Test and Test the block buttons hit the cabinet endpointPOST /api/flows/{name}/call-functions/run. Body: {function, phase: "pre"|"post", variables?, mock?, budget?}, or {functions: [...]} to run the whole block. For phase="pre" the budget field picks the budget: inbound (10 s, the default) or outbound (60 s). For phase="post" the body accepts analysis — a stub of the analytics result.
The answer carries status, elapsed_ms, the names of the variables written, the masked request, status_code, the row number and dependencies, the variable diff, and when: {expression, result} — how the condition evaluated. For phase="pre" it also carries decision — how your decision was read: {action, reason, retry_after, retry_at, warning, declared}.