Expense claims
The expense-claim endpoints let staff file out-of-pocket expenses through an integration or an AI assistant, attach the receipt, and — when the connected user can approve — approve their own claim. The final approval creates a supplier bill and posts it to the ledger, exactly like approving in AccountFUN. A base-currency claim’s bill is posted straight away; a foreign-currency claim’s bill is left as a draft so finance can set the exchange rate before posting.
Reading and creating claims requires expense_claims:read /
expense_claims:write; approving posts to the books, so it needs its own
scope, expense_claims:approve, and the authorizing user’s role must have the
Approve Expense Claims permission (the built-in Admin and Accountant roles
have it by default; Standard, Sales and Purchase don’t). See
Authentication.
Endpoints
Section titled “Endpoints”| Method | Path | Scope | Description |
|---|---|---|---|
GET | /api/v1/expense-claims | expense_claims:read | List claims (paginated). A role without Approve Expense Claims sees only its own. |
POST | /api/v1/expense-claims | expense_claims:write | Create a draft claim. |
POST | /api/v1/expense-claims/{id}/approve | expense_claims:approve | Approve your own claim (a draft is submitted first). |
GET | /api/v1/expense-claims/{id}/attachments | expense_claims:read | List the receipts attached to a claim. |
POST | /api/v1/expense-claims/{id}/attachments | expense_claims:write | Attach receipts to a claim. |
Related lookups you will usually need: contacts (the supplier) and the chart of accounts (the expense account). AI assistants file and approve claims through the MCP server.
List expense claims
Section titled “List expense claims”Returns a paginated list for the token’s organization, newest first. Summary
fields only. A role without the Approve Expense Claims permission is
scoped to the claims it filed itself; the claimantId filter is ignored for
those roles.
Request
Section titled “Request”curl "https://app.accountfun.com/api/v1/expense-claims?status=SUBMITTED" \ -H "Authorization: Bearer $ACCESS_TOKEN"Query parameters
Section titled “Query parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter by status: DRAFT, SUBMITTED, APPROVED, PAID, DECLINED, or VOIDED. |
claimantId | string | — | Only claims for a specific member. Ignored by a role restricted to its own claims. |
contactId | string | — | Only claims spent with a specific supplier. |
page | integer | 1 | Page number (1-based, max 10000). |
limit | integer | 25 | Page size (max 200). |
Response — 200 OK
Section titled “Response — 200 OK”{ "expenseClaims": [ { "id": "9b1e4c2a-...", "claimNumber": "EC-0007", "claimantId": "...", "status": "SUBMITTED", "description": "Taxi to client meeting", "spentAt": "2026-09-15", "total": "42.5000", "currency": "HKD", "billId": null, "createdAt": "2026-10-07T03:12:44.120Z", "contact": { "id": "...", "name": "City Taxi" } } ], "pagination": { "page": 1, "limit": 25, "total": 1, "totalPages": 1 }}Create a claim
Section titled “Create a claim”Creates a draft claim. It is not posted to the books: the ledger only moves when the claim reaches final approval and AccountFUN creates the linked supplier bill (posted immediately for a base-currency claim, or left as a draft for a foreign-currency one).
A staff role always files for itself — a claimantId other than the token
user is ignored. A role with Approve Expense Claims may set claimantId to
another member to file on their behalf, in which case that claim can only be
approved inside AccountFUN.
Request
Section titled “Request”curl -X POST https://app.accountfun.com/api/v1/expense-claims \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "claimantId": "00000000-0000-0000-0000-000000000001", "description": "Taxi to client meeting", "spentAt": "2026-09-15", "contactId": "00000000-0000-0000-0000-0000000000c1", "accountId": "00000000-0000-0000-0000-0000000000a1", "amount": "42.50", "taxMode": "inclusive" }'Body parameters
Section titled “Body parameters”| Field | Type | Required | Description |
|---|---|---|---|
claimantId | string | Yes | The member the claim is for. A staff role may only claim for itself. |
description | string | Yes | What the expense was for. Max 2000 characters. |
spentAt | date | Yes | The date the expense was incurred, "YYYY-MM-DD". |
contactId | string | Yes | The supplier the money was spent with. Must be an active contact in the token’s organization. |
accountId | string | Yes | The expense account to code the claim to. Must be an account in the token’s organization. |
amount | string | Yes | The amount, greater than zero. |
taxMode | string | Yes | exclusive (amount excludes tax) or inclusive (amount includes tax). |
taxRateId | string | No | An active tax rate from GET /api/v1/tax-rates. Omit, null, or "" for no tax. |
currency | string | No | 3-letter ISO 4217 code. Defaults to the organization’s base currency. Must be enabled. |
spentAt may also be sent as an ISO 8601 datetime; the calendar date as
written is used.
Response — 201 Created
Section titled “Response — 201 Created”The created claim in the same shape as the API detail object, including its
claimNumber and computed subtotal / taxAmount / total.
{ "expenseClaim": { "id": "9b1e4c2a-...", "claimNumber": "EC-0007", "claimantId": "...", "status": "DRAFT", "description": "Taxi to client meeting", "spentAt": "2026-09-15", "amount": "42.5000", "taxMode": "inclusive", "subtotal": "42.5000", "taxAmount": "0.0000", "total": "42.5000", "currency": "HKD", "billId": null, "contact": { "id": "...", "name": "City Taxi" }, "account": { "id": "...", "code": "493", "name": "Travel - National" }, "taxRate": null, "...": "..." }}Errors
Section titled “Errors”| Status | Cause |
|---|---|
400 | Invalid body — the details object names each bad field, including any id that isn’t a valid id or an amount that isn’t positive. |
400 | The supplier is deactivated, the currency isn’t enabled, or the account can’t be used for an expense claim (a bank, receivable, asset or system control account — choose an expense account). |
403 | The supplier, account or tax rate doesn’t belong to this organization, the claimant isn’t a member, or the spent-at date falls on or before the organization’s lock date. |
403 | The role lacks the Create Expense Claims permission. |
Approve a claim
Section titled “Approve a claim”Approves the caller’s own claim. For a draft claim the API submits it to its approval chain and records the decision in the same atomic call; for a claim already submitted, it records the decision only. When that approval completes the chain, AccountFUN creates the linked supplier bill — posted straight away for a base-currency claim. A foreign-currency claim is posted with the system exchange rate, or left as a draft for finance to check or amend the rate before posting, when the organization turns off its Post foreign-currency claims automatically setting (see Settings).
Requires the expense_claims:approve scope and the Approve Expense Claims
permission. No body.
curl -X POST https://app.accountfun.com/api/v1/expense-claims/9b1e4c2a-.../approve \ -H "Authorization: Bearer $ACCESS_TOKEN"Response — 200 OK
Section titled “Response — 200 OK”{ "expenseClaimId": "9b1e4c2a-...", "final": true, "outcome": "completed", "billId": "7c0e5a1f-...", "journalEntryId": "...", "postingDeferred": false}| Field | Meaning |
|---|---|
final | true when the decision completed the chain and created the bill. |
outcome | completed (the claim is approved) or advanced (it moved to the next approval level). |
billId | Present only when the decision finalized the claim. |
journalEntryId | The posted ledger entry — present when the bill was posted (base-currency claims, or foreign-currency claims when the organization auto-posts them). Absent when the bill was left as a draft for finance to set the exchange rate. |
postingDeferred | true when the bill was created as a draft for finance to set the exchange rate (a foreign-currency claim the organization does not auto-post). The claim is still APPROVED. |
Errors
Section titled “Errors”| Status | Cause |
|---|---|
403 | insufficient_scope — the token doesn’t carry expense_claims:approve, or the user’s role doesn’t have the Approve Expense Claims permission. |
403 | The claim was filed by another member — API/MCP approval is limited to your own claims. Approve it in AccountFUN. |
403 | Separation of duties — you can’t approve your own claim unless you’re an admin or the organization’s only approver. |
404 | No claim with that id in the token’s organization. |
409 | The claim isn’t in a decidable state, or the organization’s Accounts Payable / tax system accounts are missing. |
Attach receipts
Section titled “Attach receipts”Attach one or more files to a claim. File types, size limits and virus scanning follow the same rules as attaching receipts to a bill: PDF, PNG, JPEG, XLSX, XLS, DOCX, DOC, CSV; 5 MB per file, 25 MB per request, 10 attachments per claim, and the organization’s storage quota. Only the claimant can change a claim’s receipts.
Multipart upload
Section titled “Multipart upload”curl -X POST https://app.accountfun.com/api/v1/expense-claims/9b1e4c2a-.../attachments \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -F "file=@taxi-receipt.pdf"JSON upload (base64)
Section titled “JSON upload (base64)”curl -X POST https://app.accountfun.com/api/v1/expense-claims/9b1e4c2a-.../attachments \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"files\": [{\"fileName\": \"taxi-receipt.jpg\", \"contentBase64\": \"$(base64 -w0 taxi-receipt.jpg)\"}]}"Response — 201 Created
Section titled “Response — 201 Created”{ "attachments": [ { "id": "...", "originalName": "taxi-receipt.pdf", "mimeType": "application/pdf", "fileSize": 48213, "createdAt": "2026-10-07T03:13:10.004Z" } ]}Rejected files come back in a warnings array alongside the stored ones.
List attachments
Section titled “List attachments”curl https://app.accountfun.com/api/v1/expense-claims/9b1e4c2a-.../attachments \ -H "Authorization: Bearer $ACCESS_TOKEN"Returns { "attachments": [ … ] } oldest first, each with id,
originalName, mimeType, fileSize, scanStatus, and createdAt.
Claim statuses
Section titled “Claim statuses”| Status | Meaning |
|---|---|
DRAFT | Saved, not submitted. |
SUBMITTED | Moving through the approval chain. |
APPROVED | Final approval reached — a linked supplier bill was created (posted immediately for a base-currency claim, or left as a draft for a foreign-currency one). |
PAID | The linked bill has been paid. |
DECLINED | An approver rejected it. |
VOIDED | Cancelled. |
Over the API you can create a claim and approve your own; editing, declining, voiding and reimbursing are done inside AccountFUN.