TL;DR
- Flow functions let an agent call your HTTP API during a conversation — no middleware, no polling, no webhooks-after-the-fact.
- You define one function per endpoint. Method, URL, parameters in path/query/body — each with its own type and description.
- Each parameter has a source: the LLM extracts it from the conversation, the server injects it from the active call, or it's a fixed value. You choose.
- Functions run on Talkif's servers. Your credentials, your response, your transition logic — the agent just sees the result.
What you can build
The common case: your agent needs to do something in your system during the call. Before flow functions, this meant hand-rolling middleware that listened for transcript keywords or reacting after the call hung up. Now it's a first-class feature.
A few examples of what fits:
- Look something up. "What's the status of order 12345?" → GET
/orders/12345→ agent reads back the status in natural language. - Book something. "I'd like a haircut Friday at 3" → POST
/bookingswith the caller, service, and time → agent confirms the slot or offers alternatives. - Transfer to a tool. "Pay with the card on file" → POST
/charges→ agent confirms the charge or gracefully handles a decline. - Enrich the conversation. Agent calls
/availabilitybefore the caller is even asked about timing, so it can proactively offer real slots instead of guessing.
None of these need new infrastructure on your side. If you have an HTTP endpoint that does the thing, you're most of the way there.
The shape of a flow function
A flow function describes a structured HTTP request. Three independent parameter buckets, each a JSON Schema object: pathParams for URL placeholders, queryParams for the query string, body for the JSON body.
jsonc{
"name": "check_order_status",
"description": "Look up the current status of a customer order by order number",
"request": {
"method": "GET",
"url": "https://api.example.com/orders/{orderId}",
"pathParams": { "type": "object", "properties": { "orderId": { "type": "string" } }, "required": ["orderId"] },
"queryParams": { "type": "object", "properties": {} },
"body": { "type": "object", "properties": {} }
}
}Give the function a clear name and description — that's what the LLM reads when deciding whether to invoke it. Attach it to an agent, and the agent can call it whenever the conversation calls for it.
Body parameters support nested objects and arrays (up to depth 5). Path and query parameters stay as primitives — they have to flatten into URL strings.
Where parameter values come from
Each parameter has a binding source that tells Talkif who supplies its value. You pick one of three:
| Source | The value comes from | Use for |
|---|---|---|
llm (default) |
The conversation. The model extracts it from what the caller said. | Things the caller tells you: product name, date, quantity, description. |
call_context |
The active call's state, injected by the server. | Things the system already knows: the caller's contact record, the logged-in user, the current agent's tenant. |
static |
A fixed value you set when defining the function. | Constants you'd otherwise bake into a header or a hidden field: API version, source tag, environment flag. |
Parameters bound to call_context or static are filled in on the server side. The LLM doesn't see them in its tool schema, doesn't have to extract them, can't get them wrong.
The call_context source also decides what happens when the value isn't available — say the caller dialled in anonymously and you have no contact record for them. You pick one of two behaviours:
| If the value is… | What happens when it's missing | Example |
|---|---|---|
| Mandatory | The function disappears from the agent's tool list for that call. The model can't even attempt to invoke it — the capability simply isn't there. | An authenticated user ID for a refund function. No caller identity, no refunds. |
| Optional | The parameter falls back to the LLM and gets extracted from the conversation, like any ordinary LLM-filled parameter. | A preferred language. Use the stored preference if you have it; otherwise let the agent ask. |
The upshot: when a capability depends on something the system must already know, you don't need guardrail prompts to stop the model from guessing. The function is just unavailable, and the agent naturally works with what it has.
One function, many phrasings
You don't enumerate trigger phrases. The model reads the function's name and description and matches intent, not keywords. A single check_order_status definition fires for all of these — and every variation in between:
- "What's happening with order 8842?"
- "Can you check on eight-eight-four-two?"
- "My package, order 8842 — where's it at?"
- "I placed an order yesterday. Number's 8-8-4-2."
The model normalises garbled input before invoking — eight eight four two becomes 8842, extraneous words are dropped, the orderId it sends is clean. If the caller never states a number at all, the model asks for one, because you marked the parameter required. You don't write that logic; the schema is the logic.
The two levers that actually move reliability:
- Function description — the hint the model reads to decide when to call. Describe what the function does in the caller's language, not your backend's.
- Parameter description — the hint the model reads to decide what to fill in. Be explicit about format (
"digits only, no prefixes","ISO 8601 date","E.164 phone number").
jsonc{
"name": "check_order_status",
"description": "Look up shipping status and ETA for an existing customer order.",
"request": {
"pathParams": {
"type": "object",
"properties": {
"orderId": {
"type": "string",
"description": "The order number the caller referenced — digits only, no prefixes."
}
},
"required": ["orderId"]
}
}
}Descriptions are the prompt engineering. The clearer they are, the more reliably the model picks the right function with the right arguments — the same dynamic every tool-calling LLM shares. If you've shaped an OpenAI function schema before, your intuition transfers directly.
Handling failure gracefully
Your API will occasionally return an error. A 500, a timeout, a 404 for an order that doesn't exist. Flow functions handle this with two mechanisms.
The agent keeps the conversation going. The error is returned to the model as the function result, so the agent can say "I couldn't find that order — can you double-check the number?" instead of hanging silently.
You can transition on failure. If the function has a transitionTo target, the agent moves to that node regardless of whether the HTTP call succeeded. Build an "order not found" node with its own task prompt, and the caller gets a coherent recovery experience rather than a dead end.
Choosing the right binding
A quick mental model:
| If the value is… | Use |
|---|---|
| Something the caller tells you in words | llm |
| Something your system already knows about the caller | call_context |
| The same on every invocation | static |
| Nice to have from context, but the caller can provide it too | call_context, with a fallback to the LLM when the value is missing |
The default (llm) is almost always right for the obvious parameters. You reach for call_context when letting the model guess would feel wrong — when the answer lives in your database, not the conversation.
FAQ
Is the backend call synchronous? Yes. The agent waits for the response (up to the configured timeout — 100–30000ms, default 5 seconds), then continues. Callers experience a natural pause similar to "let me pull that up."
Can I use my own authentication? Yes. Webhook headers are set per function and encrypted at rest with AES-256-GCM. Bearer tokens, API keys, signed requests — whatever your endpoint expects.
Can multiple agents share the same function? Yes. Define once, reference by ID from any agent in any flow.
What if I need to transform the response before the LLM sees it? Today the raw response is returned to the model. If your API returns a field the agent shouldn't read out, shape the payload on your side.
Can I call a function that doesn't exist yet on my side? The flow will publish and deploy, but invocations will fail (timeout or 404). Build the endpoint first, then publish the flow that references it.
Where to go from here
The full reference — request shape, binding rules, null-handling behaviour, validation — lives at docs.talkif.ai/flow-builder/flow-functions. If you're building something specific, start there and the request shape will look familiar within a minute.