[Cotlet](https://cotlet.dev/index.md) · [Integrations](https://cotlet.dev/integrations.md) · [How it works](https://cotlet.dev/how-it-works.md) · [Use cases](https://cotlet.dev/use-cases.md) · [Security](https://cotlet.dev/security.md) · [For developers](https://cotlet.dev/for-developers.md) · [For automation agencies](https://cotlet.dev/for-agencies.md) · [Blog](https://cotlet.dev/blog.md) · [API docs](https://cotlet.dev/docs.md) · [Help center](https://cotlet.dev/help.md) · [Changelog](https://cotlet.dev/changelog.md) · [Contact](https://cotlet.dev/contact.md) · [Pricing](https://cotlet.dev/pricing.md) · [Sign up](https://cotlet.dev/signup)

# Expensify API and MCP server

> Yes, but it is narrow (checked 14 Oct 2026). Expensify's Integration Server API creates expenses, creates reports (domain and policy admins, after enablement) and exports reports, with credentials you generate yourself. It has no job that submits or approves a report, and Expensify's own MCP server at expensify.com/mcp is read-only: it exposes a Search tool only.

Expense management app: employees and freelancers scan receipts, create expenses and submit reports, and approvers approve them in workspaces, with reimbursement and accounting exports behind them.

## What is the Expensify API?

Cotlet's Expensify API exposes 14 endpoints for your own Expensify account: list workspaces, get workspace, list reports, get report, list expenses, get expense, export report, create expense, attach receipt, create report, add expense to report, submit report, approve report and reject report. The list_workspaces endpoint returns workspaces your Expensify account belongs to, with your role in each. Each endpoint takes simple JSON inputs like report_id, workspace or expense_id and works both as an operation of the expensify_read or expensify_write MCP tool for agents and as a REST call for scripts.

- **Category:** [Invoicing & freelance CRM](https://cotlet.dev/integrations/category/invoicing-freelance-crm.md)
- **Endpoints:** 14 (7 read, 7 write)
- **Updated:** 2026-10-20

Yes, but it is narrow (checked 14 Oct 2026). Cotlet is a hosted MCP server and REST API for Expensify with 14 endpoints (7 read, 7 write): you sign in once in your own cloud browser, your AI agent does the work.

## Does Expensify have an official API?

Yes, but it is narrow (checked 14 Oct 2026). Expensify's Integration Server API creates expenses, creates reports (domain and policy admins, after enablement) and exports reports, with credentials you generate yourself. It has no job that submits or approves a report, and Expensify's own MCP server at expensify.com/mcp is read-only: it exposes a Search tool only.

| Task | Expensify official | Cotlet |
|---|---|---|
| Create an expense | Partly: Expense creator job; employeeEmail needs advanced permissions | Yes |
| Attach a receipt to an expense | No | Yes |
| Create a report | Partly: Report creator job: domain and policy admin, enabled on request | Yes |
| Submit a report for approval | No | Yes |
| Approve or reject a report | No: Report status updater only moves Approved reports to Reimbursed | Yes |
| Search and list expenses and reports | Partly: Official MCP Search is read-only; Report Exporter reads by ID or date range | Yes |
| Export reports as CSV or PDF | Yes: Report Exporter job, by report ID or date range of up to one year | Yes |

Sources: [Expensify blog: the Expensify MCP server (24 Jun 2026)](https://use.expensify.com/blog/expensify-mcp), [Expensify Integration Server: getting started](https://integrations.expensify.com/Integration-Server/doc/getting_started.html), [Expensify Integration Server: API reference](https://integrations.expensify.com/Integration-Server/doc/), [Expensify on Zapier: triggers and actions](https://zapier.com/apps/expensify/integrations)

## For AI agents

```text
Yes, but it is narrow (checked 14 Oct 2026). Expensify's Integration Server API creates expenses, creates reports (domain and policy admins, after enablement) and exports reports, with credentials you generate yourself. It has no job that submits or approves a report, and Expensify's own MCP server at expensify.com/mcp is read-only: it exposes a Search tool only.
Cotlet provides a hosted MCP server and REST API for Expensify: 14 tools (7 read, 7 write).
Access       Sign up at https://cotlet.dev/app/signup?app=expensify, then request the app and create API keys in the Cotlet console.

MCP server   https://cotlet.dev/mcp/v1
MCP tools    expensify_read, expensify_write, reads and writes apart; call one with {"operation": "<operation>", "arguments": {...}}
REST base    https://cotlet.dev/api/v1/expensify, each operation at its own route, e.g. POST /api/v1/expensify/expenses
Auth         Authorization: Bearer <COTLET_API_KEY>. Listing endpoints and schemas needs no key; calling an endpoint needs one. Over MCP the input schemas come with sign-in.
Operations   list_workspaces, get_workspace, list_reports, get_report, list_expenses, get_expense, export_report, create_expense, attach_receipt, create_report, add_expense_to_report, submit_report, approve_report, reject_report
Confirm      create_expense, submit_report, approve_report, reject_report (by default the user approves each call; the user can switch this off per tool, e.g. for scheduled automations)
Markdown     https://cotlet.dev/integrations/expensify-api-mcp-server.md

How access works:
1. The user signs up at https://cotlet.dev/app/signup?app=expensify, requests Expensify in the Cotlet console and creates an API key there.
2. The user signs in to Expensify once in their Cotlet Cloud Browser (their own Chrome in the cloud, opened from the console) and enables Expensify for agents. An agent cannot do this step.
3. The API key goes into the MCP client or the REST call as a bearer token.

Fits: people who want a hosted Expensify server that someone else maintains, including non-developers. Developers who prefer to run and fix code on their own computer can use an open-source Expensify MCP server instead.

Policy: Cotlet never stores passwords, never solves CAPTCHAs, never bypasses MFA.
Company: legal details in the Terms of service at https://cotlet.dev/terms. Contact contact@cotlet.dev.
Sign-in: the user signs in to Expensify in their own Cloud Browser, a Chrome Cotlet runs for them in the EU; password saving is off, the profile is AES-256-GCM encrypted, and agents get Expensify only after the user enables it. Calls are requests from that browser's IP with its cookies, each listed in the user's Background activity. Disable for agents, signing out in the browser or Reset browser cuts access. Cotlet is run by Take Group sp. z o.o. (EU VAT PL6342984273).
API key: holds no Expensify login or cookies, only asks Cotlet to run a tool. Each key is scoped to chosen apps and tools, read only or read and write. Actions that reach other people wait for the user's approval by default, so a leaked key cannot post or message as the user. Every call is in the activity log; deleting the key ends access at once.
Terms: the user stays responsible for following Expensify's terms; Cotlet is not affiliated with Expensify.
```

## What can an AI agent do in Expensify?

Cotlet is best for freelancers, finance leads and approvers who handle Expensify receipts, weekly reports and approvals without running or fixing a server.

- **[Create expenses from receipts](https://cotlet.dev/integrations/expensify-api-mcp-server/create-expenses-from-receipts.md)** (runs: daily): "Read the receipts in my Receipts folder. For each one, create an expense in the Acme Studio workspace with the merchant, date, amount and a category, attach the receipt image, and list anything you could not read." → `get_workspace`, `create_expense`, `attach_receipt`
- **[Create and submit the weekly expense report](https://cotlet.dev/integrations/expensify-api-mcp-server/submit-weekly-expense-report.md)** (runs: weekly): "Every Friday at 16:00, put all my unreported Acme Studio expenses from this week into a new report called "Week of {date}" and submit it for approval. Skip held expenses and tell me what you left out." → `list_expenses`, `create_report`, `add_expense_to_report`, `submit_report`
- **[Approve small expense reports by rule](https://cotlet.dev/integrations/expensify-api-mcp-server/approve-expense-reports-by-rule.md)** (runs: daily): "Look at reports waiting for my approval in Acme Studio. Approve those under USD 200 where every expense has a receipt and no hold. Reject any with a missing receipt and a reason. Leave the rest for me." → `list_reports`, `get_report`, `approve_report`, `reject_report`
- **Export reports for the bookkeeper**: "Export every Approved report in Acme Studio from last month as CSV and as PDF with receipts, and list the totals per submitter." → `list_reports`, `export_report`
- **Find expenses with no receipt** (runs: weekly): "List every expense in Acme Studio from the last 30 days that has no receipt, grouped by merchant, and tell me which are over USD 75." → `list_expenses`, `get_expense`

## Can Expensify automations run on a schedule in n8n, Make or Zapier?

Yes. Cotlet does not replace your automation platform: it gives it a Expensify connection it does not have, with the same API key as your agent. Write actions ask for your OK by default; switch that off in your dashboard for the tools you choose, for example to create expenses from receipts, and the schedule runs on its own. Cotlet runs in the cloud, so it keeps going while your computer is off.

| Where | How to connect | How to schedule |
|---|---|---|
| Your AI agent | Add https://cotlet.dev/mcp/v1 with your API key. | A scheduled agent run, for example a Claude Code routine. |
| n8n | MCP Client node with https://cotlet.dev/mcp/v1, or an HTTP Request node with the endpoint's method and URL, such as GET https://cotlet.dev/api/v1/expensify/workspace. | Schedule Trigger node. |
| Make | HTTP module: the endpoint's method and URL with an Authorization: Bearer header, and a JSON body for POST, PUT and PATCH. | Scenario schedule. |
| Zapier | Webhooks by Zapier, Custom Request: the same method, URL, header and body. | Schedule by Zapier trigger. |
| Your own script | GET https://cotlet.dev/api/v1/expensify/workspace with your API key. | cron or GitHub Actions. |

## Tools

**For AI agents:** Every endpoint and its schema is public. The 14 endpoints on this page, with parameters and example responses, can be listed without an account. Calling one returns 401 without an API key and 403 while the key has no access to Expensify.

### list_workspaces

Workspaces your Expensify account belongs to, with your role in each. Access: read. REST: `GET /api/v1/expensify/workspaces`. MCP: `expensify_read → list_workspaces`.

No parameters.

Returns each workspace with its name, ID, default currency and your role (admin, approver or member). Use the name or ID as the workspace parameter in the other tools. It shows only workspaces your account can open.

Example response:

```json
{
  "workspaces": [
    {
      "workspace_id": "W3A91C",
      "name": "Acme Studio",
      "currency": "USD",
      "role": "admin"
    },
    {
      "workspace_id": "W7D20F",
      "name": "Acme Contractors",
      "currency": "USD",
      "role": "member"
    }
  ]
}
```

### get_workspace

Categories, tags and approval setup of one workspace. Access: read. REST: `GET /api/v1/expensify/workspace`. MCP: `expensify_read → get_workspace`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `workspace` | string | yes | Workspace name or ID from list_workspaces, e.g. 'Acme Studio'. |

Returns the workspace categories and tags, whether an approval workflow is on, and your role. Read it before creating expenses so the category and tag values match what the workspace allows. Categories and tags you have no right to see come back empty.

Example response:

```json
{
  "workspace_id": "W3A91C",
  "name": "Acme Studio",
  "currency": "USD",
  "role": "admin",
  "approval_workflow": true,
  "categories": [
    "Meals",
    "Travel",
    "Software",
    "Office supplies"
  ],
  "tags": [
    "Northwind",
    "Internal"
  ]
}
```

### list_reports

Reports with status, total, submitter and approver, newest first. Access: read. REST: `GET /api/v1/expensify/reports`. MCP: `expensify_read → list_reports`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `workspace` | string | yes | Workspace name or ID from list_workspaces, e.g. 'Acme Studio'. |
| `status` | enum | no | draft, submitted, outstanding, approved, paid or done. Default: all. |
| `needs_my_approval` | boolean | no | Only reports waiting for your approval. Default: false. |
| `since` | date | no | Only reports created on or after this date. |
| `limit` | integer | no | 1 to 100. Default: 25. |
| `cursor` | string | no | Next page cursor from the previous call. |

Lists reports you can see in a workspace, newest first, with total, currency, number of expenses and whether any expense is on hold. Filter by status to find what waits for you. Statuses are the ones Expensify shows: Draft, Submitted, Outstanding, Approved, Paid and Done.

Example response:

```json
{
  "reports": [
    {
      "report_id": "R10077",
      "title": "Week of 5 Oct",
      "submitter": "Marcus Lee",
      "status": "Submitted",
      "total": 148.2,
      "currency": "USD",
      "expenses": 4,
      "has_hold": false,
      "created_at": "2026-10-09T16:02:00Z"
    },
    {
      "report_id": "R10042",
      "title": "Week of 12 Oct",
      "submitter": "You",
      "status": "Draft",
      "total": 612.9,
      "currency": "USD",
      "expenses": 7,
      "has_hold": true,
      "created_at": "2026-10-13T09:15:00Z"
    }
  ],
  "next_cursor": null
}
```

### get_report

One report with its expenses, receipts, holds and comments. Access: read. REST: `GET /api/v1/expensify/reports/{report_id}`. MCP: `expensify_read → get_report`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `report_id` | string | yes | Report ID from list_reports. |

Returns the report header and every expense on it with merchant, date, amount, category, whether a receipt is attached and whether it is on hold. Comments on the report come last, oldest first. Use it before approving or rejecting so the decision rests on the lines.

Example response:

```json
{
  "report_id": "R10077",
  "title": "Week of 5 Oct",
  "submitter": "Marcus Lee",
  "status": "Submitted",
  "total": 148.2,
  "currency": "USD",
  "expenses": [
    {
      "expense_id": "T7f3a01",
      "merchant": "Shell",
      "date": "2026-10-06",
      "amount": 62.2,
      "category": "Travel",
      "has_receipt": true,
      "on_hold": false
    },
    {
      "expense_id": "T7f3a02",
      "merchant": "Chipotle",
      "date": "2026-10-07",
      "amount": 38,
      "category": "Meals",
      "has_receipt": true,
      "on_hold": false
    },
    {
      "expense_id": "T7f3a03",
      "merchant": "Staples",
      "date": "2026-10-08",
      "amount": 48,
      "category": "Office supplies",
      "has_receipt": true,
      "on_hold": false
    }
  ],
  "comments": []
}
```

### list_expenses

Expenses with merchant, amount, category, receipt flag and report, newest first. Access: read. REST: `GET /api/v1/expensify/expenses`. MCP: `expensify_read → list_expenses`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `workspace` | string | yes | Workspace name or ID from list_workspaces, e.g. 'Acme Studio'. |
| `since` | date | no | Only expenses dated on or after this day. |
| `until` | date | no | Only expenses dated on or before this day. |
| `on_report` | boolean | no | true only expenses already on a report, false only loose ones. Default: both. |
| `has_receipt` | boolean | no | Filter by whether a receipt is attached. Default: both. |
| `limit` | integer | no | 1 to 100. Default: 50. |
| `cursor` | string | no | Next page cursor from the previous call. |

Lists expenses in your account, newest first. Filter by date, by whether they are on a report and by whether they have a receipt. Held expenses are marked, so you can leave them out of a report. Returns one currency per expense and never converts.

Example response:

```json
{
  "expenses": [
    {
      "expense_id": "T7f3a9c",
      "merchant": "Blue Bottle Coffee",
      "date": "2026-10-12",
      "amount": 42.5,
      "currency": "USD",
      "category": "Meals",
      "has_receipt": true,
      "on_hold": false,
      "report_id": null
    },
    {
      "expense_id": "T7f3a9d",
      "merchant": "Uber",
      "date": "2026-10-12",
      "amount": 31.2,
      "currency": "USD",
      "category": "Travel",
      "has_receipt": true,
      "on_hold": true,
      "report_id": null
    }
  ],
  "next_cursor": null
}
```

### get_expense

One expense with all fields, the receipt link and its report. Access: read. REST: `GET /api/v1/expensify/expenses/{expense_id}`. MCP: `expensify_read → get_expense`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `expense_id` | string | yes | Expense ID from list_expenses. |

Returns merchant, date, amount, currency, category, tags, description, reimbursable and billable flags, the receipt URL if one is attached and the report it sits on. Use it to check a single line before you change anything around it.

Example response:

```json
{
  "expense_id": "T7f3a9c",
  "merchant": "Blue Bottle Coffee",
  "date": "2026-10-12",
  "amount": 42.5,
  "currency": "USD",
  "category": "Meals",
  "tags": [],
  "description": "Coffee with client before the Northwind kickoff",
  "reimbursable": true,
  "billable": false,
  "receipt_url": "https://receipts.example.com/T7f3a9c.jpg",
  "report_id": null
}
```

### export_report

A report as CSV or PDF, with an expense-level or report-level template. Access: read. REST: `GET /api/v1/expensify/reports/{report_id}/export`. MCP: `expensify_read → export_report`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `report_id` | string | yes | Report ID from list_reports. |
| `format` | enum | yes | csv or pdf. |
| `template` | string | no | CSV template: basic, all_data_expense, all_data_report or the name of a custom template. Default: basic. |
| `include_receipts` | boolean | no | PDF only: include attached receipts. Default: true. |

Returns a download link for one report. CSV uses an export template (basic, all data at expense level, all data at report level or a custom one a workspace admin built); PDF includes the expenses, attached receipts and report notes. Large exports are prepared in the background, so the call may return a pending status first. It changes nothing in Expensify.

Example response:

```json
{
  "report_id": "R10077",
  "format": "csv",
  "template": "basic",
  "status": "ready",
  "download_url": "https://files.cotlet.dev/exports/R10077-basic.csv",
  "expires_at": "2026-10-15T09:00:00Z"
}
```

### create_expense

Creates an expense in a workspace with merchant, date, amount, currency and category. Access: write, asks the user first. REST: `POST /api/v1/expensify/expenses`. MCP: `expensify_write → create_expense`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `workspace` | string | yes | Workspace name or ID from list_workspaces, e.g. 'Acme Studio'. |
| `amount` | number | yes | Amount as a decimal number, e.g. 42.50. |
| `currency` | string | yes | Three-letter currency code, e.g. 'USD'. |
| `merchant` | string | yes | Merchant name as on the receipt. |
| `date` | date | yes | Date of the expense, YYYY-MM-DD. |
| `category` | string | no | A category of the workspace, from get_workspace. |
| `tags` | string[] | no | Tags of the workspace, from get_workspace. |
| `description` | string | no | Short note shown on the expense. |
| `reimbursable` | boolean | no | Default: true. |
| `billable` | boolean | no | Default: false. |

Creates one expense in your account, not on a report yet. Category and tags must exist in the workspace (read them with get_workspace), and the expense can be marked reimbursable or billable. Returns the new expense ID; add a receipt with attach_receipt and put it on a report with add_expense_to_report. By default you confirm each call; you can switch that off for this tool.

Example response:

```json
{
  "expense_id": "T7f3a9c",
  "workspace": "Acme Studio",
  "merchant": "Blue Bottle Coffee",
  "date": "2026-10-12",
  "amount": 42.5,
  "currency": "USD",
  "category": "Meals",
  "description": "Coffee with client before the Northwind kickoff",
  "reimbursable": true,
  "has_receipt": false,
  "report_id": null,
  "created_at": "2026-10-14T09:31:00+02:00"
}
```

### attach_receipt

Adds a receipt image or file to an expense, or replaces the one on it. Access: write. REST: `POST /api/v1/expensify/expenses/{expense_id}/receipt/attach`. MCP: `expensify_write → attach_receipt`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `expense_id` | string | yes | Expense ID from list_expenses. |
| `receipt_url` | string | yes | HTTPS URL of the receipt image or file. |
| `replace` | boolean | no | Replace an existing receipt. Default: false, which fails if one is attached. |

Attaches the file at the URL to the expense and returns the receipt link. Expensify posts an "added a receipt" message on the expense showing who made the change. It reaches no one and you can replace the file in Expensify, so it does not wait for your OK. On an Approved report only a Workspace Admin can attach or replace a receipt; otherwise the call returns the role problem.

Example response:

```json
{
  "expense_id": "T7f3a9c",
  "has_receipt": true,
  "receipt_url": "https://receipts.example.com/T7f3a9c.jpg",
  "replaced": false
}
```

### create_report

Creates an empty draft report in a workspace. Access: write. REST: `POST /api/v1/expensify/reports`. MCP: `expensify_write → create_report`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `workspace` | string | yes | Workspace name or ID from list_workspaces, e.g. 'Acme Studio'. |
| `title` | string | yes | Report title. |

Creates a Draft report on the workspace you name and returns its ID; nobody is notified until you submit it. Add expenses with add_expense_to_report and send it with submit_report. The draft stays editable and can be deleted in Expensify, so it does not wait for your OK.

Example response:

```json
{
  "report_id": "R10042",
  "title": "Week of 12 Oct",
  "workspace": "Acme Studio",
  "status": "Draft",
  "total": 0,
  "currency": "USD"
}
```

### add_expense_to_report

Puts an existing expense of yours on a draft report. Access: write. REST: `POST /api/v1/expensify/reports/{report_id}/expenses/{expense_id}/to-report`. MCP: `expensify_write → add_expense_to_report`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `report_id` | string | yes | Report ID from list_reports. |
| `expense_id` | string | yes | Expense ID from list_expenses. |

Adds one of your own expenses to a Draft report and returns the new total. Only expenses in your own account can be added, and an expense that is on hold is accepted but will move to a new Draft report if you submit with unheld ones. It reaches no one and can be undone in Expensify, so it does not wait for your OK.

Example response:

```json
{
  "report_id": "R10042",
  "expense_id": "T7f3a9c",
  "expenses": 1,
  "total": 42.5,
  "currency": "USD"
}
```

### submit_report

Submits a draft report to the workspace approval workflow. Access: write, asks the user first. REST: `POST /api/v1/expensify/reports/{report_id}/submit`. MCP: `expensify_write → submit_report`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `report_id` | string | yes | Report ID from list_reports. |

Sends the report to the approver set by the workspace. If the workspace has no approval workflow, Expensify shows Mark as done and the call returns status done instead of submitted. A report cannot be submitted when every expense is on hold or every transaction is a pending Expensify Card transaction. By default you confirm each call; you can switch that off for this tool.

Example response:

```json
{
  "report_id": "R10042",
  "status": "submitted",
  "approver": "Dana Reyes",
  "total": 612.9,
  "currency": "USD",
  "submitted_at": "2026-10-16T16:00:00+02:00"
}
```

### approve_report

Approves a submitted report you are the approver for. Access: write, asks the user first. REST: `POST /api/v1/expensify/reports/{report_id}/approve`. MCP: `expensify_write → approve_report`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `report_id` | string | yes | Report ID from list_reports. |
| `approve_held` | boolean | no | Approve the full amount even with held expenses. Default: false. |

Approves the report as the assigned approver, or as a Workspace Admin who may bypass the workflow. After the final approver the report becomes Approved and is ready for payment; this tool does not pay it. A report with held expenses is refused unless you pass approve_held. By default you confirm each call; you can switch that off for this tool.

Example response:

```json
{
  "report_id": "R10077",
  "status": "approved",
  "approved_amount": 148.2,
  "currency": "USD",
  "next_approver": null,
  "approved_at": "2026-10-14T09:40:00+02:00"
}
```

### reject_report

Sends a submitted report back to its submitter with a comment. Access: write, asks the user first. REST: `POST /api/v1/expensify/reports/{report_id}/reject`. MCP: `expensify_write → reject_report`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `report_id` | string | yes | Report ID from list_reports. |
| `comment` | string | yes | Reason shown to the submitter. |
| `return_to` | string | no | Email of the submitter or an earlier approver. Default: the submitter. |

Rejects the whole report and posts your comment, because Expensify requires one. The submitter can fix the report and resubmit. On a multi-level workflow, return_to chooses the submitter or an earlier approver. By default you confirm each call; you can switch that off for this tool.

Example response:

```json
{
  "report_id": "R10081",
  "status": "rejected",
  "returned_to": "Marcus Lee",
  "comment": "Chipotle USD 38.00 has no receipt. Please attach it and resubmit."
}
```

## How do I connect Expensify to Claude Code, Codex or Cursor?

Connecting Expensify takes three steps: sign up, sign in to Expensify once in your Cloud Browser and enable it for agents, then add one MCP server or call the REST API with the same key.

### Connect Expensify to your agent.

1. **Create an account** Sign up and add Expensify in the console. Your API key is there too.

2. **Sign in to Expensify in your Cloud Browser** Open your Cloud Browser in the console: your own Chrome in the cloud. Sign in to Expensify there yourself, the way you always do, then press Enable for agents, read only or read and write. We never save your password, and your agent keeps working even when your computer is off.

3. **Add Cotlet to your agent** Paste the snippet for your client. The same server and key serve every app you connect, so you do this once.

   Claude Code:

```
claude mcp add --transport http cotlet \
  https://cotlet.dev/mcp/v1 \
  --header "Authorization: Bearer $COTLET_API_KEY"
```

   Cursor:

```
{
  "mcpServers": {
    "cotlet": {
      "url": "https://cotlet.dev/mcp/v1",
      "headers": {
        "Authorization": "Bearer ${COTLET_API_KEY}"
      }
    }
  }
}
```

   Codex:

```
# ~/.codex/config.toml
[mcp_servers.cotlet]
url = "https://cotlet.dev/mcp/v1"
bearer_token_env_var = "COTLET_API_KEY"
```

4. **Ask your agent** Ask in plain words. Your agent finds the Expensify tools on the server and calls them for you; anything that reaches other people waits for your OK unless you switch that off for a tool.

```
Read the receipts in my Receipts folder. For each one, create an expense in the Acme Studio workspace with the merchant, date, amount and a category, attach the receipt image, and list anything you could not read.
```

### Call Expensify from your scripts.

1. **Export your API key** Your scripts use the same API key as your agent. Keep it in an environment variable, never in your code.

```
export COTLET_API_KEY="cotlet_..."
```

2. **Call a tool** Every tool is an endpoint under /api/v1/expensify with its own method and path, listed with the endpoints above. Path parameters go in the URL; the other parameters go in the query for GET and DELETE and in a JSON body otherwise.

```
curl -X POST https://cotlet.dev/api/v1/expensify/expenses \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"workspace":"Acme Studio","amount":42.5,"currency":"USD","merchant":"Blue Bottle Coffee","date":"2026-10-12","category":"Meals","description":"Coffee with client before the Northwind kickoff"}'
```

3. **Read the result** You get plain JSON back, ready for your script. A failed call costs nothing.

```
{
  "expense_id": "T7f3a9c",
  "workspace": "Acme Studio",
  "merchant": "Blue Bottle Coffee",
  "date": "2026-10-12",
  "amount": 42.5,
  "currency": "USD",
  "category": "Meals",
  "description": "Coffee with client before the Northwind kickoff",
  "reimbursable": true,
  "has_receipt": false,
  "report_id": null,
  "created_at": "2026-10-14T09:31:00+02:00"
}
```

## Why use Cotlet for Expensify?

**Built for agents**

- One MCP server for every app you connect.
- The same tools as a REST API for scripts and cron jobs.
- Works with Claude Code, Codex and Cursor.

**Your login stays yours**

- You sign in to Expensify yourself, in your own Cloud Browser. We never save your password; the profile is encrypted with AES-256-GCM, the cipher 1Password uses.
- A+ on Qualys SSL Labs, securityheaders.com and Mozilla Observatory.
- The model never sees a login.
- No stored passwords, no CAPTCHA solving, no MFA bypass.

**You stay in control**

- Scope each API key: its apps, its tools, read only or read and write.
- Anything that reaches other people waits for your OK, so a leaked key cannot post or message as you.
- Every background request listed in your Background activity. Disable for agents in one click.

**Kept working for you**

- We test every integration every 6 hours.
- When Expensify changes, the tool pauses until we fix it.
- Failed calls cost nothing.

## Is it safe to connect Expensify to Cotlet?

Yes. You sign in to Expensify yourself, in your own Cloud Browser, and we never save your password. Your agents only get Expensify once you enable it, and every background request is listed. The browser profile is stored encrypted with AES-256-GCM, the same cipher 1Password uses for its vaults and Apple for iCloud Keychain. Independent public scanners grade cotlet.dev A+ and find it clean; click a result to rerun it yourself.

- **A+ on Qualys SSL Labs**: Encryption in transit: TLS setup and certificates, on all four server addresses. [Live result](https://www.ssllabs.com/ssltest/analyze.html?d=cotlet.dev)
- **A+ on securityheaders.com**: Browser protections sent with every page, all six headers present. [Live result](https://securityheaders.com/?q=cotlet.dev&followRedirects=on)
- **A+ on Mozilla Observatory**: Mozilla's web security baseline: score 120 of 100, 12 of 12 tests passed. [Live result](https://developer.mozilla.org/en-US/observatory/analyze?host=cotlet.dev)
- **Clean on Sucuri SiteCheck**: No malware, spam or defacement, and not on any of 9 blocklists, Google Safe Browsing included. [Live result](https://sitecheck.sucuri.net/results/cotlet.dev)

[How Cotlet protects your access](https://cotlet.dev/security.md)

### Common concerns

**Who runs Cotlet?**

Cotlet is run by Take Group, a Polish software company working since 2019. Take Group runs about 3,200 websites on its own systems and has its own DevOps team for infrastructure security. We built and used integrations like this inside the company for years before opening them as Cotlet.

**Is this an official Expensify integration?**

No, Cotlet is independent of Expensify. Expensify's own API is too limited for this work. Cotlet works through your own Expensify account, the way you use Expensify in the browser, so you need no partner approval and no developer setup.

**What about client data and GDPR?**

Cotlet is run from the EU and follows the GDPR. Cloud Browsers run in Finland, a Tier 1 country in the ITU Global Cybersecurity Index 2024. Data passes through a call and is not kept after it.

**Can I start with read only?**

Yes. Enable the app for agents as read only, or make the API key read only, and anything that reaches other people waits for your OK unless you switch that off per tool.

**Is it reliable?**

Every Cotlet service is checked every 3 minutes from Europe, North America, Asia and Australia. The live results and any outage are public at status.cotlet.dev.

**How do I stop?**

Press Disable for agents next to Expensify in your console: your agents lose access at once and you stay signed in. Signing out of Expensify in the browser works too, and Reset browser deletes the whole profile.

## Is there a Expensify MCP server on GitHub?

Yes. Community Expensify MCP servers exist on GitHub, usually maintained by one person. You install one on your own computer, add your own API credentials, and keep it running yourself; they follow the Integration Server API, so they stop at what that API can do and break when it changes. Cotlet hosts a server that works in the Expensify app itself, including submit and approve, and our team keeps it working.

|  | GitHub MCP server | Cotlet |
|---|---|---|
| Setup | Clone the repo, generate partner credentials, install it, add it to each MCP client | Sign up, sign in to Expensify once, add one MCP server or API key |
| When Expensify changes | It breaks until you or the maintainer fix it | Our team tests it daily and fixes it for you; failed calls are free |
| Your Expensify session | API credentials in a file on your computer, readable by any program you run | Signed in inside your own Cloud Browser in the EU, profile encrypted, only for the apps you enable, disabled for agents in one click |
| When the session expires | Tools fail until you update the credentials or sign in again by hand | Your Cloud Browser keeps you signed in; if Expensify logs you out, your agent gets a link to sign in again |
| Upkeep | You update, monitor and debug it yourself | Nothing to host or update |
| Where it runs | Your computer, while it is on | Hosted: Claude Code, Codex, Cursor, n8n and scripts, also on a schedule |
| Price | Free, paid for with your time | Free 7-day trial, then pay-as-you-go credit packs, no subscription |

### Which one fits you

**Pick Cotlet if**

- you are not a developer, or you do not want to maintain a server
- you need it to keep working when Expensify changes, without checking it yourself
- your agent or a script should run it on a schedule, also while your computer is off

**Pick an open-source server if**

- you are comfortable installing, updating and fixing it yourself
- you want your sign-in to stay on your own computer
- you only need it now and then, from one MCP client

## How much does the Expensify API cost?

Free 7-day trial with 2,000 credits, no card needed. After that, the Expensify API is pay as you go with no limit on apps: Credit packs from $29 for 5,000 credits, one-time, no subscription. A successful call uses 10 credits (about $0.04 to $0.06), your Cloud Browser uses 50 credits a day while it has an app enabled for agents, however many apps that is, and an agent driving the browser itself uses 5 credits a minute. Credit packs stay valid for 12 months and stack, so unused credits carry over.

| Credits | Price, one time | Calls at 10 credits | Per 1,000 credits |
|---|---|---|---|
| 5,000 | $29 | 500 | $5.80 |
| 15,000 | $79 | 1,500 | $5.27 |
| 32,000 | $149 | 3,200 | $4.66 |
| 60,000 | $249 | 6,000 | $4.15 |

Only successful calls use credits; failed calls and timeouts cost nothing. At a balance of 0 calls pause, never a surprise charge. Authenticate with the `Authorization: Bearer` header. [See full pricing](https://cotlet.dev/pricing.md)

## How Cotlet holds your access

Cotlet hosts MCP servers and REST APIs for apps with no usable API. Your agent works in your own Expensify account.

- **Company details:** Cotlet is run by Take Group sp. z o.o., a company registered in Poland. Its full address and registration numbers are in the Terms of service. [Terms of service](https://cotlet.dev/terms)
- **Your sign-in:** You sign in to Expensify yourself, in your own Cloud Browser: a Chrome we run for you in the EU, one per app. We never save your password (password saving is off), the profile is stored encrypted with AES-256-GCM, and your agents get Expensify only after you enable it. When Expensify signs you out, your agent gives you a link to sign in again. [Security](https://cotlet.dev/security.md)
- **Revoking access:** Press Disable for agents next to Expensify, sign out of Expensify in your Cloud Browser, or reset the browser. Every background request is listed in your Background activity. Actions that reach other people wait for your OK unless you switch that off per tool.
- **What we never do:** Store passwords, solve CAPTCHAs or bypass MFA.
- **Expensify's terms:** You use your own Expensify account and stay responsible for following Expensify's terms. Cotlet is independent and not affiliated with Expensify. [Terms](https://cotlet.dev/terms)
- **Contact:** Questions about the company, security or an app: [contact@cotlet.dev](mailto:contact@cotlet.dev)

## Frequently asked questions

**How do I get access?**

Sign up free, open Expensify in the console and press Request access. Then you create your API key and connect Expensify there.

**Do I need to be technical?**

No. You sign in to Expensify once in your Cloud Browser, a normal Chrome you open from the console, and paste one command into your agent, or one URL into n8n. Your agent can walk you through it. There is no server to install, update or fix.

**Is it safe to connect Expensify to Cotlet?**

Yes. You sign in to Expensify yourself, in your own Cloud Browser in the EU, and we never save your password. Your agents only get Expensify once you enable it, every background request is listed, and the browser profile is stored encrypted with AES-256-GCM, the cipher 1Password uses for its vaults. The AI model never sees a login, posts and messages wait for your OK, and Disable for agents cuts access in one click. Independent scanners grade cotlet.dev A+ on Qualys SSL Labs, securityheaders.com and Mozilla Observatory; live results are on https://cotlet.dev/security.

**Is a hosted server safer than a local one? My Expensify sign-in would leave my computer.**

On Cotlet you sign in inside your own Cloud Browser, which only you open, and its profile is stored encrypted with AES-256-GCM in the EU. Agents get only the apps you enable, and every background request is listed. A local server keeps your Expensify cookie in a plain file on your computer, readable by any program you run, and stops when your computer is off.

**Why does Cotlet send requests instead of clicking in my browser?**

To be fast, Cotlet doesn't click through Expensify's pages. It sends the same requests your browser would, from your browser's IP with its cookies, without opening a window. Expensify sees the same browser and IP you signed in with, and every request is listed in your Background activity.

**What if someone gets my Cotlet API key?**

The key holds no Expensify login or cookies, it only asks Cotlet to run a tool. It can call only the apps and tools you gave it, and a read-only key changes nothing. Anything that reaches other people, like posts, comments or messages, waits for your OK in the dashboard by default, so a leaked key cannot post or message as you. When you switch approval off for a scheduled job, give that job its own key limited to that one tool. Every call is in your activity log, and deleting the key ends access at once.

**Why a signed-in browser and not OAuth, like other integration platforms?**

Expensify offers no OAuth token you can simply grant for these tools: its official API leaves them out or needs a developer app, a plan or an approval first (see the official API section above). That is why Cotlet sets the limits itself: per key, per tool, with your approval before anything reaches other people. Cotlet is run by Take Group sp. z o.o., an EU company (VAT PL6342984273).

**Do I need to be a workspace admin?**

No for creating expenses and submitting your own reports: any member can. Approving needs you to be the assigned approver, or a Workspace Admin who bypasses the workflow, and Expensify blocks an admin from approving their own report when Prevent Self-Approval is on.

**Why not use Expensify's own MCP server?**

Expensify's MCP server at expensify.com/mcp exposes a Search tool only, and Expensify states that access is read-only at launch (24 Jun 2026). It cannot create expenses, submit or approve reports, so a job that changes anything still needs another route.

**Can my agent post or send messages in Expensify without asking me?**

No. Every call to create_expense, submit_report, approve_report or reject_report waits for your OK by default. For scheduled automations you can switch confirmation off per tool in your dashboard, for example only for approvals. attach_receipt, create_report, add_expense_to_report runs without asking, because it sends nothing to anyone and one call undoes it.

**Can I use the Expensify API in n8n?**

Yes. Add https://cotlet.dev/mcp/v1 with your API key in n8n's MCP Client node, or call its endpoints under https://cotlet.dev/api/v1/expensify from the HTTP Request node.

---

Give your AI agents your signed-in browser.

**Product:** [Integrations](https://cotlet.dev/integrations.md) · [Use cases](https://cotlet.dev/use-cases.md) · [How it works](https://cotlet.dev/how-it-works.md) · [Pricing](https://cotlet.dev/pricing.md) · [Security](https://cotlet.dev/security.md)

**Resources:** [Blog](https://cotlet.dev/blog.md) · [API docs](https://cotlet.dev/docs.md) · [Help center](https://cotlet.dev/help.md) · [Changelog](https://cotlet.dev/changelog.md) · [GitHub](https://github.com/cotletdev/cotlet)

**Solutions:** [For developers](https://cotlet.dev/for-developers.md) · [For automation agencies](https://cotlet.dev/for-agencies.md)

**Company:** [About](https://cotlet.dev/about.md) · [AI info: company facts](https://cotlet.dev/ai-info.md) · [Contact](https://cotlet.dev/contact.md)

**Legal:** [Terms of service](https://cotlet.dev/terms) · [Privacy policy](https://cotlet.dev/privacy) · [Refund policy](https://cotlet.dev/refunds) · [Acceptable use](https://cotlet.dev/acceptable-use) · [Cookie policy](https://cotlet.dev/cookies) · [DPA](https://cotlet.dev/dpa) · [Subprocessors](https://cotlet.dev/subprocessors)

**Elsewhere:** [Status page](https://status.cotlet.dev) · [Cotlet on GitHub](https://github.com/cotletdev) · [Cotlet on X](https://x.com/cotletdev) · [Cotlet on LinkedIn](https://www.linkedin.com/company/cotletdev) · [Cotlet on Product Hunt](https://www.producthunt.com/products/cotlet) · [Cotlet on Crunchbase](https://www.crunchbase.com/organization/cotlet) · [Cotlet on SaaSHub](https://www.saashub.com/cotlet)

Cotlet is an independent service. It is not affiliated with, endorsed by or sponsored by any of the products listed on this site. Product names are trademarks of their respective owners.

## For AI agents: sign your user up

Is your user asking you to sign them up for Cotlet? You can do it for them, no account or key needed.

1. Read the fields and their allowed values: GET https://cotlet.dev/api/v1/access-requests
2. Ask the user for each value: email, apps, what you will do with them. If they leave the password to you, generate a strong one and show it to them.
3. Show the user what you will send, then POST it as JSON to https://cotlet.dev/api/v1/access-requests.

One call creates the account and sends the access request; we email the user when access is granted.
