Skip to main content

What Are Client Actions?

Client actions allow your AI agent to request that your application perform an action on the client side. When an agent determines it needs external information or wants to trigger an operation, it responds with a finishReason of "tool-calls" and includes tool-call parts describing what it needs. Your application executes the action, submits the result back to the API, and then continues the conversation.
Client actions correspond to the Custom Actions configured on your agent in the Chatbase dashboard. The toolName in the API response is the name of the configured action.

Flow

1

Send a chat message

Send a message to the chat endpoint as usual.
2

Receive a client action request

The response has finishReason: "tool-calls" and tool-call parts containing toolCallId, toolName, and input.
3

Execute the action client-side

Use toolName and input to determine what to do and execute the action in your application.
4

Submit the result

Send the result to POST /agents/{agentId}/conversations/{conversationId}/tool-result with the toolCallId and output.
5

Continue the conversation

Call the chat endpoint again with the conversationId. You can omit message to let the agent continue based on the tool result alone, or include a new message.

Message Parts

Responses can include three types of parts in the parts array:

text

Text content generated by the agent.Fields: type, text

tool-call

A client action the agent wants your app to execute.Fields: type, toolCallId, toolName, input

tool-result

The result of a previously executed client action (visible in conversation history).Fields: type, toolCallId, toolName, output

Detecting a Client Action

Check the finishReason in the response metadata. When it is "tool-calls", the parts array will contain one or more tool-call entries:
{
  "data": {
    "id": "msg_abc123",
    "role": "assistant",
    "parts": [
      { "type": "text", "text": "Let me look up that order for you." },
      {
        "type": "tool-call",
        "toolCallId": "call_abc123",
        "toolName": "lookupOrder",
        "input": { "orderId": "ORD-123" }
      }
    ],
    "metadata": {
      "userMessageId": "msg_xyz789",
      "conversationId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
      "userId": "user_abc123",
      "finishReason": "tool-calls",
      "usage": { "credits": 2 }
    }
  }
}

Submitting the Result

After executing the action, submit the result using the tool-result endpoint:
POST /api/v2/agents/{agentId}/conversations/{conversationId}/tool-result

Request Body

toolCallId
string
required
The toolCallId from the tool-call part in the chat response.
output
any
The result of executing the action.

Response

{
  "data": {
    "success": true
  }
}

Continuing the Conversation

After submitting the tool result, continue the conversation by calling the chat endpoint again. You can either:
  • Omit message to let the agent continue based on the tool result alone.
  • Include a message to provide additional context or a follow-up question.
You must include the conversationId to continue the same conversation.
curl -X POST 'https://www.chatbase.co/api/v2/agents/YOUR_AGENT_ID/chat' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "conversationId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
  }'

Streaming Client Actions

When streaming is enabled, client action input arrives incrementally through these events:
1

tool-input-start

Signals the start of a client action. Includes toolCallId and toolName.
2

tool-input-delta

Incremental chunks of the action input stream in.
3

tool-input-available

The complete input is ready. You can read the full input object directly from this event without concatenating the preceding deltas.
The stream’s message-metadata event will have finishReason: "tool-calls". See Streaming for full event type reference.

Code Examples

// Step 1: Send a message
const chatResponse = await fetch(
  "https://www.chatbase.co/api/v2/agents/YOUR_AGENT_ID/chat",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      message: "What's the status of order ORD-123?",
      stream: false,
    }),
  }
);

const { data } = await chatResponse.json();
const { conversationId, finishReason } = data.metadata;

// Step 2: Check if a client action was invoked
if (finishReason === "tool-calls") {
  for (const part of data.parts) {
    if (part.type === "tool-call") {
      // Step 3: Execute the action
      const result = await executeAction(part.toolName, part.input);

      // Step 4: Submit the result
      await fetch(
        `https://www.chatbase.co/api/v2/agents/YOUR_AGENT_ID/conversations/${conversationId}/tool-result`,
        {
          method: "POST",
          headers: {
            Authorization: "Bearer YOUR_API_KEY",
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            toolCallId: part.toolCallId,
            output: result,
          }),
        }
      );
    }
  }

  // Step 5: Continue the conversation
  const continueResponse = await fetch(
    "https://www.chatbase.co/api/v2/agents/YOUR_AGENT_ID/chat",
    {
      method: "POST",
      headers: {
        Authorization: "Bearer YOUR_API_KEY",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        conversationId,
        stream: false,
      }),
    }
  );

  const continued = await continueResponse.json();
  console.log(continued.data.parts);
}

// Your action handler
async function executeAction(toolName, input) {
  switch (toolName) {
    case "lookupOrder":
      // Call your order service
      return { status: "shipped", eta: "2026-04-03" };
    default:
      return { error: "Unknown action" };
  }
}

Error Handling

CodeStatusDescription
RESOURCE_TOOL_CALL_NOT_FOUND404No pending client action matches the provided toolCallId. It may have expired or already been resolved.
VALIDATION_INVALID_BODY400The request body failed schema validation. Check the details field for specifics.