Skip to content

Connect Expensify to ChatGPT: Sync Expenses and Automate Reports

Learn how to connect Expensify to ChatGPT using Truto's auto-generated MCP server. Sync expenses, manage policies, and automate financial reports with AI.

Roopendra Talekar Roopendra Talekar · · 10 min read
Connect Expensify to ChatGPT: Sync Expenses and Automate Reports

If you need to connect Expensify to ChatGPT to automate expense tracking, synchronize financial reporting, or manage employee policies, you need a Model Context Protocol (MCP) server. This server acts as the standardized translation layer between ChatGPT's internal tool calls and Expensify's backend architecture. You can either spend weeks building, hosting, and maintaining this infrastructure yourself, or use a managed integration platform like Truto to dynamically generate a secure, authenticated MCP server URL.

If your team uses Claude, check out our guide on connecting Expensify to Claude or explore our broader architectural overview on connecting Expensify to AI Agents.

Giving a Large Language Model (LLM) read and write access to a financial system of record is a massive engineering challenge. You have to handle asynchronous file exports, complex templating languages required by the API, and strict domain-level access controls. Every time your engineering team modifies how you interact with Expensify, your custom server code must be updated, redeployed, and regression tested.

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

The Engineering Reality of the Expensify API

A custom MCP server is essentially a self-hosted integration middleware. While the open MCP standard provides a predictable way for LLMs to discover tools using JSON-RPC 2.0, implementing it against Expensify's highly specific API is exceptionally painful.

If you decide to build a custom MCP server for Expensify, you own the entire API lifecycle. Here are the specific integration challenges that break standard CRUD assumptions when working with Expensify:

Asynchronous Export and File Retrieval

Expensify does not return large datasets in a synchronous JSON response. If an LLM needs to analyze expenses for a specific policy, it cannot just call GET /expenses. Instead, the API requires a two-step asynchronous workflow. First, you must hit the reports.export endpoint, which initiates a background job and returns a file name. You then have to poll or immediately query a separate files.get endpoint using that file name to download the actual payload. If your MCP server does not correctly expose both endpoints and instruct the LLM on how to chain them together, ChatGPT will fail to retrieve any historical data.

FreeMarker Templating Requirements

Certain advanced endpoints in Expensify, specifically reconciliations.create, do not accept standard JSON parameter structures. They require you to pass a literal Apache FreeMarker template string inside the payload to format the output file. Expecting an LLM to accurately write, escape, and pass a FreeMarker template as a JSON string argument is highly error-prone unless the MCP tool schema provides explicit instructions and parameter boundaries.

Policy vs. Domain Authorization Models

Expensify uses a bifurcated authorization model. Standard expense creation and tagging happen at the "Policy" level (which functions like a workspace). However, actions like issuing company cards or managing bulk employee lists happen at the "Domain" level. If you build a custom MCP server, you must maintain distinct tool contexts so the LLM understands when to pass a policyID versus when to invoke a domain admin operation.

Truto resolves these issues by mapping Expensify's endpoints to a standardized set of Proxy APIs, wrapping them in heavily documented JSON Schemas, and exposing them dynamically as MCP tools.

Generating the Expensify MCP Server

Truto scopes each MCP server to a single integrated account. This ensures that when ChatGPT invokes a tool, the request routes cleanly to a specific, authenticated Expensify tenant without requiring the LLM to manage API keys or OAuth tokens.

You can generate the MCP server URL in two ways: via the Truto dashboard or programmatically via the API.

Method 1: Via the Truto UI

This is the fastest method for internal tooling or testing.

  1. Log into your Truto dashboard and navigate to the Integrated Accounts page.
  2. Select your connected Expensify account.
  3. Click the MCP Servers tab.
  4. Click Create MCP Server.
  5. Configure the server. You can assign a human-readable name, select method filters (e.g., restrict to read only), and define an expiration date.
  6. Click Create and immediately copy the generated MCP server URL.

This URL contains a cryptographic token that securely identifies the integrated account. Treat this URL like a production secret.

Method 2: Via the Truto API

For production use cases where you are provisioning agents on behalf of your customers, you will generate the MCP server programmatically. Make an authenticated POST request to the /mcp endpoint of the specific integrated account.

curl -X POST https://api.truto.one/integrated-account/<INTEGRATED_ACCOUNT_ID>/mcp \
  -H "Authorization: Bearer $TRUTO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ChatGPT Expensify Assistant",
    "config": {
      "methods": ["read", "write"],
      "tags": ["policies", "expenses"]
    }
  }'

The API will return a JSON object containing the ready-to-use URL:

{
  "id": "mcp_abc123",
  "name": "ChatGPT Expensify Assistant",
  "config": { "methods": ["read", "write"], "tags": ["policies", "expenses"] },
  "expires_at": null,
  "url": "https://api.truto.one/mcp/a1b2c3d4e5f6..."
}

Connecting the MCP Server to ChatGPT

Once you have your Truto MCP URL, you need to register it with your client. We will look at both the native ChatGPT interface and the standard configuration file method used by remote runners.

Method A: Via the ChatGPT UI

If you are using ChatGPT Pro, Plus, Business, Enterprise, or Education, you can connect the server directly in the browser.

  1. Open ChatGPT and navigate to Settings -> Apps -> Advanced settings.
  2. Toggle on Developer mode.
  3. Under the custom connectors or MCP servers section, click Add new server.
  4. Enter a name (e.g., "Expensify Integration").
  5. Paste the Truto MCP URL into the Server URL field.
  6. Click Save.

ChatGPT will perform an initialization handshake with the Truto MCP router and immediately pull down the available Expensify tool schemas.

Method B: Via Manual Config File

If you are running a custom LLM framework or a remote agent infrastructure (like LangChain or a custom OpenAI wrapper), you will connect via a configuration file using the Server-Sent Events (SSE) transport.

Add the following to your mcp.json or equivalent configuration file:

{
  "mcpServers": {
    "expensify-truto": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-sse",
        "--url",
        "https://api.truto.one/mcp/a1b2c3d4e5f6..."
      ]
    }
  }
}

This directs your local or hosted agent runner to bridge the standard stdio MCP protocol over HTTP to Truto's remote endpoint.

Expensify Hero Tools

When ChatGPT queries the Truto MCP server, it receives dynamically generated tool schemas based on the underlying Expensify API integration. Here are six high-leverage hero tools your agents can use immediately.

List All Expensify Policies

Tool Name: list_all_expensify_policies

This tool retrieves the list of all policies (workspaces) the authenticated user has access to. It is usually the first tool an agent calls, as nearly all expense operations require a policyID.

"What Expensify policies do I currently have access to? Please list their names and IDs."

Create an Expensify Expense

Tool Name: create_a_expensify_expense

This tool creates one or more expenses in an employee's account. The LLM must supply the amount, currency, and the target email of the employee. It can also attach tags and categories.

"Create a new expense for 45.50 USD on the 'Q3 Marketing' policy for user alice@company.com. Categorize it as 'Software Subscriptions'."

Expensify Reports Export

Tool Name: expensify_reports_export

This tool kicks off the asynchronous reporting job. It starts a JSON export of reports and their associated expenses. Crucially, it returns a generated file name rather than the data itself.

"Start an export of all approved reports from last month for the Engineering policy. Tell me the file name when the job starts."

Get Single Expensify File By ID

Tool Name: get_single_expensify_file_by_id

Used in tandem with the export tool, this retrieves a downloaded file generated by reports.export or reconciliations.create. The LLM passes the filename received in the previous step.

"Fetch the contents of the file 'export_abc123.json' that we just generated from the reports export."

Expensify Employees Bulk Update

Tool Name: expensify_employees_bulk_update

This powerful administrative tool utilizes Expensify's Advanced Employee Updater. It allows the agent to add, update, or remove policy members in bulk, rather than making dozens of individual API calls.

"Add bob@company.com and charlie@company.com to the 'Sales Travel' policy. Set their approval limits to $500."

Create an Expensify Reconciliation

Tool Name: create_a_expensify_reconciliation

Restricted to domain admins, this tool generates a company-card reconciliation file for a domain. It requires a FreeMarker template string to format the output. The LLM handles constructing the template and passing it into the payload.

"Generate a company card reconciliation file for the last 30 days. Format the output as a basic CSV using a FreeMarker template with columns for Date, Merchant, and Amount."

To view the complete inventory of available Expensify operations, schemas, and required parameters, visit the Truto Expensify Integration page.

Workflows in Action

When ChatGPT is equipped with Truto MCP tools, it can orchestrate complex, multi-step Expensify processes entirely autonomously.

Use Case 1: End-of-Month Asynchronous Expense Extraction

Finance teams need structured data for their internal ledgers. Because Expensify uses an async export pattern, the agent must sequence its operations correctly.

"Run the expense export for all 'Approved' reports from the last 30 days on our primary policy. Once the job starts, retrieve the file contents and summarize the top three highest spending categories."

Step-by-step Execution:

  1. list_all_expensify_policies: ChatGPT fetches the list of workspaces to identify the ID for the "primary policy".
  2. expensify_reports_export: The agent triggers the export job, passing the policy ID and the 'Approved' status filter. Expensify returns a fileName (e.g., finance_export_882.json).
  3. get_single_expensify_file_by_id: The agent immediately calls this tool, passing finance_export_882.json to download the actual financial payload.
  4. Analysis: ChatGPT reads the returned JSON array, calculates the totals per category, and outputs the final summary to the user.
sequenceDiagram
    participant User
    participant Agent as ChatGPT (Agent)
    participant MCP as Truto MCP Server
    participant API as Expensify API

    User->>Agent: "Run the expense export..."
    Agent->>MCP: Call list_all_expensify_policies
    MCP->>API: GET policies
    API-->>MCP: Policy list
    MCP-->>Agent: Returns policy ID
    
    Agent->>MCP: Call expensify_reports_export (policyID)
    MCP->>API: POST /reports/export
    API-->>MCP: { "fileName": "finance_export_882.json" }
    MCP-->>Agent: Returns fileName
    
    Agent->>MCP: Call get_single_expensify_file_by_id (fileName)
    MCP->>API: GET /files/finance_export_882.json
    API-->>MCP: Full JSON payload
    MCP-->>Agent: Returns financial data
    Agent->>User: "Here is the summary of the top three categories..."

Use Case 2: Employee Onboarding and Rule Enforcement

IT and HR admins frequently need to onboard users and enforce strict corporate spending rules simultaneously.

"We just hired a new sales rep, sarah@company.com. Add her to the 'Global Sales' policy and immediately create a rule that requires a receipt for any of her expenses over $25."

Step-by-step Execution:

  1. list_all_expensify_policies: ChatGPT retrieves the ID for the 'Global Sales' policy.
  2. expensify_employees_bulk_update: The agent constructs the bulk update payload, passing Sarah's email to attach her to the policy.
  3. create_a_expensify_expense_rule: The agent creates a specific compliance rule attached to Sarah's profile, configuring the payload to reject submissions lacking receipts if the amount exceeds 2500 cents ($25.00).

Security and Access Control

Exposing financial systems to LLMs requires strict governance. Truto MCP servers provide four layers of configuration to limit the blast radius of AI agents:

  • Method Filtering (config.methods): You can restrict the MCP server to only allow specific operations. For a reporting agent, passing ["read"] ensures the LLM can only query data and is structurally prevented from creating expenses or modifying users.
  • Tag Filtering (config.tags): If you only want the LLM to access policy data, you can pass ["policies"]. Tools related to domains or global configurations will be completely hidden from the agent's context.
  • API Token Auth (config.require_api_token_auth): By default, possessing the MCP URL grants access. By setting this flag to true, the client must also pass a valid Truto API token in the Authorization header, adding a required second layer of authentication.
  • Expiration (expires_at): You can set an ISO datetime for the MCP server to automatically self-destruct. This is ideal for granting a contractor or temporary automated agent 24-hour access to run an audit.

A Note on Expensify API Rate Limits

When building automated financial workflows, you must account for API rate limits.

Truto does not retry, throttle, or apply backoff on rate limit errors.

If ChatGPT executes tools rapidly and hits Expensify's rate limits, the upstream API will return an HTTP 429 status code. Truto passes this 429 error directly back to the caller in the JSON-RPC response. To simplify debugging across different vendors, Truto normalizes the upstream rate limit information into standard IETF headers:

  • ratelimit-limit: The total allowed requests in the current window.
  • ratelimit-remaining: The number of requests left.
  • ratelimit-reset: The time when the quota resets.

It is entirely the caller's responsibility to read these headers and implement the appropriate retry or backoff logic in their agent framework. Do not assume the integration layer will absorb traffic spikes for you.

Wrap-up

Integrating LLMs with financial platforms like Expensify fundamentally changes how teams handle expense tracking, reporting, and policy enforcement. However, manually building the middleware to handle dynamic JSON-RPC tool generation, authentication refreshes, and asynchronous export flows is a massive distraction from your core product.

By leveraging Truto's auto-generated MCP servers, you can connect ChatGPT to Expensify in minutes, applying strict method filters and secure scoping. Your agents get immediate, documented access to the exact API operations they need, and your engineering team avoids writing a single line of boilerplate integration code.

FAQ

How do I connect Expensify to ChatGPT?
You can connect Expensify to ChatGPT by generating a Model Context Protocol (MCP) server URL using Truto. This URL translates ChatGPT's JSON-RPC tool calls into authenticated REST API requests against Expensify.
Does Truto handle Expensify API rate limits automatically?
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. Truto normalizes the upstream rate limit information into standard headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset), and the client is responsible for implementing backoff logic.
Can I restrict which Expensify actions ChatGPT can perform?
Yes. When creating the Truto MCP server, you can use method filtering (e.g., limiting tools to 'read' operations only) and tag filtering to securely scope what the AI agent can access.

More from our Blog