Bookkeeping
Expenses
Money the company already spent by bank, card, or cash: receipts, card charges, and direct debits.
Record an expense
Scope: expenses:write. paid_from_account_id is the bank, credit card, or cash account the money came out of. Each line names an expense account and an amount. The vendor (merchant) is optional and is matched by name or created.
With "post": true the expense is recorded right away (status recorded): each line's account is debited and the paid-from account is credited. Without it the expense is saved as a draft.
curl -X POST "https://api.helloledger.ai/v1/companies/{company_id}/expenses" \
-H "Authorization: Bearer $HL_API_KEY" \
-H "Idempotency-Key: exp_9910" \
-H "Content-Type: application/json" \
-d '{
"vendor": {
"name": "Uber"
},
"date": "2026-10-02",
"payment_source": "card",
"paid_from_account_id": "66f1a2b3c4d5e6f708091d04",
"reference": "rcpt-20261002-1",
"memo": "Client visit",
"external_id": "exp_9910",
"post": true,
"lines": [
{
"account_id": "66f1a2b3c4d5e6f708091d01",
"description": "Ride to client",
"amount": "45.50"
}
]
}'{
"expense_id": "66f1a2b3c4d5e6f708094c01",
"external_id": "exp_9910",
"vendor_id": "66f1a2b3c4d5e6f708092a02",
"vendor_name": "Uber",
"vendor_created": true,
"reference": "rcpt-20261002-1",
"date": "2026-10-02",
"status": "recorded",
"posted": true,
"payment_source": "card",
"paid_from_account_id": "66f1a2b3c4d5e6f708091d04",
"total_amount": "45.50",
"currency": "USD",
"memo": "Client visit",
"lines": [{ "account_id": "66f1a2b3c4d5e6f708091d01", "account_name": "Office Supplies", "description": "Ride to client", "amount": "45.50" }],
"result": "posted"
}Fields
| Field | Notes |
|---|---|
| date | Date of the expense (YYYY-MM-DD). Required on create. |
| paid_from_account_id | Required on create. A bank, credit card, or cash account; other account types return 422 invalid_paid_from_account. |
| payment_source | bank (default), card, or cash. |
| lines | 1–100 lines of account_id and amount, with an optional description. |
| vendor_id or vendor | Optional, one or the other. |
| reference, memo | Receipt number and a note. |
| external_id | Your ID for the expense, stored for reference. |
| post | true records it now. Default false saves a draft. |
List and read expenses
Scope: expenses:read or expenses:write. Filter by status (draft, recorded, voided), vendor_id, external_id, and a from/to date range.
curl "https://api.helloledger.ai/v1/companies/{company_id}/expenses?from=2026-10-01&to=2026-10-31" \
-H "Authorization: Bearer $HL_API_KEY"curl "https://api.helloledger.ai/v1/companies/{company_id}/expenses/66f1a2b3c4d5e6f708094c01" \
-H "Authorization: Bearer $HL_API_KEY"Edit, post, void, or delete
PATCH changes a draft or a recorded expense; a recorded one is re-posted with the new amounts. Voiding reverses the ledger. DELETE removes drafts only.
An expense matched to a bank transaction returns 409 expense_matched on edit or void. Unmatch it in HelloLedger first.
curl -X PATCH "https://api.helloledger.ai/v1/companies/{company_id}/expenses/66f1a2b3c4d5e6f708094c01" \
-H "Authorization: Bearer $HL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"payment_source": "bank",
"paid_from_account_id": "66f1a2b3c4d5e6f708091d03"
}'curl -X POST "https://api.helloledger.ai/v1/companies/{company_id}/expenses/66f1a2b3c4d5e6f708094c01/post" \
-H "Authorization: Bearer $HL_API_KEY"curl -X POST "https://api.helloledger.ai/v1/companies/{company_id}/expenses/66f1a2b3c4d5e6f708094c01/void" \
-H "Authorization: Bearer $HL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "Personal charge"
}'curl -X DELETE "https://api.helloledger.ai/v1/companies/{company_id}/expenses/66f1a2b3c4d5e6f708094c01" \
-H "Authorization: Bearer $HL_API_KEY"Employee reimbursements
Expenses employees paid personally and claim back go through approval in HelloLedger. The API doesn't create or change them; trying returns 409 unsupported_expense_type. They still show in GET /expenses.