Skip to main content
An agent’s Workflow page is where you run tools outside the conversation: before the agent speaks, so it starts already knowing who it’s talking to, and after the session ends, so your systems are updated without anyone touching them. Voice agents call these pre-call and post-call functions; chat agents get the same two slots, named pre-chat and post-chat functions. Each slot is a dependency graph rather than a flat list. Functions with no dependency all start at once, and a function that depends on another waits for its output.
The agent's Workflow page, with Workflow highlighted in the left rail next to Agent and Simulation. A flow runs from Dial in/out through pre-call functions, Call started, the Agent node with a knowledge base attached and an Add webhook slot below, Call ended, Post-call data extraction, and Post-call functions. A tool menu for a connection named salesforce retell is open over the pre-call functions node, listing Salesforce tools with Search Contact selected and its description shown. Blue rings mark the Workflow nav item and the Add buttons on the pre-call and post-call function nodes.

The agent's Workflow page: adding a Salesforce tool as a pre-call function, with post-call functions at the end of the flow.

Everything below works the same on both channels except where noted.

When to use it

Reach for a pre-call or pre-chat function when the agent needs a fact it can’t ask for, or shouldn’t have to:
  • Greet an identified person by name. Look the caller’s number, or the chat’s contact details, up in your CRM before the first word, instead of asking “can I take your name?”
  • Load account state. Pull their open tickets, last order, or policy status so the agent answers the first question without a lookup mid-conversation.
  • Decide how to open. Fetch a flag (overdue balance, VIP tier, do-not-call) and branch the greeting on it.
Reach for a post-call or post-chat function when the work happens after the conversation, not in it:
  • Log the outcome. Write the summary and disposition back to the CRM timeline.
  • Create the follow-up. Open a task, a ticket, or a callback when the conversation agreed to one.
  • Notify. Text the caller a confirmation, or push the result to your own endpoint.
Keep a tool in the conversation itself when the agent needs the result while it’s talking, like checking calendar availability for a time the caller just named. See integration tools for prompt agents and integration tools in conversation flow for that surface.
Workflow functions are configured on the dashboard only (as of August 2026). They are not part of the public API, so you can’t set them through create-agent or update-agent.

What you can run

Both slots take the same four kinds of function, from the same + menu: Saving an SMS function in the pre-call slot, or on a chat agent, is rejected: the session is over before an SMS can make sense, and a chat agent has no number to send from.

Add functions on the Workflow page

1

Open the Workflow page

In the agent editor, select Workflow in the left rail. The canvas lays the whole session out end to end: Dial in/out, Pre-call functions, Call started, the agent, Call ended, Post-call data extraction, and Post-call functions. A chat agent shows the same flow with Any trigger, Pre-chat functions, Chat started, Chat ended, Post-chat data extraction, and Post-chat functions.
2

Add the first function

Click + Add on the opening or closing functions node and pick SMS, Code, Custom function, or a tool from one of your connected providers. Add integration connects a new provider without leaving the page.
3

Configure it

Give it a Name. This is how other functions refer to it, so it must be unique within that slot. Then set its inputs and outputs in the Function fields card. Every input is either a literal you set now, which may be a {{variable}} reference, or a description the LLM fills in; every output you keep is what the agent sees and, optionally, a dynamic variable. On a pre-call function the inputs start on a literal rather than a description, since there’s no conversation to infer from yet, but you can switch any of them. Configure a tool’s inputs and outputs covers the modes and the test run in full.
4

Chain the next one

Hover a function’s row and click its +, then choose Add sequential function to run the new one after it, or Add parallel function to run it alongside it. Use the node’s own + Add instead to add a function that starts immediately, independent of the rest.
The Pre-call functions node on the agent's Workflow page, reached from Dial in/out. It holds a function named search_salesforce_c... with edit, delete, and + buttons, and below it an indented child row named get_salesforce_u... that runs after it, plus an + Add button. The first row's + button is ringed in blue and its dropdown is open, showing two items: Add sequential function, with a corner-down-right icon, and Add parallel function, with a stacked-lines icon.

A function's row menu, offering a sequential or a parallel function.

Run functions in parallel or in sequence

Retell runs the graph in batches: every function whose dependencies have finished runs concurrently, then the next batch, until the graph is done. Total time is the longest chain, not the sum of every function.
  • Parallel. A function with no dependency starts as soon as the phase begins, so two independent lookups cost as long as the slower one. Add parallel function starts the new function at the same point as the row you clicked, and anything that was waiting on that row now waits for both.
  • Sequential. Add sequential function makes the new function depend on every function in the row’s card, so it starts only once they’ve all succeeded. That’s what lets it use their output.
A chain can be at most 4 functions deep, and each slot holds at most 15 functions (as of August 2026). Saving is rejected if a function depends on itself, on a name that doesn’t exist, or on a cycle.

Pass a response from one function to the next

Chaining works through dynamic variables. On the function’s Output tab, give a response field a name in the Dynamic variable column; downstream functions then reference it as {{that_name}} in any input, and the agent’s prompt can use it too.
The configuration dialog for a Salesforce Search Contact function named search_salesforce_contact, whose description tells the agent to find the contact by phone number at the start of the conversation using the caller's number. In the Function fields card, on the Input tab beside an Output tab, the required phone_number field's value row is ringed in blue, showing a curly-brace value-mode pill and an input where two open braces have been typed. Below it the dynamic variable picker is open with Agent, System, and Contacts tabs, System selected, listing session_duration_ms, current_node, previous_node, direction, user_number highlighted, agent_number, call_id, and call_type. A Run a test button sits at the bottom of the card, with Cancel and Save below.

Pinning a function input to a dynamic variable: typing {{ in a value field opens the picker.

Variables become available as the graph runs, so a function only ever sees output from functions that finished before it. The dashboard enforces the same rule as you type: when you pick a variable, it offers only the outputs of the functions this one waits on, not the ones running beside it or after it. Two more things to know about how the data reaches the agent:
  • The result itself lands in the agent’s context, not just the variables you named. Every tool call is woven into the transcript the LLM reads, with its arguments and its response; what a workflow function changes is only where — a pre-conversation call is prepended, so the agent has it from its first turn. Responses are capped at 30,000 characters for an integration tool and 15,000 for a custom function or code tool (as of August 2026). Use Select fields on the Output tab to send only the fields that matter, rather than letting a large provider payload crowd the prompt.
  • Dynamic variables are extracted from the raw response, before that field selection. Narrowing what the agent sees never breaks a variable you mapped.
If a {{variable}} in an input has no value — the function it came from didn’t run, or returned nothing for that field — the input is left out. If that input was required, the function is skipped rather than sent with a gap in it.

Variables a post-conversation function can use

Post-call and post-chat functions run after post-call analysis, so they can read its results on top of everything available during the session: These are available to post-conversation functions only; nothing earlier can read them, because the analysis hasn’t run yet.

Gate a post-conversation function on a condition

Every post-call and post-chat function can carry an only when gate, so you don’t write a task on a call that never connected. In the function’s config, set the match to All or Any, then add rows:
  • Variables condition — compare a {{variable}} against a value with =, , >, <, , , contains, not contains, exists, or not exists. Picking exists or not exists drops the value field, since there’s nothing to compare against.
  • Disconnection reason — pick one or more reasons the session must have ended with.
  • Call status — require a specific final status.
The configuration dialog for a HubSpot Log Call Activity post-call function named log_hubspot_call. Its conditions card sits below the Description: an All selector heads the line All of the following conditions match, above one row testing the variable call_summary with the operator Exists, whose value field is greyed out because the operator takes no comparison. Below that row, an Add button ringed in blue has its menu open, offering the three kinds of row: Variables condition, Disconnection reason, and Call status. The Function fields card below shows a required contact_id input, with Run a test and Add input buttons, and Cancel and a disabled Save close the dialog.

A post-call function gated on a condition: log the call only when the analysis produced a summary.

A function with no rows always runs. A gated-off function counts as not having run, so anything depending on it is skipped too. That’s usually what you want: gate the lookup, and the write that follows it drops with it.

Examples

Greet an identified caller and load their tickets

A support line answers with the caller’s name and their open ticket already in context. Pre-call functions:
  1. find_user — Zendesk Search User, with phone_number pinned to {{user_number}}. On the Output tab, map the user’s id to zendesk_user_id and name to caller_name.
  2. list_tickets — Zendesk List User Requested Tickets, added as a sequential function under find_user, with user_id set to {{zendesk_user_id}}.
The prompt then opens with Greet {{caller_name}} by name. and the agent already has the ticket list in context, so “what’s the status of my ticket?” is answered on the first turn instead of the third. The same pair works on a chat agent as pre-chat functions. Swap {{user_number}} for whichever dynamic variable your chat is started with, such as an email address passed in when the widget opens.

Log the session and open a follow-up task

A sales agent writes every conversation back to HubSpot, and creates a task only when there’s something to follow up on. Pre-call function:
  • search_contact — HubSpot Search Contact on {{user_number}}, mapping the record ID to hubspot_contact_id.
Post-call functions:
  1. log_hubspot_call — HubSpot Log Call Activity on {{hubspot_contact_id}}, with the body set to {{call_summary}}. Gated on All: {{call_summary}} exists, so a call that never got far enough to analyze writes nothing.
  2. create_followup — HubSpot Create Task, added as a parallel function beside log_hubspot_call so both writes go out at once. Gated on All: {{call_status}} = ended and {{followup_needed}} = true, where followup_needed is a custom post-call analysis field. Its subject is a description the LLM fills in from the transcript.
The session is logged every time it completes; the task appears only on the conversations that earned one.

Confirm a booking by text

A clinic texts the caller after the call, but only when a booking actually happened. Post-call functions:
  1. get_booking — Cal.com Get Booking, with the UID set to {{booking_uid}}, a variable an in-conversation Book Appointment tool mapped earlier in the call. Gated on All: {{booking_uid}} exists.
  2. send_confirmation — an SMS function, sequential under get_booking, whose content the LLM writes from the transcript and the booking response.
No booking means {{booking_uid}} never gets set, the gate fails, and both functions drop, so no text goes out. On a chat agent, replace the SMS with a Custom function that posts to your own notification endpoint.

Timing and limits

A call waits for its pre-call graph before the conversation begins, up to a budget that depends on the call:
  • Inbound and web calls wait 1 minute.
  • Outbound calls wait 5 minutes, before Retell dials. The extra room is there because nothing is ringing yet, so nobody is sitting on the line while a CRM lookup runs.
Exceeding the budget doesn’t cancel anything. The remaining functions keep running in the background, and what they return still reaches the agent: each call and its response are added to the transcript the LLM reads, and the variables they map stay available to everything downstream, post-call functions included. What a late arrival misses is the initial prompt and the agent’s opening message, which are already built by the time it lands. So the budget is the deadline for anything the agent’s first words depend on, not for the data itself. Post-call functions are capped at 5 minutes total. That one is a hard stop: whatever hasn’t finished is cancelled so teardown can complete. Figures are as of August 2026.

When a function fails

A failure stays inside the function it happened in. A function that errors, times out, is missing a required input, or is gated off is marked unsuccessful, and:
  • Everything waiting on it is skipped, and everything waiting on those. A lookup that fails takes down the write that needed its ID, rather than sending a request with a blank field.
  • Independent branches carry on. A failed CRM lookup doesn’t stop the calendar check running beside it.
  • The session continues. A pre-conversation failure still connects the call or opens the chat, just without those variables, so write the prompt to read correctly when a lookup came back empty. A post-conversation failure still completes teardown.

See what ran

Open the call in call and chat history and read the transcript. Pre-call function calls sit under a Before call divider at the top, the conversation follows Call started, and post-call ones sit under Call ended at the bottom. Each entry shows the arguments sent and the response returned, so you can tell a bad input apart from a provider error. During a live call, the same entries collapse under Before conversation in the live transcript. Chat sessions run their pre-chat and post-chat functions the same way, but the dashboard’s chat transcript doesn’t show those entries yet (as of August 2026).

FAQ

No. Every function in the slot runs whenever its dependencies are met. Unlike a tool in the conversation, there is no LLM decision about whether to call it. The LLM is only involved in filling inputs you described rather than pinned, and post-conversation functions add an only when gate you control.
From the agent’s prompt and the dynamic variables available at that point, including outputs from earlier functions in the graph. There’s no conversation yet, so an input that can only come from the user belongs on an in-conversation tool instead. Post-conversation functions also see the full transcript.
Yes, by as long as the graph takes, up to 1 minute on an inbound or web call, and a chat waits for its pre-chat graph with no budget at all. Outbound calls absorb their 5-minute budget before dialing, so nobody hears the delay. Keep the chain shallow: two functions running in parallel cost one function’s latency, while two chained cost both. If a lookup is slow and not needed for the opening line, run it as an in-conversation tool instead.
Yes. Pre-conversation outputs stay in the session’s dynamic variables for the whole session, so a post-conversation function can reference them directly. That’s how the HubSpot example above reuses {{hubspot_contact_id}} without looking the contact up twice.
Saving is rejected. Names are the handle other functions depend on, so they have to be unique within a slot. The same name may appear once in the pre-conversation slot and once in the post-conversation slot.