Method & PathPermissionPurposeSide effects
GET journalsjournal.viewPaginated list—
POST journalsjournal.createCreate draft entry—
GET journals/{journal}journal.viewDetail—
PATCH journals/{journal}journal.createEdit draft—
DELETE journals/{journal}journal.createDelete draft—
GET journals/{journal}/pdfjournal.viewStream PDF with signature block—
GET journals/{journal}/historyjournal.viewWorkflow event history—
POST journals/{journal}/submitjournal.submitDraft → SubmittedWorkflow transition
POST journals/{journal}/reviewjournal.reviewSubmitted → ReviewedTOTP step-up optional/required per tenant policy
POST journals/{journal}/approvejournal.approveReviewed → Approved, auto-postsPosts to ledger
POST journals/{journal}/rejectjournal.rejectBack to Draft, history preserved—
POST journals/{journal}/reversejournal.reverse (Preparer + Full access tiers — Staff and Accountant request; Administrator/Reviewer/Approver only oversee)Request reversal of a posted entrySubmits a mirror reversing entry into the maker-checker queue (the original stays posted until it is approved); TOTP required if require_totp_for_posting

Recurring journal entries (recurring-journal-entries)

Method & PathPermissionPurposeSide effects
GET recurring-journal-entriesjournal.viewList schedules—
POST recurring-journal-entriesjournal.createCreate schedulePass source_document_id to copy an existing entry — it must be approved and posted (status = posted), otherwise 422 on source_document_id; drafts and in-flight records cannot be made recurring. Dry-runs the template (rolled back) to catch bad references; computes first occurrence
GET recurring-journal-entries/{schedule}journal.viewDetail + runs—
PATCH recurring-journal-entries/{schedule}journal.createUpdate (partial)Recomputes next-run if recurrence fields changed
DELETE recurring-journal-entries/{schedule}journal.createDelete (or pause if it has generated documents)—
POST recurring-journal-entries/{schedule}/pause | /resumejournal.createPause/resumeResume recomputes next occurrence
POST recurring-journal-entries/{schedule}/runs/{run}/resolve-dstjournal.createManually resolve a DST-ambiguous occurrenceGenerates the flagged entry at the given instant

Request/response shape (illustrative, sanitized)

POST /api/v1/journals
{
  "transaction_date": "2026-08-01",
  "branch_id": "<branch-uuid>",
  "currency": "PHP",
  "lines": [
    { "account_id": "<account-uuid>", "debit": 1000.00, "credit": 0 },
    { "account_id": "<account-uuid>", "debit": 0, "credit": 1000.00 }
  ]
}
201 Created
{
  "uuid": "<journal-uuid>",
  "document_number": null,
  "status": "draft",
  "total_debit": "1000.0000",
  "total_credit": "1000.0000"
}

Debits/credits are validated and totaled server-side; document_number is assigned atomically only at posting time, not at draft creation.