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.

    POST
    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"
        }
      ]
    }'
    Response (201)
    {
      "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

    FieldNotes
    dateDate of the expense (YYYY-MM-DD). Required on create.
    paid_from_account_idRequired on create. A bank, credit card, or cash account; other account types return 422 invalid_paid_from_account.
    payment_sourcebank (default), card, or cash.
    lines1–100 lines of account_id and amount, with an optional description.
    vendor_id or vendorOptional, one or the other.
    reference, memoReceipt number and a note.
    external_idYour ID for the expense, stored for reference.
    posttrue 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.

    GET
    curl "https://api.helloledger.ai/v1/companies/{company_id}/expenses?from=2026-10-01&to=2026-10-31" \
      -H "Authorization: Bearer $HL_API_KEY"
    One expense · GET
    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.

    PATCH
    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"
    }'
    Post a draft · POST
    curl -X POST "https://api.helloledger.ai/v1/companies/{company_id}/expenses/66f1a2b3c4d5e6f708094c01/post" \
      -H "Authorization: Bearer $HL_API_KEY"
    POST
    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"
    }'
    Delete a draft · DELETE
    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.