Skip to content

Connect Expensify to AI Agents: Bulk Update Staff and Rules

Roopendra Talekar Roopendra Talekar 10 min read AI & Agents
TrutoFor teams building AI agents

Give your AI agent Expensify tools.

Connect Expensify to AI Agents using Truto's /tools endpoint to programmatically fetch schemas, bind them to LLMs, and autonomously execute complex HR and expense rule workflows.

In this guide

  1. 01Fetch Expensify tools from Truto
  2. 02Bind tools to your agent framework
  3. 03Execute the workflow loop
  4. 04Handle rate limits and errors

The guide

Learn how to connect Expensify to AI Agents to automate bulk staff updates, expense rules, and reconciliations using Truto's /tools endpoint and SDKs.

You want to connect Expensify to an AI agent so your system can independently onboard staff, assign custom expense rules, and trigger month-end reconciliations. Here is exactly how to do it using Truto's /tools endpoint and SDK, bypassing the need to build and maintain a custom Expensify API connector from scratch.

Giving a Large Language Model (LLM) read and write access to your financial operations platform requires strict architectural discipline. You either spend months building, hosting, and maintaining a custom connector that normalizes complex templates and payloads, or you use a managed infrastructure layer that provides the schemas directly to your agent. If your team uses ChatGPT, check out our guide on connecting Expensify to ChatGPT, or if you are building on Anthropic's models, read our guide on connecting Expensify to Claude. For developers building custom autonomous workflows, you need a programmatic way to fetch these tools and bind them to your agent framework.

This guide breaks down exactly how to fetch AI-ready tools for Expensify, bind them natively to an LLM using frameworks like LangChain, LangGraph, CrewAI, or the Vercel AI SDK, and execute complex financial workflows. For a deeper look at the architecture behind this approach, refer to our research on architecting AI agents and the SaaS integration bottleneck.

The Engineering Reality of the Expensify API

Giving an LLM access to external APIs is straightforward when dealing with standard RESTful CRUD operations. But when deploying agents against mature, enterprise-grade financial systems like Expensify, standard integration assumptions collapse.

Expensify's API introduces several specific integration challenges. If you hardcode these interactions into your agent, you will spend your engineering cycles writing defensive parsing logic instead of improving your model's reasoning capabilities.

The Asynchronous Export and Template Trap

Standard LLMs are trained to expect immediate JSON responses when requesting data. If an agent wants to view a reconciliation report, it naturally expects to call a get_reconciliation tool and receive a flat JSON array of expenses.

Expensify does not work this way. Data extraction in Expensify is fundamentally asynchronous and relies on custom FreeMarker templates. To get report data, you must first send a POST request with a specific XML-like template string defining the exact structure you want the output to take. The API does not return the data; it returns a generated file name. You must then make a subsequent GET request using that file name to download the actual payload. If your agent is not explicitly trained to handle this multi-step, template-driven async flow, it will hallucinate the template syntax or fail to fetch the resulting file.

The Bulk Update Complexity

Expensify relies heavily on its Advanced Employee Updater endpoint for personnel changes. Instead of simple POST /employees or PATCH /employees/{id} endpoints, you interact with a bulk endpoint that expects a deeply nested array of employee objects.

An AI agent attempting to add one user must understand that it needs to construct a bulk payload array containing a single object, mapped to the correct policy ID, with specific approver and manager email strings. Minor formatting errors in this bulk array result in silent failures or misconfigured organizational hierarchies.

Dual-Layer Authentication Scopes

Expensify authenticates requests using a combination of Partner credentials (partnerUserID and partnerUserSecret) and User credentials (userID and userSecret). Different endpoints require different credential combinations depending on the action's scope. Pushing this authentication logic into an LLM's context window wastes tokens and increases the risk of hallucinated credential payloads.

By routing calls through Truto's proxy layer, these authentication headers are injected automatically at the infrastructure level. The agent only needs to focus on the business logic - providing the correct email addresses, rule amounts, and policy IDs.

Fetching Expensify Tools via Truto

Truto solves these integration bottlenecks by converting the underlying API methods into standardized Proxy APIs. Every resource on an integration is mapped into a strict JSON schema. Truto handles the authentication injection, query parameter normalization, and endpoint mapping.

To give your AI agent access to Expensify, you simply call Truto's /tools endpoint for your specific integrated account. This returns a ready-to-use array of tool schemas that define exactly what the agent can do and what arguments it must provide.

curl -X GET "https://api.truto.one/integrated-account/{account_id}/tools?methods[0]=read&methods[1]=write" \
  -H "Authorization: Bearer YOUR_TRUTO_API_KEY"

The response is a structured array of tools. Using an SDK like truto-langchainjs-toolset, you can fetch these tools programmatically and bind them directly to your LLM.

import { TrutoToolManager } from 'truto-langchainjs-toolset';
import { ChatOpenAI } from '@langchain/openai';
 
// Initialize the Tool Manager for the Expensify account
const toolManager = new TrutoToolManager({
  apiKey: process.env.TRUTO_API_KEY,
  integratedAccountId: 'your-expensify-account-id'
});
 
// Fetch the tools
const tools = await toolManager.getTools();
 
// Bind the tools to your LLM
const llm = new ChatOpenAI({ modelName: 'gpt-4o' });
const llmWithTools = llm.bindTools(tools);

A Factual Note on Rate Limits

When building autonomous agents, rate limiting is a critical infrastructure concern. AI agents can easily generate dozens of requests in seconds while looping through bulk data.

It is critical to understand that Truto does not retry, throttle, or apply backoff on rate limit errors.

When the upstream Expensify API returns an HTTP 429 (Too Many Requests), Truto passes that exact error directly back to your caller. Truto normalizes the upstream rate limit information into standardized headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset) per the IETF specification.

Your execution loop - whether that is a custom Node.js script, a LangGraph node, or a Vercel AI SDK route - is completely responsible for reading these headers, pausing execution, and implementing the retry and backoff logic. Do not build an agent assuming the proxy layer will magically absorb traffic spikes.

Hero Tools for Expensify AI Agents

When you call the /tools endpoint for Expensify, you expose specific capabilities to your LLM. Here are the highest-leverage operations you should equip your agent with to automate bulk updates and rule management.

List All Expensify Policies

Tool name: list_all_expensify_policies

This tool retrieves all policies (workspaces) that the authenticated user can access. This is the foundational read tool. Before an agent can update staff or create rules, it must discover the correct policy ID to target.

"Fetch a list of all active workspaces in our Expensify account and find the policy ID for 'Engineering Department'."

Get Single Policy Details

Tool name: get_single_expensify_policy_by_id

Once the agent has a policy ID, this tool fetches the granular details of that workspace. It returns the current categories, report fields, tags, tax rates, and the list of active employees. Agents use this to audit current access before making destructive bulk updates.

"Get the details for policy ID 123456. Who is the current approver for the 'Travel' category, and are there any active contractors on the employee list?"

Bulk Update Employees

Tool name: expensify_employees_bulk_update

This is the core tool for programmatic onboarding and offboarding. It interacts with the Advanced Employee Updater to add, update, or remove policy members in bulk. The agent constructs a JSON array of employee objects, specifying emails, approvers, and manager roles.

"We just hired three new engineers. Add john@example.com, sarah@example.com, and mike@example.com to the Engineering policy. Set their manager and approver to cto@example.com."

Create Expense Rule

Tool name: create_a_expensify_expense_rule

This tool allows the agent to generate specific expense rules for policy members. Rules dictate spending limits, required receipts, and auto-approval thresholds. By giving an agent access to this tool, you can dynamically adjust budgets based on external context.

"Create a new expense rule for sarah@example.com. Set a daily limit of $50 for meals and require receipts for any amount over $25."

Update Expense Rule

Tool name: update_a_expensify_expense_rule_by_id

When organizational policies change, the agent uses this tool to mutate existing rules. It requires the specific rule ID and the updated parameter schema. This is highly useful for temporary limit increases during corporate offsites.

"Update the existing travel rule for john@example.com (Rule ID 9876). Temporarily increase his daily hotel allowance to $300 for the duration of this week."

Generate Reconciliation File

Tool name: create_a_expensify_reconciliation

This tool initiates the asynchronous generation of a company-card reconciliation file for a domain. The agent must provide the required FreeMarker template in the payload. The API responds with a generated file name, kicking off the multi-step export flow.

"Generate a reconciliation file for last month's corporate card spend. Use our standard CSV output template. Tell me the generated file name when the job starts."

Download Export File

Tool name: get_single_expensify_file_by_id

This tool completes the asynchronous loop. Once the agent has the file name from the reconciliation or export job, it calls this endpoint (passing file_system=reconciliation) to download the actual data payload for parsing or external storage.

"Fetch the contents of the reconciliation file named 'recon_20260401.csv' and summarize the top three spenders for the month."

To view the complete schema definitions and the full list of available tools, visit the Expensify integration page.

Workflows in Action

Equipping an agent with tools is only the first step. The true value is unlocked when the LLM chains these tools together to execute multi-step operations autonomously. Here are two real-world workflows your AI agent can perform.

Scenario 1: Automated Department Re-Org

When a department undergoes restructuring, updating reporting lines and spending limits manually is tedious and error-prone. An AI agent handles this seamlessly.

"The Marketing department is being restructured. Find the Marketing policy. Get the current list of employees. Update the entire list so that everyone now reports to marketing_vp@example.com as their approver. Then, update the expense rule for the department to lower the auto-approval threshold to $50."

Execution Steps:

  1. The agent calls list_all_expensify_policies to find the ID for the Marketing policy.
  2. It calls get_single_expensify_policy_by_id to retrieve the current employee list.
  3. The agent formats a new JSON payload and calls expensify_employees_bulk_update to reassign the approver emails for all listed staff.
  4. It calls update_a_expensify_expense_rule_by_id to adjust the threshold.

Result: The entire department's hierarchy and budget logic is updated in seconds, with zero manual data entry.

Scenario 2: Month-End Financial Reconciliation

Finance teams spend days at the end of the month downloading and reconciling CSVs. An autonomous agent can manage the async API flow and extract the required insights on a schedule.

"It is month-end. Trigger a company-card reconciliation export for the global domain using the standard template. Once it generates, download the file, find all expenses tagged 'Software', and list the vendors."

Execution Steps:

  1. The agent calls create_a_expensify_reconciliation passing the required template string.
  2. The tool returns a file name (e.g., recon_output_123.txt).
  3. The agent calls get_single_expensify_file_by_id using that file name to pull the raw data.
  4. The agent parses the returned data internally, filters for 'Software', and presents the summarized vendor list back to the user.

Result: The finance team receives a clean summary of software spend without ever logging into the dashboard or running a manual export.

Building Multi-Step Workflows

To achieve the workflows described above, your application must orchestrate a robust execution loop. Standard LLM calls are stateless; they return a request to execute a tool, but your code must actually make the HTTP call and feed the result back into the model's context.

This approach works with any major agent framework (LangChain, LangGraph, CrewAI, Vercel AI SDK). The architecture looks like this:

sequenceDiagram
    participant App as Your App
    participant Agent as Agent Framework
    participant Truto as Truto Proxy Layer
    participant Upstream as Expensify API

    App->>Agent: "Add user to policy"
    Agent->>App: Yield tool_call (expensify_employees_bulk_update)
    App->>Truto: POST /proxy/expensify/bulk
    Truto->>Upstream: Forward with Auth
    Upstream-->>Truto: 429 Too Many Requests
    Truto-->>App: 429 (IETF ratelimit headers)
    Note over App: Wait based on ratelimit-reset
    App->>Truto: Retry POST /proxy/expensify/bulk
    Truto->>Upstream: Forward with Auth
    Upstream-->>Truto: 200 OK
    Truto-->>App: JSON Result
    App->>Agent: Submit tool result
    Agent-->>App: "User successfully added"

Handling the Execution Loop and Errors

When writing your execution loop, you must account for failure states. If the agent hallucinates a parameter type, the Truto proxy will return a 400 Bad Request. If you hit Expensify's limits, you will receive a 429.

Here is a conceptual example of a custom loop handling these constraints:

async function executeAgentLoop(prompt: string, tools: any, toolManager: TrutoToolManager) {
  const messages = [{ role: "user", content: prompt }];
  
  while (true) {
    // Call the LLM with current context
    const response = await llmWithTools.invoke(messages);
    messages.push(response);
 
    // If no tools were called, the agent is done
    if (!response.tool_calls || response.tool_calls.length === 0) {
      return response.content;
    }
 
    // Execute each requested tool
    for (const toolCall of response.tool_calls) {
      try {
        // toolManager routes the request to Truto
        const result = await toolManager.executeTool(toolCall.name, toolCall.args);
        
        messages.push({
          role: "tool",
          tool_call_id: toolCall.id,
          content: JSON.stringify(result)
        });
 
      } catch (error) {
        // Handle explicitly passed rate limits
        if (error.status === 429) {
          const resetTime = error.headers['ratelimit-reset'];
          console.warn(`Rate limited. Reset at ${resetTime}. Agent must retry.`);
          // Pass the error back so the agent knows it failed
          messages.push({
            role: "tool",
            tool_call_id: toolCall.id,
            content: `Error 429: Rate limited. Wait until ${resetTime} before retrying.`
          });
        } else {
          // Pass generic errors back to the agent to self-correct
          messages.push({
            role: "tool",
            tool_call_id: toolCall.id,
            content: `Error executing tool: ${error.message}`
          });
        }
      }
    }
  }
}

By passing HTTP errors directly back into the LLM's context window as a tool role message, the agent can actively reason about failures. If it receives a 400 because it provided a string instead of an array for the bulk employee update, a smart model (like GPT-4o or Claude 3.5 Sonnet) will read the error, correct its payload schema, and try again autonomously.

Building AI agents that safely interact with financial and HR systems requires deterministic tools, strict schema validation, and transparent error handling. By abstracting the Expensify API into standardized, LLM-ready tools, you shrink the surface area for hallucinations and remove the operational burden of managing complex authentication flows. Your agents can navigate bulk updates and asynchronous reconciliation tasks securely, while your engineering team maintains complete control over the execution and retry logic.

Two ways to put Expensify to work

Truto

For product teams

Give your agent Expensify tools

Your customers connect their own Expensify accounts. Your product gets one API and MCP tools for Expensify, through Truto.

FAQ

How do I give my AI agent access to Expensify data?
You can fetch AI-ready tool schemas from Truto's /tools endpoint, which maps directly to Expensify's endpoints. These schemas can be bound to frameworks like LangChain or the Vercel AI SDK, allowing your agent to make deterministic API calls.
How does Truto handle Expensify API rate limits?
Truto does not retry, throttle, or apply backoff on rate limit errors. If Expensify returns an HTTP 429, Truto passes the error to the caller with normalized IETF standard headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset). Your application must handle the retry logic.
Can I bulk update employees in Expensify using an AI agent?
Yes. By exposing the 'expensify_employees_bulk_update' tool to your agent, it can autonomously generate the required payload to add, update, or remove policy members in bulk based on a natural language prompt.
What agent frameworks work with Truto's tools?
Truto's unified tools are framework-agnostic. You can bind them to LangChain, LangGraph, CrewAI, Vercel AI SDK, or any custom execution loop that supports standard LLM tool calling.
Expensify ExpensifyAI agent tools Get a sandbox

More from our Blog