REST API Accounts - edmozley/freeitsm GitHub Wiki

๐Ÿ“’ REST API: Chart of accounts

The usage guide for accounts in the REST API โ€” the chart itself, and the sync a finance system calls. Mirrors the interactive documentation at System โ†’ API โ†’ Documentation. User guide: Chart of accounts. New in 5.1.0.

[!NOTE] New to the API? The basics table on REST API: Tickets covers base URL, authentication, the response envelope, error codes, rate limits and PATCH semantics - identical across all modules.

Accounts work exactly like cost centres - company-scoped, codes as text, one sync call to stay in step - with one more field, kind.

๐Ÿข Company-scoped Every account is one company's. Lists are filtered to the key's companies; one outside them is a 404 on every by-id route. Writes land in company_id (checked against the key) or the key's default company.
๐Ÿ”ค Codes are text "2.0.3.0.4" and "0010" stay as sent. Unique per company, ignoring case. Send codes as JSON strings.
๐Ÿท๏ธ Kind expense, fixed_asset, payroll or income. Words the import understands ("Capex", "Salaries", "Revenue") are accepted too and stored as the key.
๐Ÿ”„ One call keeps you in step POST /accounts/sync takes the whole chart, matched on code. All or nothing.

๐Ÿš€ Quick start

B="https://your-server/api/v1"; K="Authorization: Bearer fitsm_โ€ฆ"

# Active fixed-asset accounts
curl -s -H "$K" "$B/accounts?kind=fixed_asset&active=true" | jq '.data[] | {code, name}'

# Nightly sync from finance - try it first with dry_run
curl -s -H "$K" -H "Content-Type: application/json" -X POST "$B/accounts/sync" -d '{
  "dry_run": true,
  "accounts": [
    {"code": "2.0.3.0",   "name": "IT running costs",   "kind": "expense"},
    {"code": "2.0.3.0.4", "name": "Software Licences",  "parent_code": "2.0.3.0"},
    {"code": "2.1.0.3.2", "name": "Computer Equipment", "kind": "fixed_asset"},
    {"code": "1.3.0.0.1", "name": "Employee Payroll",   "kind": "payroll"}
  ]}'

Endpoints

Method Path Permission What
GET /accounts accounts.read List / search
POST /accounts accounts.create Add one
GET /accounts/{id} accounts.read One account
PATCH /accounts/{id} accounts.update Change fields
DELETE /accounts/{id} accounts.delete Delete (refused with 409 while it has children - make it inactive instead)
POST /accounts/sync accounts.update and create Bring the whole chart in step

List filters

company_id, code (exact), kind (expense / fixed_asset / payroll / income; anything else is a 400), q (code, name or description), active=true|false, parent_id (0 = top level), sort (code default, name, kind, updated_at, id; prefix - for descending), page / per_page.

An account

{
  "id": 12, "code": "2.0.3.0.4", "name": "Software Licences", "kind": "expense", "description": null,
  "parent": { "id": 11, "code": "2.0.3.0", "name": "IT running costs" },
  "is_active": true,
  "company": { "id": 1, "name": "Default" },
  "child_count": 0,
  "created_at": "2026-11-12T19:02:00Z", "updated_at": "2026-11-12T19:02:00Z"
}

Writes take code, name, kind (required on create), description, parent_id or parent_code (same company; never itself or anything below it), is_active, and on create company_id. kind can be changed but not emptied.

The sync

Body: accounts (array, at most 10,000 โ€” split bigger charts by company), each with code and any of name, kind, description, parent_code, is_active; optional company_id, dry_run, deactivate_missing.

  • Matched on code, ignoring case. Only the keys you send are changed.
  • A new code needs a name and a kind - or a parent_code, whose kind it then takes (the nearest ancestor with one, in this call or already there).
  • parent_code may name another row in the same call, or an existing account. A blank one means top level.
  • deactivate_missing: true makes inactive every account of that company not in the list. Nothing is ever deleted.
  • All or nothing. Any problem and nothing is written: 422 invalid_rows with every problem in error.details.errors (line = 1-based position in accounts).
  • dry_run: true returns what would happen and writes nothing.

Response: company_id, applied, dry_run, counts (create, update, unchanged, deactivate) and changes - one entry per row that changed, with its line, code, name, kind, action and the fields that changed.


Related: REST API ยท Chart of accounts ยท REST API: Cost centres