back to tutorials

How to Use Pure Function Calling with Caller Handling

Learn how to implement pure function calling where the caller has full control over function execution. This tutorial covers manual conversation loops, activity messages, and building complex workflows with external function handling.

This tutorial demonstrates how to implement "pure" function calling in ChatBotKit, where functions are defined without handlers or static results, giving the caller complete control over function execution.

Learning Objectives

By the end of this tutorial, you will be able to:

  • Define pure functions without handlers or result configurations
  • Implement a manual conversation loop for full control
  • Handle the activity end reason to detect function calls
  • Construct response activity messages manually
  • Build complex workflows with external function execution

Prerequisites

  • Node.js 18+ installed
  • A ChatBotKit account with an API secret
  • Understanding of async/await and conversation flows

Estimated time: 25 minutes

Understanding Pure Functions

ChatBotKit supports three approaches to function results:

ApproachConfigurationExecution
Staticresult.dataServer returns data immediately
Channelresult.channelCaller publishes via channel
PureNo result propertyCaller handles everything

With pure functions, when the AI calls a function, the conversation ends with end.reason: 'activity'. You then:

  1. Inspect the function call
  2. Execute the function externally
  3. Construct a response activity message
  4. Continue the conversation

This pattern is ideal for:

  • Complex orchestration or approval workflows
  • Human-in-the-loop scenarios
  • Persisting function calls to external systems
  • Running functions in different processes or services

Step 1: Set Up Your Project

Create a new Node.js project:

Create a .env file:

CHATBOTKIT_API_SECRET=your_api_secret_here

Step 2: Define Pure Functions

Create index.js and define functions without a result property:

Step 3: Set Up the Conversation

Initialize the client and messages:

Step 4: Implement the Manual Conversation Loop

Create a loop that handles function calls manually:

Step 5: Handle Function Calls

When the conversation ends with activity, extract and execute the function:

Step 6: Implement Function Execution

Create the function that executes your business logic:

Complete Example

Here's the complete working example:

Activity Message Structure

The response activity message must follow this structure:

Important: The result field must be a JSON string, not an object.

Use Cases for Pure Functions

Human-in-the-Loop Approval

External Service Execution

Audit Logging

Troubleshooting

Infinite Loop

Always set a maxIterations limit and check the end reason to break out of the loop appropriately.

Missing Activity Data

Ensure you check lastMessage.type === 'activity' and activity.type === 'request' before accessing function data.

Result Not a String

The result field in the response activity must be a JSON string. Use JSON.stringify().

Next Steps