Skip to content

First chart of accounts

This walks through setting up a chart of accounts on a fresh SAE Books instance, posting an opening balances journal, and confirming the result in the trial balance.

You'll need:

  • A running SAE Books instance (quickstart)
  • A bearer token for your tenant (auth)
export SAEBOOKS_API_BASE=https://app.saebooks.com.au/api/v1
export JWT=...   # from POST /auth/login

1. Decide the structure

The chart is hierarchical, keyed by code. The top-level shape is fixed (the account types — ASSET, LIABILITY, EQUITY, INCOME, OTHER_INCOME, EXPENSE, COST_OF_SALES, OTHER_EXPENSE); below that, codes and grouping are yours.

A common Australian SME structure:

Code Name Type
1100 Cash at bank ASSET
1200 Trade debtors ASSET
1500 Inventory ASSET
1700 Plant & equipment ASSET
2100 Trade creditors LIABILITY
2300 GST payable LIABILITY
3100 Owner equity EQUITY
4000 Sales INCOME
5000 Cost of goods sold COST_OF_SALES
6000 Operating expenses EXPENSE
6100 Wages & salaries EXPENSE

You don't have to commit upfront — you can add accounts later. But the codes are sticky in the sense that reports order on them, so pick a numbering scheme you'll be happy with in a year.

2. Seed the chart

There is no bulk-create endpoint. Post accounts one at a time to POST /api/v1/accounts. The account_type values are uppercase enum strings; tax_code_default sets the default tax code for a line posted to that account (use the tax code string, e.g. "GST"):

curl -X POST "$SAEBOOKS_API_BASE/accounts" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"code":"1100","name":"Cash at bank","account_type":"ASSET"}'

curl -X POST "$SAEBOOKS_API_BASE/accounts" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"code":"1200","name":"Trade debtors","account_type":"ASSET"}'

curl -X POST "$SAEBOOKS_API_BASE/accounts" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"code":"2100","name":"Trade creditors","account_type":"LIABILITY"}'

curl -X POST "$SAEBOOKS_API_BASE/accounts" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"code":"2300","name":"GST payable","account_type":"LIABILITY","tax_code_default":"GST"}'

curl -X POST "$SAEBOOKS_API_BASE/accounts" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"code":"3100","name":"Owner equity","account_type":"EQUITY"}'

curl -X POST "$SAEBOOKS_API_BASE/accounts" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"code":"4000","name":"Sales","account_type":"INCOME","tax_code_default":"GST"}'

curl -X POST "$SAEBOOKS_API_BASE/accounts" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"code":"6000","name":"Operating expenses","account_type":"EXPENSE","tax_code_default":"GST"}'

Each call returns the created account with its UUID. Save the UUIDs you need for the next step.

Valid account_type values: ASSET, LIABILITY, EQUITY, INCOME, OTHER_INCOME, EXPENSE, COST_OF_SALES, OTHER_EXPENSE.

3. Post opening balances

Opening balances are a single journal that brings the trial balance up from zero to your "as at" position on day one. It must balance.

The journal entries endpoint is /api/v1/journal_entries. Lines reference accounts by UUID (account_id), not by code. The payload fields are entry_date, narration, and reference:

curl -X POST "$SAEBOOKS_API_BASE/journal_entries" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "entry_date": "2026-07-01",
    "narration": "Opening balances",
    "lines": [
      {"account_id":"<uuid-of-1100>","debit":"15000.00","credit":"0"},
      {"account_id":"<uuid-of-1200>","debit":"4200.00","credit":"0"},
      {"account_id":"<uuid-of-2100>","debit":"0","credit":"3100.00"},
      {"account_id":"<uuid-of-3100>","debit":"0","credit":"16100.00"}
    ]
  }'

This creates a DRAFT entry. To post it, call:

curl -X POST "$SAEBOOKS_API_BASE/journal_entries/<entry-id>/post" \
  -H "Authorization: Bearer $JWT" \
  -H "If-Match: 1" \
  -H "X-Idempotency-Key: $(uuidgen)"

The server validates SUM(debits) = SUM(credits) at both the schema level and on post; an unbalanced journal returns 422.

4. Verify

curl "$SAEBOOKS_API_BASE/reports/trial_balance?as_of_date=2026-07-01" \
  -H "Authorization: Bearer $JWT"

You should see your four touched accounts at their opening balances, and every other account at zero.

What you've built

  • A chart of accounts you'll add to as the business grows
  • An immutable opening-balances journal that anchors every report
  • A trial balance that balances at zero — the precondition for every P&L, balance sheet, and BAS run from here on

Next