Approve a tool action and finish the conversation
Section: DOC-MA-delegation-approvals-using-tools#use-and-approve-tools-in-conversations.
Build an assistant that can search for information but waits for a person before sending an email or performing another reviewed action. Use the platform-run approval recipe first: configure the gate, submit a request, show the pending action, send the decision and follow the same run to its result.
If your application executes the action itself, use the separate client-tool recipe. Client tools do not pass through the platform approval queue; your application validates arguments, checks permission and obtains the required confirmation before executing them.
Before you start: choose the tool and its review policy
Section: DOC-MA-delegation-approvals-using-tools#tool-execution-policy.
You need an authenticated conversation, a selected server whose tools have been discovered, and the exact names of the tools your assistant can call. Use tool discovery and inspect the server/tool names before writing approval patterns. The example below uses illustrative search and email patterns; adapt them to your discovered names.
For this recipe, allow ordinary lookup calls and require a decision before the chosen sending action. Keep the same conversation and accepted run when listing and deciding on approvals.
Step 1: Add the approval gate to the send
Section: DOC-MA-delegation-approvals-using-tools#worked-example.
Add this policy to the user’s normal send request after adapting send_email to the discovered sending tool. This is the policy portion, not a complete message. It allows a 30-second approval wait and a finite overall run budget.
{
"conversationKey": "support-chat-001",
"overrideGenerationConfig": {
"toolPolicy": {
"requireApprovalToolNamePatterns": ["send_email"],
"approvalMode": "APPROVAL_MODE_MIXED",
"approvalTimeoutMs": 30000,
"timeouts": { "overallMs": 600000 }
}
}
}
Request excerpt: Send a message to a conversation · Request fields.
Send the user’s request once and retain its run ID. If the model does not request the gated action, no approval appears; read the actual reply rather than manufacturing a pending decision. Use the send reference for the complete toolPolicy schema.
Step 2: Show the action awaiting a decision
Section: DOC-MA-delegation-approvals-using-tools#checking-pending-approvals.
Read the pending calls for the conversation and present the action that needs a decision. Keep its tool-call ID so the decision applies to that call.
curl -X POST https://api.travila.ai/api/v1/llm/list-pending-approvals \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{
"conversationKey": "support-chat-001"
}'
Reference: List pending tool approvals · Request fields.
Present the pending action for review before asking for a decision. Keep its toolCallId tied to that action; do not reuse a decision for an unrelated later call. An empty pending list means there is no current approval to submit, not that the run completed.
Step 3: Submit the decision and inspect the outcome
Section: DOC-MA-delegation-approvals-using-tools#submitting-approvals.
After the user decides, submit the decision for the pending toolCallId. The example approves the call. Continue following the run afterward: an approval decision is not confirmation that the tool or the full task succeeded.
curl -X POST https://api.travila.ai/api/v1/llm/submit-tool-approvals \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{
"conversationKey": "support-chat-001",
"approvals": [
{
"toolCallId": "call_abc123",
"approved": true
}
]
}'
Reference: Approve or reject pending tool calls · Request fields.
Continue reading the same run through generation outcomes. Approval authorizes execution of the held call; it does not confirm that execution succeeded. Show a rejected, failed, timed-out or still-unknown tool result accurately.
Finished result: the person reviewed the pending action, the decision referred to that call ID, and the conversation shows its actual result and correlated run outcome. If the decision response is lost, read the pending/call state before deciding again; do not resend the original user message to recover.
Variant: decide which calls wait together
Section: DOC-MA-delegation-approvals-using-tools#approval-patterns.
Keep APPROVAL_MODE_MIXED when independent auto-approved lookups may proceed while the sending action waits. Choose APPROVAL_MODE_BLOCK_ALL when the entire batch must wait for every gated decision.
Current limit: tool-policy retries are not applied
Section: DOC-MA-delegation-approvals-using-tools#retries.
Tool-policy retry settings are accepted and stored, but current execution does not apply their patterns, error filters or backoff. Setting them does not cause a failed lookup to retry.
Inspect the failed call and show its actual outcome. Initiate another lookup only when it can safely be repeated. Reconcile an uncertain external write before any new attempt; a stored retry policy does not establish that repetition is safe.
Variant: bound the assistant’s supporting lookups
Section: DOC-MA-delegation-approvals-using-tools#parallel-calls-and-budgets.
Set a finite lookup budget for the task. Allow parallel calls only when those lookups are independent.
Inspect whether the reply contains the needed evidence. Show partial work as partial; reaching a budget limit does not establish that every requested lookup completed.
Recover when the run or review deadline expires
Section: DOC-MA-delegation-approvals-using-tools#run-time-budget.
Choose a finite duration for the task and show a timeout or unfinished result honestly. Inspect any external action separately before retrying it.
Approval and client-tool deadlines bound those individual waits; they are separate from the overall run budget and from a provider spending limit.
Current limit: stop-on-failure rules are not enforced
Section: DOC-MA-delegation-approvals-using-tools#stop-on-failure-rules.
Configured stop-on-failure rules are stored but do not currently stop a run after a tool error. Do not depend on this setting to prevent later model or tool steps.
If a later application action depends on the failed action succeeding, check the original outcome in your application before allowing that action. Inspect the tool and run outcomes separately; neither a failure nor a stopped run undoes an external effect that already occurred.
Separate recipe: let the assistant open a page in your app
Section: DOC-MA-delegation-approvals-using-tools#client-side-tools.
Use this flow when the user asks to open their profile screen. Declare and implement the navigate_to client tool with the tool-building recipe. It takes a screen argument; /profile must be an allowed route for the signed-in user. Replace support-chat-001 in every request below with the thread ID from that setup. Your client owns navigation and reports whether it actually succeeded.
Use the native request/response loop below to receive a tool call and return its result. A request can finish waiting before the run ends, so inspect status and use recovery when needed. These are Travila's native fields, not an interchangeable OpenAI tool-call payload.
Set a finite answer window for application-run tools
Section: DOC-MA-delegation-approvals-using-tools#client-tools.
Choose an answer window long enough for the application to check and perform the requested action. Keep the returned deadline with the pending call.
Use that returned deadline when recovering a pending call. After expiry, inspect the terminal tool outcome instead of submitting a late success; see disconnect recovery.
Follow the call from request to result
Section: DOC-MA-delegation-approvals-using-tools#the-loop.
Send the message, then keep submitting for as long as the platform keeps asking:
- Call
send-message-sync. Ifstatuscomes backAGENT_STATUS_AWAITING_CLIENT_TOOLS, the run is paused on you andpendingClientToolsholds the calls. - Validate each call's arguments, check the user's permissions and obtain any confirmation required by your application before executing it. Respect
clientToolDeadlineAt— after expiry the timeout policy ends the wait and a late result is rejected. - Call
submit-client-tool-results-syncwith the results and theclientToolCursoryou were given. - The response can contain another
AGENT_STATUS_AWAITING_CLIENT_TOOLSbatch, a terminal result, or an active/unknown outcome when the wait ends first. Repeat for another batch; otherwise reconcile the same run within a bounded wait. Returned messages alone do not prove completion.
A run can ask more than once. Each round returns a new cursor; always echo back the most recent one.
Step 1: Receive the complete navigation call
Section: DOC-MA-delegation-approvals-using-tools#turn-1--the-model-asks.
curl -X POST https://api.travila.ai/api/v1/llm/send-message-sync \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{
"conversationKey": "support-chat-001",
"userMessage": {
"role": "ROLE_USER",
"content": [
{
"type": "CONTENT_PART_TYPE_TEXT",
"content": "Navigate to my profile."
}
]
}
}'
Reference: Send a message and wait for the result · Request fields.
{
"runId": "cf9f08e7-4486-41e1-bac1-b9428d1aeb85",
"status": "AGENT_STATUS_AWAITING_CLIENT_TOOLS",
"clientToolCursor": 1,
"pendingClientTools": [
{
"id": "call_306135",
"name": "navigate_to",
"argumentsJson": {
"screen": "/profile"
},
"isClientTool": true,
"clientToolDeadlineAt": "2026-09-02T06:43:24.304Z"
}
]
}
Reference: Send a message and wait for the result · Response fields.
Step 2: Validate, open and return the real result
Section: DOC-MA-delegation-approvals-using-tools#turn-2--you-answer.
Validate the screen argument and check that the current user may open /profile, obtain any confirmation your app requires, then navigate once. Submit the actual result for the returned call ID and cursor.
Note the result field names: toolCallId, toolName and resultJson, not the id and name the pending call uses.
curl -X POST https://api.travila.ai/api/v1/llm/submit-client-tool-results-sync \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{
"conversationKey": "support-chat-001",
"clientToolCursor": 1,
"results": [
{
"toolCallId": "call_306135",
"toolName": "navigate_to",
"resultJson": {
"navigated_to": "/profile"
}
}
]
}'
Reference: Submit client-side tool results and wait for the next segment · Request fields.
{
"runId": "cf9f08e7-4486-41e1-bac1-b9428d1aeb85",
"status": "AGENT_STATUS_COMPLETED",
"messages": [
{
"role": "ROLE_ASSISTANT",
"content": [
{
"type": "CONTENT_PART_TYPE_TEXT",
"content": "I've opened your profile."
}
],
"finishReason": "stop"
}
]
}
Reference: Submit client-side tool results and wait for the next segment · Response fields.
Finished result: the permitted profile screen was opened, the result was submitted for that exact call, and the same run reached a recognized outcome. Continue the loop if another batch is returned; a first tool result is not necessarily the end of the conversation.
Recover the original call after a disconnect
Section: DOC-MA-delegation-approvals-using-tools#if-the-connection-drops.
The run does not care which connection answers it. Pull the outstanding calls with list-pending-client-tools, then submit as normal:
curl -X POST https://api.travila.ai/api/v1/llm/list-pending-client-tools \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{
"conversationKey": "support-chat-001"
}'
Reference: List client-side tool calls awaiting a result · Request fields.
When the missing call ID is known, read that call’s current result before executing or submitting anything again:
curl -X POST https://api.travila.ai/api/v1/llm/get-tool-call \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{
"conversationKey": "support-chat-001",
"toolCallId": "call_306135"
}'
Reference: Get a single tool call by ID · Request fields.
Do not resend the original user message to recover. It can create duplicate work or invoke the conversation’s interrupt policy, which may cancel, queue, reject or ignore the new request.
Variant: run the same client-tool flow from a worker
Section: DOC-MA-delegation-approvals-using-tools#client-side-tools-async.
The synchronous loop waits for a result within a bounded request. For a backend worker that should release its connection sooner, use asynchronous send and submission, then discover pending calls separately. Both styles still require outcome checks and recovery.
- Send with
send-message. A newly admitted run returns arunIdwithout waiting for that run’s model response. Validation and context preparation still take time; queued or ignored requests can return a different outcome. - Learn that a call is waiting, by either route below.
- Read the calls with
list-pending-client-tools. This gives the current pending calls and answer deadlines. SyncpendingClientToolsandget-tool-callcan also report the deadline. - Submit with
submit-client-tool-results. It records accepted results and returns therunIdwithout waiting for the next generation result.
You can mix the two styles freely. A -sync submit works just as well after an async discovery, if you want the next turn back on that request.
Wake the worker for a pending call
Section: DOC-MA-delegation-approvals-using-tools#knowing-a-call-is-waiting.
Wake the worker from the supported tool-start event, or poll pending calls within the configured answer window. In either case, read the complete pending call and its deadline before acting.
Choose whether the worker keeps the request open
Section: DOC-MA-delegation-approvals-using-tools#which-style-to-use.
| Sync | Fire-and-forget | |
|---|---|---|
| Send | send-message-sync | send-message |
| Discovering a call | On the response you are already holding | llm.tool_call_started webhook, or poll list-pending-client-tools |
| Submit | submit-client-tool-results-sync | submit-client-tool-results |
| Time to discovery | Bounded by the request itself | Whatever your webhook or poll interval costs |
| Holds a connection | While waiting, within the request deadline | Only until acceptance |
The sync loop is a convenient starting point when your client can wait. Keep the same polling/recovery path for a dropped connection or a wait that ends before the run completes.
Keep partial replies and terminal outcomes distinct
Section: DOC-MA-delegation-approvals-using-tools#two-rules-for-clients.
Keep unfamiliar or missing outcomes unresolved and reconcile the original run. Display provisional text as provisional; only a correlated terminal outcome finishes the customer’s task.
See the Agent tools guide for more on tool calling.
Document ID: DOC-MA-delegation-approvals-using-tools. Section identities and revisions.