> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hubtalk.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# The integration contract

> What the platform sends, what it expects back, how the mapping works, trial runs and configuration keys.

# 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 into `call_decision`, `call_decision_reason`, and `call_retry_after` (values and consequences are in [Dial decisions in a campaign](/v4/platform/call-functions/campaign-decision)).

A minimal handler that cancels the call:

```python theme={null}
from http.server import BaseHTTPRequestHandler, HTTPServer
import json

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        body = json.dumps({"decision": "cancel", "reason": "debt_paid"})
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.end_headers()
        self.wfile.write(body.encode())

HTTPServer(("0.0.0.0", 8099), Handler).serve_forever()
```

In the function's response mapping: `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 endpoint `POST /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}`.

## Platform configuration keys

| Key                                               | Default | What it sets                                                                       |
| ------------------------------------------------- | ------- | ---------------------------------------------------------------------------------- |
| `call_functions.pre_call_budget_seconds`          | 10      | budget before the first reply (inbound, web)                                       |
| `call_functions.pre_call_budget_seconds_outbound` | 60      | budget before dialing (outbound)                                                   |
| `call_functions.late_delivery`                    | on      | background completion on inbound and web calls                                     |
| `call_functions.post_call_analysis_wait_seconds`  | 60      | how long post-call functions wait for the conversation analysis; `0` — do not wait |
| `call_functions.decision_pause_window`            | 20      | window of recent decisions for the mass-cancellation guard; `0` disables it        |
| `call_functions.decision_pause_cancel_share`      | 1.0     | share of cancellations in the window that pauses the campaign                      |
