Let a customer navigate your app through chat
Section ID: Guide/j8fgpgk84t#build-an-agent-that-uses-your-tools
Let a user ask the assistant to open their profile in your app. Define a navigation tool, receive the requested action, check and execute it in your app, then return the actual result so the assistant can finish its reply. The optional connected-app step extends the workflow to a supported third-party account such as Google Calendar.
Client-side tools do not go through the approval queue. Approval is a separate mechanism for tools the platform runs but a human must authorise — see approval patterns. Your application must check the user's permissions, validate the arguments and obtain any required confirmation before running its own tool; the run waits for your result.
Start with one allowed route and one tool. The completed recipe opens the customer's profile, returns the actual navigation result and displays the assistant's reply for that run. Connected SaaS tools are a separate extension after the client-tool loop works.
Prerequisites
Section ID: Guide/j8fgpgk84t#prerequisites
-
A secret key (
sk_…) for your tenant, issued through your enabled management surface or account administrator. Backend calls use the secret key plusX-On-Behalf-Ofto act on behalf of a specific user. See Authentication & API Keys. -
A conversation thread. Create one with
create-threadif you do not have one. See Build an AI chat assistant for the full setup.
Step 1: Describe the navigation your app can perform
Section ID: Guide/j8fgpgk84t#step-1-define-a-client-side-tool
Client-side tools are actions that run in your app or backend, not on the platform. Declare them in clientTools when creating the thread — or per turn via overrideGenerationConfig:
Request example: Create a new conversation thread. Example identifiers and values are illustrative; substitute authorized values.
Example request headers: X-API-Key: sk_your_key_here; X-On-Behalf-Of: user_123; Content-Type: application/json. Replace the credential and user placeholders with your own authorized values.
Response:
Response example: Create a new conversation thread. Example identifiers and values are illustrative; substitute authorized values.
Save the threadId — you'll pass it as conversationKey in every subsequent request.
Implement the corresponding action in your application before sending the test question. For this recipe, /profile must be an allowed route for the signed-in user. Declaring the tool schema does not implement navigation or validate arguments.
Step 2: Ask to open the profile and retain the pending call
Section ID: Guide/j8fgpgk84t#step-2-send-a-message-and-receive-the-call
Send a message that makes the model want to navigate. The model reads your tool description and decides whether to call it.
Use send-message-sync for a bounded wait that can return the pending call directly. If the wait ends first or the connection drops, use the recovery flow below; do not resend the message.
Request example: Send a message and wait for the result. Example identifiers and values are illustrative; substitute authorized values.
Example request headers: X-API-Key: sk_your_key_here; X-On-Behalf-Of: user_123; Content-Type: application/json. Replace the credential and user placeholders with your own authorized values.
Response:
Response example: Send a message and wait for the result. Example identifiers and values are illustrative; substitute authorized values.
AGENT_STATUS_AWAITING_CLIENT_TOOLS means the run is paused on you. Take the id and name off the pending call, and keep clientToolCursor — you need all three in the next step.
Step 3: Navigate once and report what happened
Section ID: Guide/j8fgpgk84t#step-3-return-the-result-and-get-the-reply
For navigate_to, check that screen names an allowed route and that the current user may open it. Run the action, then post its actual result with submit-client-tool-results-sync. The response may contain the assistant reply, another tool batch or a still-active outcome; inspect status before treating the turn as finished.
Request example: Submit client-side tool results and wait for the next segment. Example identifiers and values are illustrative; substitute authorized values.
Example request headers: X-API-Key: sk_your_key_here; X-On-Behalf-Of: user_123; Content-Type: application/json. Replace the credential and user placeholders with your own authorized values.
Response:
Response example: Submit client-side tool results and wait for the next segment. Example identifiers and values are illustrative; substitute authorized values.
After submitting the real result, handle another pending batch if one is returned; otherwise inspect the correlated run outcome. Follow the result contract before declaring the customer’s navigation complete.
Copy the pending call’s id and name into toolCallId and toolName, and return the actual resultJson. Echo the latest clientToolCursor for each synchronous batch. Another AGENT_STATUS_AWAITING_CLIENT_TOOLS response starts another batch; provisional text or a request whose wait ended early is not a terminal outcome.
Check the customer’s completed navigation
Section ID: Guide/j8fgpgk84t#run-it
After the request above, verify the whole customer journey:
-
The app received the complete
navigate_tocall, ID and currentclientToolCursor. -
It validated
/profileand the user's access before executing the action. -
The profile screen actually opened, and the submitted result describes that outcome.
-
The same run reached a recognized terminal outcome, or another tool batch was handled with its new cursor.
Finished result: the screen is open and the assistant's available reply is tied to the completed action. If navigation failed, submit a truthful failure outcome rather than claiming it opened. The default five-minute answer window ends the wait on expiry; it does not establish whether an external action happened. Recover the original call before repeating an uncertain action.
Recover the call when your connection cannot stay open
Section ID: Guide/j8fgpgk84t#if-you-cant-hold-a-connection
A worker can send with send-message and learn about a call from the enabled llm.tool_call_started delivery surface or by polling pending client tools. The event’s presence in the API does not establish that webhook delivery is enabled in your deployment.
Retain the original conversation and call IDs. Read the pending call, its deadline and current status before executing; when the call ID is known, use the exact call readback; a dropped connection does not authorize repeating an action that may already have completed. Validate arguments and permissions, reconcile the external result, then answer the same pending call with submit-client-tool-results. Inspect the resumed run to its outcome.
Do not resend the original user message to recover a lost response. That creates another send and can invoke the configured cancel, queue, reject or ignore policy. If the call has become terminal or its deadline has passed, stop the old execution attempt and use the reported outcome.
Separate recipe: let the customer connect a calendar
Section ID: Guide/j8fgpgk84t#step-4-connect-a-third-party-app-optional
Use a connected calendar when the assistant needs a supported external account. The hosted Pipedream integration must be enabled for your deployment; it exposes connected accounts through pipedream:{appSlug} MCP servers without your application storing their credentials.
-
Follow the connected-account recipe to issue a fresh Connect link for this attempt and use its returned expiry when the user chooses to connect. Redirect them through the authorized flow.
-
After the return, list accounts to confirm the intended calendar exists; a redirect alone is not the result.
-
Select
pipedream:google_calendarfor the conversation and inspect its discovered tools. Discovery occurs per turn, so a later connection is picked up on the next message. -
Ask for work supported by those tools, apply the required platform approval policy, and follow the run to its actual calendar outcome.
Finished result: the chosen calendar is connected and the requested calendar work has a known outcome. A connection or tool list alone does not establish that the work finished. Use connected-app recovery for an unhealthy account and tool recovery for a lost call response.
Diagnose a platform MCP tool independently
Section ID: Guide/j8fgpgk84t#try-it-out
If the connected-app variant fails before a useful tool result, test an authorized platform tool directly using the exact discovered server, name and argument schema. That diagnostic bypasses the conversation; it cannot test the navigate_to action implemented in your app.
Keep direct memory-tool experiments to isolated or operator-qualified data. A caller-supplied memory user ID needs trusted-backend authorization; the authentication header does not automatically rewrite that argument. See memory scope before using real records.
Next steps
Section ID: Guide/j8fgpgk84t#next-steps
| I want to... | Go to |
|---|---|
| Understand all tool sources and how they interact | Agent tools |
| Connect users' third-party SaaS accounts | Connected apps |
| Discover what MCP servers and tools are available | List available servers |
| Configure approval policies for platform tools | Agent tools — approval flow |
| Browse all LLM API endpoints | LLM API Reference |
Document ID: Guide/j8fgpgk84t