Journal Transactions
Journal Transactions API
The Journal Transactions API allows you to create balanced multi-entry journal transactions across multiple ledgers with automatic currency conversion to the system's base currency.
See also:
Object Relationships
Journal transactions are the primary mechanism for complex, multi-party movements of funds. They integrate into the wider ledger hierarchy as follows:
Key Rules
- Settlement: Journal entries immediately update the Posted Balance of the target ledgers.
- Aggregation: If a journal entry is posted to a Subledger, the change automatically cascades up to its Parent Control Ledger(s).
- Balance Constraint: For a transaction to be committed, the sum of all debits must equal the sum of all credits when converted to the system's Local Currency (LCY).
Key Features
- Create multi-entry journal transactions with any number of ledger entries (minimum 2)
- Add journal entries to existing transactions or create new ones
- Automatic balance validation in the system's base currency (LCY)
- Support for different currencies across entries with automatic conversion
- Flexible ledger identification (by UUID, code, or numeric ID)
- Support for transaction metadata and custom properties
- Automatic parent ledger balance updates
- DirectBooking validation - journal entries are rejected on ledgers with DirectBooking=false
Endpoints
Create Journal Transaction
POST /v1/ledgers/journals
Create a balanced multi-entry journal transaction with automatic currency conversion. You can either create a new transaction or add entries to an existing one.
Adding Entries to Existing Transactions
To add journal entries to an existing transaction, include the transaction_id field in your request:
{
"transaction_id": "70a07097-22a2-457a-b6e6-84ce7129d6ca",
"transaction": {
"type": "JOURNAL",
"total_amount": 500000,
"currency_code": "CHF",
"description": "Additional entries for existing transaction"
},
"entries": [
{
"ledger_code": "EXPENSES-UTILITIES",
"amount": 500000,
"type": "debit",
"event": "utility_expense",
"description": "Additional utility expense"
},
{
"ledger_code": "CASH-MAIN",
"amount": 500000,
"type": "credit",
"event": "utility_payment",
"description": "Additional utility payment"
}
]
}If the transaction_id is not provided or is null, a new transaction will be created.
Request Body
{
"transaction": {
"type": "JOURNAL",
"total_amount": 1000000,
"currency_code": "CHF",
"description": "Monthly payroll expense allocation",
"reference_id": "PAY-2023-12",
"value_date": "2023-12-31T12:00:00Z",
"metadata": {
"department": "HR",
"payment_period": "2023-12"
}
},
"entries": [
{
"ledger_code": "EXPENSES-SALARY",
"amount": 1000000,
"type": "debit",
"event": "payroll_expense",
"description": "December 2023 payroll expenses",
"value_date": "2023-12-31T12:00:00Z"
},
{
"ledger_code": "CASH-MAIN",
"amount": 1000000,
"type": "credit",
"event": "payroll_payment",
"description": "December 2023 payroll payment",
"value_date": "2023-12-31T12:00:00Z"
}
],
"is_internal": false
}Response (201 Created)
All monetary amounts are returned in CcyAmtWithPrecision format (minor units as a string, with currency code and precision).
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "JOURNAL",
"status": "completed",
"total_amount": {
"amount": "1000000",
"currency": "CHF",
"precision": 2
},
"currency_id": "7c0dfa3d-2a9e-4d1a-8f8f-0d8d06358334",
"currency_code": "CHF",
"description": "Monthly payroll expense allocation",
"reference_id": "PAY-2023-12",
"timestamp": "2023-12-31T12:00:01Z",
"value_date": "2023-12-31T12:00:00Z",
"completed_at": "2023-12-31T12:00:01Z",
"total_debit_lcy": {
"amount": "1000000",
"currency": "CHF",
"precision": 2
},
"total_credit_lcy": {
"amount": "1000000",
"currency": "CHF",
"precision": 2
},
"metadata": {
"department": "HR",
"payment_period": "2023-12"
},
"created_at": "2023-12-31T12:00:01Z",
"modified_at": "2023-12-31T12:00:01Z",
"created_by": "1e3d0ff4-e9e3-4b6a-a3b3-4e9b97df8f34",
"modified_by": "1e3d0ff4-e9e3-4b6a-a3b3-4e9b97df8f34",
"active": true
}Usage Guidelines
Multi-Currency Entries
When creating journal entries with different currencies, the system automatically calculates the Local Currency Equivalent (LCY) amount for each entry using the current exchange rates. The journal transaction will be balanced based on the LCY amounts, not the original currency amounts.
Example:
{
"transaction": {
"type": "JOURNAL",
"total_amount": 1000,
"currency_code": "CHF",
"description": "Multi-currency journal entry"
},
"entries": [
{
"ledger_code": "EXPENSES-USD",
"amount": 1000,
"currency_code": "USD",
"type": "debit",
"event": "usd_expense",
"description": "USD expense"
},
{
"ledger_code": "CASH-EUR",
"amount": 950,
"currency_code": "EUR",
"type": "credit",
"event": "eur_payment",
"description": "EUR payment"
}
]
}In this example, even though 1000 USD ≠ 950 EUR in nominal terms, the transaction may be balanced if the LCY equivalents (e.g., in CHF) are equal.
Ledger Identification Options
You can identify ledgers in three different ways:
- UUID (highest priority): Use
ledger_idwith a valid UUID - Ledger Code (second priority): Use
ledger_codewith the accounting code - Numeric ID (lowest priority): Use
ledger_num_idwith the numeric identifier
The system will try to find the ledger in the order listed above, based on which fields are provided.
Balance Validation
The system performs the following validations:
- The sum of all debit entries must equal the sum of all credit entries in LCY
- Each ledger must have sufficient funds for debit operations (unless overdraft is enabled)
- All referenced ledgers must exist and be active
- DirectBooking Validation: Journal entries are rejected on ledgers where
DirectBooking=false. This ensures that only ledgers configured for direct booking can receive journal entries.
Parent Ledger Updates
When an entry affects a ledger that has a parent, the parent's balance (and any ancestors up the hierarchy) will be automatically updated to reflect the change.
Error Codes
| Code | Description | HTTP Status |
|---|---|---|
ledger_not_found | The specified ledger was not found | 404 |
transaction_not_found | The specified transaction was not found | 404 |
insufficient_funds | The ledger has insufficient funds for the debit operation | 400 |
journal_imbalanced_lcy | The journal entries don't balance in LCY | 400 |
invalid_entry_type | The entry type is not valid (must be 'debit' or 'credit') | 400 |
invalid_ledger_entry_type | The entry type is not valid for this ledger | 400 |
failed_to_create_transaction | Failed to create the transaction | 500 |
failed_to_create_ledger_entry | Failed to create a ledger entry | 500 |
failed_to_update_ledger | Failed to update a ledger's balance | 500 |
conversion_failed | Currency conversion failed | 500 |
invalid_ledger_configuration | Ledger configuration prevents direct booking | 400 |