Skip to content

Connect Expensify to Claude: Manage Policies and Reconciliation

Learn how to build a managed MCP server to connect Expensify to Claude. Automate expense reports, policy management, and financial reconciliation securely.

Uday Gajavalli Uday Gajavalli · · 10 min read
Connect Expensify to Claude: Manage Policies and Reconciliation

If you need to connect Expensify to Claude to automate expense reconciliation, manage policy workflows, or oversee employee spend rules, you need a Model Context Protocol (MCP) server. This server acts as the translation layer between Claude's tool calls and Expensify's API. You can either build and maintain this infrastructure yourself, or use a managed integration platform like Truto to dynamically generate a secure, authenticated MCP server URL. If your team uses ChatGPT, check out our guide on /connect-expensify-to-chatgpt-sync-expenses-and-automate-reports/ or explore our broader architectural overview on /connect-expensify-to-ai-agents-bulk-update-staff-and-rules/.

Giving a Large Language Model (LLM) read and write access to a specialized financial system like Expensify is an engineering challenge. You have to handle complex asynchronous file exports, map Expensify's unique domain models to MCP tool definitions, and deal with strict domain-admin scoping constraints. Every time an endpoint changes or you need to support a new FreeMarker template for exports, you have to update your server code, redeploy, and test the integration.

This guide breaks down exactly how to use Truto to generate a secure, managed MCP server for Expensify, connect it natively to Claude Desktop, and execute complex financial workflows using natural language.

The Engineering Reality of the Expensify API

A custom MCP server is a self-hosted integration layer. While the open MCP standard provides a predictable way for models to discover tools, the reality of implementing it against Expensify's API is painful. Expensify does not behave like a standard CRUD REST API; it relies heavily on legacy batch processing patterns and templated exports.

If you decide to build a custom MCP server for Expensify, here are the specific integration challenges you will face:

FreeMarker Templating for Data Exports When you want to pull data out of Expensify - whether it is a list of reports, expenses, or a company card reconciliation - you cannot just call a simple GET /expenses endpoint. Expensify requires you to pass a FreeMarker template string in your request payload to define exactly how the output file should be formatted. If your LLM attempts to generate this template on the fly, it will almost certainly fail due to syntax errors or missing required fields. A managed MCP server abstracts this away by strictly defining the expected inputs in the tool schema, guiding the LLM to provide only the necessary parameters while the underlying proxy handles the template injection.

Asynchronous File Retrieval Workflows Exporting data from Expensify is inherently a two-step, asynchronous process. When you request a report export or a reconciliation file, the API does not return the data in the response body. Instead, it triggers a background job and returns a file name. You must then poll the file retrieval endpoint using that specific file name until the JSON data is ready. If you build your own MCP server, you have to write custom orchestration logic to handle this polling, otherwise Claude will simply see a file name and assume the job is finished.

Domain Admin Requirements and The Advanced Employee Updater Managing employees across policies (workspaces) requires navigating Expensify's strict permissions model. Bulk updating employees or managing company cards requires domain-level administrative rights, not just policy-level rights. Furthermore, updating staff relies on the "Advanced Employee Updater" endpoint, which expects a highly specific payload structure containing nested arrays for adding, updating, and removing employees simultaneously. Without a rigorously defined MCP JSON Schema, Claude will hallucinate the array structures, resulting in rejected API calls.

Factual Note on Rate Limits When building AI integrations, rate limits are a critical architectural consideration. Truto does not retry, throttle, or apply backoff on rate limit errors. When the upstream Expensify API returns an HTTP 429, Truto passes that error directly to the caller. Truto normalizes the upstream rate limit information into standardized headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset) per the IETF specification. The caller - whether that is your custom code or the agent framework - is entirely responsible for implementing retry and backoff logic. Truto does not automatically absorb rate limit errors.

How to Create the MCP Server for Expensify

Truto's MCP server generation is dynamic and documentation-driven. Rather than hand-coding tool definitions, Truto derives them directly from the integration's documented resources and schemas. This ensures that only well-curated, AI-ready tools are exposed.

You can generate an MCP server URL for a connected Expensify account in two ways.

Method 1: Via the Truto UI

This is the fastest method for internal tooling or quick prototyping.

  1. Log into your Truto dashboard and navigate to your connected Expensify environment.
  2. Go to the integrated account page for the specific Expensify connection.
  3. Click the MCP Servers tab.
  4. Click Create MCP Server.
  5. Select your desired configuration (e.g., limit to read-only methods, filter by specific tags, set an expiration date).
  6. Copy the generated MCP server URL (it will look like https://api.truto.one/mcp/a1b2c3...).

Method 2: Via the Truto API

For production use cases where you are provisioning AI agents dynamically for your customers, you should generate the MCP server programmatically.

Make a POST request to /integrated-account/:id/mcp with your desired configuration:

const response = await fetch('https://api.truto.one/integrated-account/<EXPENSIFY_ACCOUNT_ID>/mcp', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer <YOUR_TRUTO_API_TOKEN>',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: "Finance Team Expensify MCP",
    config: {
      methods: ["read", "write"], 
      tags: ["expenses", "policies"]
    },
    expires_at: "2026-12-31T23:59:59Z"
  })
});
 
const mcpServer = await response.json();
console.log("Your MCP Server URL:", mcpServer.url);

The returned URL is a fully self-contained, cryptographically secure endpoint. The URL itself acts as the authentication token, routing the request to the correct tenant's Expensify connection.

How to Connect the MCP Server to Claude

Once you have the URL, connecting it to Claude is a matter of configuration. The MCP client will handle the JSON-RPC 2.0 handshake to discover the available tools.

Method A: Via the Client UI (ChatGPT or Claude Web)

If you are using enterprise accounts that support UI-based custom connectors:

  1. In your AI client settings, navigate to the integrations or custom connectors section (e.g., Settings - Connectors - Add custom connector in Claude, or Settings - Apps - Advanced settings - Developer mode in ChatGPT).
  2. Provide a recognizable name (e.g., "Expensify Finance API").
  3. Paste the Truto MCP Server URL.
  4. Save the configuration. The client will immediately parse the available Expensify tools.

Method B: Via Manual Config File (Claude Desktop)

For local development or custom agent deployments, you can configure Claude Desktop using the claude_desktop_config.json file. Because Truto provides a hosted SSE (Server-Sent Events) endpoint, you use the standard @modelcontextprotocol/server-sse package to proxy the connection.

Add the following to your configuration file:

{
  "mcpServers": {
    "expensify_truto": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-sse",
        "https://api.truto.one/mcp/<YOUR_GENERATED_TOKEN>"
      ]
    }
  }
}

Restart Claude Desktop. The Expensify tools will now appear as accessible capabilities in your prompt interface.

Expensify Hero Tools

When you connect Expensify via Truto, Claude gains access to a normalized set of proxy methods. Here are the highest-leverage tools available for Expensify automation.

List All Policies

Tool Name: list_all_expensify_policies

Before taking action on expenses or users, an agent needs to know which policy (workspace) it is operating within. This tool lists all policies the authenticated user can access, returning critical metadata including the Policy ID, which is required for almost all subsequent operations.

"List all the Expensify policies available for this account. Return the Policy ID and name for the primary US entity workspace."

Create an Expense

Tool Name: create_a_expensify_expense

This tool allows the agent to generate new expenses directly in an employee's account. It handles the specific JSON payload required by Expensify, ensuring that fields like amount, merchant, date, and the mandatory policy ID are formatted correctly.

"Create an expense for $150.00 at 'Delta Airlines' for user john.doe@example.com under the US Entity policy. Categorize it as 'Travel' and mark the date as today."

Create a Report

Tool Name: create_a_expensify_report

Once expenses are created, they must be grouped into a report for submission and approval. This tool creates a new report container on a specific policy. The agent can use this to logically group expenses by trip, project, or month.

"Create a new expense report on the US Entity policy named 'Q3 Conference Travel' for user john.doe@example.com."

Create a Reconciliation

Tool Name: create_a_expensify_reconciliation

This is a critical tool for finance teams. It triggers the generation of a company-card reconciliation file for a specific domain. Because this endpoint relies on Expensify's background job system, the tool kicks off the process and returns a file name.

"Trigger a company card reconciliation for the domain example.com for the month of October. Give me the file name so we can retrieve the data."

Get Single File by ID

Tool Name: get_single_expensify_file_by_id

This is the required second half of any export or reconciliation workflow. Once create_a_expensify_reconciliation or expensify_reports_export returns a file name, the agent uses this tool to download the actual JSON data representing the report or reconciliation.

"Using the file name you just received, fetch the actual reconciliation file contents so I can review the unlinked card transactions."

Bulk Update Employees

Tool Name: expensify_employees_bulk_update

This tool interacts with Expensify's Advanced Employee Updater. It allows an agent to add, modify, or remove policy members in bulk. This is exceptionally useful for automating onboarding and offboarding workflows.

"Add sarah.smith@example.com to the US Entity policy as an 'employee', and update michael.jones@example.com to have an 'auditor' role. Process this as a single bulk update."

For the complete tool inventory and detailed JSON schemas, view the Expensify integration page.

Workflows in Action

Providing LLMs with isolated tools is just the starting point. The real power of MCP lies in chaining these operations together to automate multi-step domain workflows.

Workflow 1: End-to-End Company Card Reconciliation

Finance operations teams spend hours manually matching corporate card feeds to submitted expenses. An AI agent can fully automate the retrieval of this data for auditing.

"I need to audit our corporate card spend. Please trigger a reconciliation file for our main domain for last month. Once it is ready, fetch the file, analyze the data, and summarize any transactions over $500 that haven't been attached to an expense report yet."

Execution Steps:

  1. create_a_expensify_reconciliation: The agent calls this tool, passing the domain name and date range. Expensify begins processing the background job and returns a file_name.
  2. get_single_expensify_file_by_id: The agent calls this tool using the file_name it just received. It may receive a pending status, forcing it to retry briefly, until the full JSON reconciliation file is returned.
  3. Data Analysis: The agent parses the returned JSON, filters for unlinked transactions above $500, and formats a summary table for the user.
sequenceDiagram
    participant Claude as Claude Desktop
    participant Truto as Truto MCP
    participant Expensify as Expensify API
    
    Claude->>Truto: Call create_a_expensify_reconciliation
    Truto->>Expensify: POST /reconciliations
    Expensify-->>Truto: Return file_name
    Truto-->>Claude: Return file_name to agent
    
    Claude->>Truto: Call get_single_expensify_file_by_id (file_name)
    Truto->>Expensify: GET /files/{file_name}
    Expensify-->>Truto: Return JSON data
    Truto-->>Claude: Return parsed financial data

Workflow 2: Employee Onboarding and Spend Rules

When a new employee joins, IT and HR need to provision them in Expensify, assign them to the correct workspace, and establish their baseline expense rules.

"We have a new hire, Alex. Add alex.w@example.com to the primary US workspace as a standard employee. Then, create an expense rule for him that automatically flags any 'Meals' expense over $75."

Execution Steps:

  1. list_all_expensify_policies: The agent looks up the available workspaces to find the exact Policy ID for the "US workspace".
  2. expensify_employees_bulk_update: The agent constructs the nested array payload to add Alex to the identified policy.
  3. create_a_expensify_expense_rule: Using Alex's email and the Policy ID, the agent creates a specific rule targeting the 'Meals' category with a $75 limit.

Workflow 3: Drafting a Client Trip Expense Report

Sales representatives frequently need to log multiple expenses from a single trip and group them together for their manager's approval.

"I just got back from the Austin conference. I spent $45.50 at 'Terry Blacks BBQ' for dinner yesterday, and $350.00 at 'Hilton Austin' for lodging. Create these expenses for my account, create a new report called 'Austin Sales Trip', and put both expenses into that report."

Execution Steps:

  1. list_all_expensify_policies: The agent retrieves the user's active Policy ID.
  2. create_a_expensify_expense: The agent calls this tool twice - once for the BBQ meal and once for the hotel - capturing the returned Expense IDs for both.
  3. create_a_expensify_report: The agent creates the container report.
  4. update_a_expensify_report (or via the initial creation payload depending on integration depth): The agent links the generated Expense IDs to the newly created Report ID.

Security and Access Control

Exposing financial systems like Expensify to AI models requires strict security boundaries. Truto's MCP architecture enforces security through several mechanisms on the server URL:

  • Method Filtering: You can strictly limit an MCP server to read-only operations by setting methods: ["read"]. This allows an agent to fetch policies and view expenses without the ability to create or delete financial records.
  • Tag Filtering: By applying tags: ["reports"], you can isolate the server so the LLM only has context on report-related tools, hiding potentially dangerous administrative endpoints.
  • Required API Token Auth: For high-security environments, you can enable require_api_token_auth: true. This forces the MCP client to pass a valid Truto API token in the Authorization header, meaning the MCP URL alone is useless if leaked.
  • Time-To-Live (TTL): You can set an expires_at timestamp. Once reached, the server is automatically destroyed. This is ideal for granting an AI agent temporary access to generate end-of-month reconciliations before revoking permissions.

Wrapping Up

Connecting Claude to Expensify via MCP transforms the LLM from a passive chat interface into an active financial operator. By utilizing Truto to generate a managed MCP server, you eliminate the need to build custom infrastructure, manage OAuth token lifecycles, or constantly maintain code against Expensify's idiosyncratic endpoints.

Instead, you get a secure, dynamic integration layer that scales with your AI strategy. Your agents gain the ability to navigate complex asynchronous exports, execute bulk updates safely, and manage financial workflows exactly as a human administrator would - just significantly faster.

FAQ

Does Truto automatically handle API rate limits for Expensify?
No. Truto does not retry, throttle, or apply backoff on rate limit errors. When Expensify returns an HTTP 429, Truto passes that error directly to the caller, standardizing the information into ratelimit-limit, ratelimit-remaining, and ratelimit-reset headers. The calling agent framework is responsible for handling backoff logic.
How do AI agents handle Expensify's asynchronous file exports?
Expensify exports (like reconciliations) require a two-step process. The MCP server provides separate tools for this: the agent first calls the creation tool to trigger the job and receive a file name, and then uses the get_single_expensify_file_by_id tool to fetch the actual JSON data.
Can I restrict Claude to only reading data from Expensify?
Yes. When creating the Truto MCP server, you can use method filtering by passing methods: ["read"]. This ensures Claude can only fetch policies or view expenses, completely blocking write or delete operations.
How does Truto handle Expensify's FreeMarker templates?
Because LLMs struggle to write valid FreeMarker templates on the fly, Truto's MCP server exposes strictly defined tool schemas for common operations. This guides the LLM to provide only the necessary parameters, while the underlying proxy manages the complex payload formatting.

More from our Blog