API v1 Reference

RESTful API for accessing accounting data. All endpoints require authentication via API key.

Authentication

API Key Authentication

Include your API key in the Authorization header with the Bearer scheme.

Example Header

Authorization: Bearer <your-api-key>

Error Response (401)

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Unauthorized"
  }
}

Structured Error Shape

All API errors return a consistent JSON shape: { error: { code: string, message: string } }.

Note: Generate your API key from your Profile Settings page.

Chart of Accounts

GET
/api/v1/coa

Retrieve the chart of accounts hierarchy.

Query Parameters

NameTypeRequiredDescription
activebooleanOptionalFilter by active status (default: true). Use `all=true` to include inactive.
allbooleanOptionalInclude inactive COA entries.
searchstringOptionalSearch by name or code (regex).

Response

[
  {
    "_id": "...",
    "parent": null,
    "code": "1",
    "name": "Current Assets",
    "position": "Db",
    "category": "Asset",
    "isActive": true
  }
]

Accounts

GET
/api/v1/accounts

Retrieve accounts. Supports filters and lookup by ID.

Query Parameters

NameTypeRequiredDescription
idstringOptionalGet a single account by ID.
numberstringOptionalFilter by exact account number.
coastringOptionalFilter accounts by parent COA ID.
allbooleanOptionalInclude inactive accounts (default: active only).

Response

[
  {
    "_id": "...",
    "coa": "...",
    "number": "10101",
    "name": "Petty Cash",
    "balance": 5000000,
    "isActive": true
  }
]

Transactions

GET
/api/v1/transactions

List transactions with optional filters.

Query Parameters

NameTypeRequiredDescription
idstringOptionalGet a single transaction by ID.
codestringOptionalFilter by transaction code (case-insensitive partial match).
statusstringOptionalFilter by status: Pending, Confirmed, Rejected, Reversed.
sourcestringOptionalFilter by source: api, ui.
startDatestring (YYYY-MM-DD)OptionalFilter by effective date (inclusive start).
endDatestring (YYYY-MM-DD)OptionalFilter by effective date (inclusive end).
vendorstringOptionalSearch by reference field (case-insensitive partial match).
includeDetailsbooleanOptionalInclude journal line details.

Response

[
  {
    "_id": "...",
    "code": "GJ-20260701-001",
    "effectiveDate": "2026-07-01T00:00:00.000Z",
    "amount": 1000000,
    "status": "Confirmed",
    "reference": "INV-001",
    "information": "Payment received",
    "source": "api",
    "created": { "at": "...", "by": "..." },
    "evidence": [
      { "url": "/uploads/evidence/...", "description": "Invoice scan" }
    ]
  }
]
GET
/api/v1/transactions/:id

Get a single transaction by ID with journal line details.

Response

{
  "_id": "...",
  "code": "GJ-20260701-001",
  "effectiveDate": "2026-07-01T00:00:00.000Z",
  "amount": 1000000,
  "status": "Confirmed",
  "source": "api",
  "evidence": [],
  "details": [
    { "account": { "number": "10101", "name": "Petty Cash" }, "debit": 1000000, "credit": 0 },
    { "account": { "number": "40101", "name": "Revenue" }, "debit": 0, "credit": 1000000 }
  ]
}
POST
/api/v1/transactions

Create a new transaction. Supports dry-run, idempotency, evidence, and evidence descriptions.

Request Body

{
  "type": "General",
  "effectiveDate": "2026-07-01",
  "reference": "INV-001",
  "information": "Payment received",
  "dryRun": false,
  "evidence": [
    { "url": "https://example.com/invoice.pdf", "description": "Invoice scan" }
  ],
  "lines": [
    { "accountId": "...", "debit": 1000000, "credit": 0 },
    { "accountId": "...", "debit": 0, "credit": 1000000 }
  ]
}

Response

{
  "_id": "...",
  "code": "GJ-20260701-001",
  "status": "Pending",
  "source": "api"
}

Idempotency: Send Idempotency-Key header to prevent duplicate submissions. The server caches the response for the key and returns it on reuse.

Dry-run: Set dryRun: true to validate the transaction without persisting. Returns a validation summary.

PUT
/api/v1/transactions/:id

Update a pending transaction. Replaces all journal lines if provided.

Query Parameters

NameTypeRequiredDescription
idstringRequiredTransaction ID (in path).

Request Body

{
  "effectiveDate": "2026-07-02",
  "reference": "INV-001",
  "information": "Updated info",
  "lines": [
    { "accountId": "...", "debit": 2000000, "credit": 0 },
    { "accountId": "...", "debit": 0, "credit": 2000000 }
  ]
}

Response

{
  "message": "Updated."
}
DELETE
/api/v1/transactions/:id?action=confirm

Confirm a pending transaction. Affects account balances.

Response

{
  "message": "Confirmed."
}
DELETE
/api/v1/transactions/:id?action=reject

Reject a pending transaction.

Response

{
  "message": "Rejected."
}
DELETE
/api/v1/transactions/:id?action=cancel

Cancel a pending transaction (soft delete — sets status to Canceled, hidden from ledger).

Response

{
  "message": "Deleted."
}
DELETE
/api/v1/transactions/:id?action=reverse

Create a reversal for a confirmed transaction. Creates a pending reversal with opposite journal lines. The original is only marked as Reversed when the reversal transaction is confirmed.

Response

{
  "message": "Reversal created.",
  "reversalId": "...",
  "reversalCode": "REV-GJ-20260709-123"
}

Evidence

POST
/api/v1/transactions/:id/evidence

Upload an evidence file to a transaction. Supports optional description.

Request Body

multipart/form-data:
  file: (binary)
  description: "Invoice scan" (optional)

Response

{
  "url": "/uploads/evidence/12345-invoice.pdf",
  "description": "Invoice scan"
}
DELETE
/api/v1/transactions/:id/evidence?url=...

Remove evidence by URL from a transaction.

Query Parameters

NameTypeRequiredDescription
urlstringRequiredURL of the evidence to remove.

Response

{
  "message": "Evidence removed."
}

Transaction Types

GET
/api/v1/transaction-types

List available transaction types and their code prefixes.

Response

{
  "General": "GJ",
  "FundTransfer": "FT",
  "Expense": "EX",
  "Revenue": "RV",
  "Purchase": "PC",
  "Sales": "SL",
  "Payroll": "PR",
  "Tax": "TX",
  "Depreciation": "DP",
  "Closing": "CE"
}

Ledger

GET
/api/v1/ledger-periods

Retrieve ledger entries for a specific account within a date range. Returns opening balance, period mutations, and running balance.

Query Parameters

NameTypeRequiredDescription
accountIdstringRequiredAccount ID to retrieve ledger for.
startDatestring (YYYY-MM-DD)OptionalStart date (inclusive). Defaults to beginning of current year.
endDatestring (YYYY-MM-DD)OptionalEnd date (inclusive). Defaults to today.

Response

{
  "account": { "number": "10101", "name": "Petty Cash" },
  "coa": { "code": "1", "name": "Current Assets", "position": "Db" },
  "openingBalance": 0,
  "startDate": "2026-01-01",
  "endDate": "2026-07-01",
  "rows": [
    { "date": "2026-07-01T00:00:00.000Z", "code": "GJ-20260701-001", "information": "Payment", "debit": 1000000, "credit": 0, "balance": 1000000 }
  ]
}

Reports

GET
/api/v1/reports/balance-sheet

Generate the balance sheet as of a specific date.

Query Parameters

NameTypeRequiredDescription
datestring (YYYY-MM-DD)OptionalDate as of which to generate the balance sheet. Defaults to today.

Response

{
  "asOfDate": "2026-07-01T00:00:00.000Z",
  "assets": { "_id": "Asset", "code": "", "name": "ASSET", "position": "Db", "children": [...], "accounts": [], "total": 50000000 },
  "liabilities": { "_id": "Liability", "code": "", "name": "LIABILITY", "position": "Cr", "children": [...], "accounts": [], "total": 10000000 },
  "equity": { "_id": "Equity", "code": "", "name": "EQUITY", "position": "Cr", "children": [...], "accounts": [], "total": 40000000 },
  "netIncome": 5000000
}
GET
/api/v1/reports/income-statement

Generate the income statement for a date range.

Query Parameters

NameTypeRequiredDescription
startDatestring (YYYY-MM-DD)OptionalStart date (inclusive). Defaults to Jan 1 of current year.
endDatestring (YYYY-MM-DD)OptionalEnd date (inclusive). Defaults to today.

Response

{
  "startDate": "2026-01-01T00:00:00.000Z",
  "endDate": "2026-07-01T00:00:00.000Z",
  "revenue": { "_id": "Revenue", "code": "", "name": "REVENUE", "position": "Cr", "category": "Revenue", "parent": null, "children": [...], "total": 50000000 },
  "cogs": { "_id": "COGS", "code": "", "name": "COGS", "position": "Db", "category": "COGS", "parent": null, "children": [...], "total": 20000000 },
  "expenses": { "_id": "Expense", "code": "", "name": "EXPENSE", "position": "Db", "category": "Expense", "parent": null, "children": [...], "total": 25000000 },
  "grossProfit": 30000000,
  "netProfit": 5000000
}

Error Codes

All errors return the same JSON shape: { error: { code: string, message: string } }

400Bad RequestVALIDATION_ERRORMissing or invalid request parameters / body.
401UnauthorizedUNAUTHORIZEDMissing or invalid API key.
403ForbiddenFORBIDDENAPI key does not have permission.
404Not FoundNOT_FOUNDThe requested resource does not exist.
409ConflictIDEMPOTENCY_REPLAYIdempotency key conflict or duplicate.
500InternalINTERNAL_ERRORUnexpected server error.

Additional Error Codes

UNBALANCED_JOURNAL — Debits must equal credits (HTTP 400).

NOT_PENDING — Transaction is not in Pending status (HTTP 400).

ACCOUNT_NOT_FOUND — Referenced account ID does not exist (HTTP 404).

Best Practices

Store securely: Treat your API key like a password. Do not expose it in client-side code or version control.

Regenerate periodically: You can regenerate your API key from your profile page at any time. Regenerating invalidates the previous key immediately.

Date format: All dates should be in ISO 8601 format (YYYY-MM-DD) or ISO string (YYYY-MM-DDTHH:mm:ss.sssZ).

Transaction source: Transactions created via the API are automatically marked with source: "api" to distinguish them from UI-created transactions.

Idempotency: Use the Idempotency-Key header on POST /api/v1/transactions to safely retry requests without creating duplicates. Keys expire after a period.

Dry-run validation: Set dryRun: true on POST /api/v1/transactions to validate journal lines, account existence, and balanced books without persisting the transaction.

Evidence: Upload supporting documents via POST /api/v1/transactions/:id/evidence (multipart/form-data). Optionally attach a description field. Evidence can also be provided inline during creation via the evidence array in the request body.