Books API (1.0.0)

Download OpenAPI specification:

An API for a shared Book — a financial ledger for one person or small business, backed by a Clerk Organization so it can have collaborators. A Book owns a set of ledger-api accounts and exposes a simplified, type-aware transaction model (expense/income/transfer) so callers never have to think in raw double-entry debits and credits.

Every route acts on the caller's active Clerk Organization: the Book is always the verified JWT's org_id claim, never a URL or body parameter. There is no Book-ID parameter anywhere to get an authorization check wrong on.

Authenticate with a Clerk session token, issued against the books-api JWT template (org claims enabled), sent as Authorization: Bearer <token>.

See books-api-plan.md and docs/projects/books-api/design.md in the operations repo for the full domain design this implements.

Book

The active Book's own metadata.

Get the active Book

Authorizations:
clerkAuth

Responses

Response samples

Content type
application/json
{
  • "currency": "USD",
  • "org_id": "string",
  • "chart_version": 0,
  • "created_at": "2019-08-24T14:15:22Z"
}

Create the active Book (one-time initializer)

Provisions books-api-plan.md's 15-account default chart of accounts against ledger-api. Not an upsert — fails with 422 if a Book already exists for this organization.

Authorizations:
clerkAuth
Request Body schema: application/json
required
currency
required
string

ISO 4217, e.g. USD. One currency per Book.

Responses

Request samples

Content type
application/json
{
  • "currency": "USD"
}

Response samples

Content type
application/json
{
  • "book": {
    },
  • "accounts": [
    ]
}

Accounts

Book-scoped accounts and categories (backed by namespaced ledger-api accounts).

List the Book's accounts and categories

Authorizations:
clerkAuth

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Add a custom account or category

Authorizations:
clerkAuth
Request Body schema: application/json
required
id
required
string

Slug — lowercase letters, numbers, and hyphens only.

display_name
required
string
type
required
string (AccountType)
Enum: "asset" "liability" "income" "expense"

Responses

Request samples

Content type
application/json
{
  • "id": "side-hustle",
  • "display_name": "string",
  • "type": "asset"
}

Response samples

Content type
application/json
{
  • "id": "side-hustle",
  • "display_name": "string",
  • "type": "asset",
  • "archived": true,
  • "balance": 0,
  • "created_at": "2019-08-24T14:15:22Z"
}

Rename or archive an account/category

books-api metadata only — never touches ledger-api. ledger-api's own account name is treated as immutable once created.

Authorizations:
clerkAuth
path Parameters
id
required
string

An account/category's slug (e.g. checking, groceries), not ledger-api's internal id.

Request Body schema: application/json
required
display_name
string
archived
boolean

Responses

Request samples

Content type
application/json
{
  • "display_name": "string",
  • "archived": true
}

Response samples

Content type
application/json
{
  • "id": "side-hustle",
  • "display_name": "string",
  • "type": "asset",
  • "archived": true,
  • "balance": 0,
  • "created_at": "2019-08-24T14:15:22Z"
}

Transactions

Typed transactions (expense/income/transfer) posted into the Book's ledger.

List the Book's transactions, most recent first

Authorizations:
clerkAuth
query Parameters
limit
integer >= 1
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Record a transaction

type picks how the signed ledger-api entries are derived — see books-api-plan.md 1.4. id is required and doubles as ledger-api's idempotency key: a retried POST with the same id returns the original, 200, never a duplicate post.

Authorizations:
clerkAuth
Request Body schema: application/json
required
id
required
string

Client-supplied — also used as ledger-api's idempotency key.

type
required
string (TransactionType)
Enum: "expense" "income" "transfer"
date
string <date>

Defaults to today (UTC) if omitted.

description
string
source_account_id
string

expense, transfer

destination_account_id
string

income, transfer

category_id
string

income, or expense without splits

amount
integer

Positive, minor units. Required unless splits is used (expense).

Array of objects (Split)

expense only — multiple categories from one source account, must sum to amount if both are given.

Responses

Request samples

Content type
application/json
{
  • "id": "string",
  • "type": "expense",
  • "date": "2019-08-24",
  • "description": "string",
  • "source_account_id": "string",
  • "destination_account_id": "string",
  • "category_id": "string",
  • "amount": 0,
  • "splits": [
    ]
}

Response samples

Content type
application/json
{
  • "id": "string",
  • "type": "expense",
  • "description": "string",
  • "voids": "string",
  • "voided_by": "string",
  • "effective_date": "2019-08-24",
  • "created_at": "2019-08-24T14:15:22Z",
  • "entries": [
    ]
}

Get a transaction with its entries

Authorizations:
clerkAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "id": "string",
  • "type": "expense",
  • "description": "string",
  • "voids": "string",
  • "voided_by": "string",
  • "effective_date": "2019-08-24",
  • "created_at": "2019-08-24T14:15:22Z",
  • "entries": [
    ]
}

Void a transaction

Posts a new, equal-and-opposite transaction and links the two records — books-api-plan.md 1.5's "void, not edit" rule. No in-place edit exists; correcting a mistake means voiding the original and recording a new one.

Authorizations:
clerkAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "id": "string",
  • "type": "expense",
  • "description": "string",
  • "voids": "string",
  • "voided_by": "string",
  • "effective_date": "2019-08-24",
  • "created_at": "2019-08-24T14:15:22Z",
  • "entries": [
    ]
}