Appearance
Manual Booking
Introduction
Manual bookings allow you to create custom journal transactions for accounting purposes. Unlike system-generated transactions from invoices or expenses, manual bookings give you direct control over journal entries while maintaining double-entry bookkeeping principles.
Each manual booking creates a journal transaction with multiple entries that record specific debits and credits to individual accounts. The system ensures all entries balance (total debits equal total credits) and maintains accounting integrity.
Transaction Types
| Type | Description |
|---|---|
| Single | Simple transaction with exactly two journal entries (one debit, one credit) |
| Compound | Complex transaction with more than two journal entries |
Create and update manual bookings
post
/journal
Create or update manual bookings with proper double-entry validation. Supports both single transactions (2 entries) and compound transactions (multiple entries).
This endpoint requires both the manage accounting and the create journal_transaction permission. Users without either receive a 403 Forbidden response.
| Attribute (* required) | Type | Description |
|---|---|---|
id | String? | ID of existing booking to update (omit for new transaction) |
reversal_date | Date? | Only when updating a booking that can't be edited in place (see Immutable journal): posting date of the reversal booking. Defaults to the booking's default_reversal_date |
date * | Date | Transaction posting date (must be within open fiscal year) |
message * | String | Transaction description (max 255 chars) |
currency | String? | Booking currency code (e.g. EUR, USD) - defaults to CHF. Foreign currencies must be configured for the organization |
conversion_rate | Number? | Per-unit conversion rate to CHF (e.g. 0.9328 for 1 EUR = 0.9328 CHF). Omit to use the automatic rate (organization rate, falling back to the official BAZG monthly rate) |
type | String? | single or compound. Optional - the mode is inferred from whether entries is present. When given, it enforces the matching field set |
| Single Transaction Mode | ||
debit_account_id * | String | Account ID for debit entry |
credit_account_id * | String | Account ID for credit entry |
amount * | Number | Transaction amount in the booking currency (must be > 0) |
vat_rate_id | String? | VAT Rate ID |
| Compound Transaction Mode | ||
entries * | Array | Array of journal entries (min 2 entries) |
entries.*.account_id * | String | Account ID |
entries.*.debit_amount | Number? | Debit amount (≥ 0) |
entries.*.credit_amount | Number? | Credit amount (≥ 0) |
entries.*.vat_rate_id | String? | VAT Rate ID |
entries.*.description | String? | Entry-specific description (max 255 chars) |
entries.*.currency | String? | Entry currency - defaults to the booking currency |
| File Attachment | ||
file | File? | Supporting document (max 10MB, PDF/images/Office docs) |
Validation Rules
- Single mode: Requires
debit_account_id,credit_account_id, andamount - Compound mode: Requires
entriesarray with balanced debits/credits - Exactly one mode must be used: a request without
entriesmust contain the single-mode fields, and vice versa. A request providing neither is rejected with422(not silently accepted) type: singleadditionally prohibitsentries;type: compoundadditionally requiresentries- Each entry must have either debit OR credit amount, not both
- Sum of debit amounts must equal sum of credit amounts
- Transaction date must be within an open fiscal year
- Accounts must exist and be different for single transactions
- A foreign
currencymust be configured as a currency of the organization
Foreign Currency Bookings
Amounts are entered in the booking currency and balanced in that currency. On posting, every journal entry is converted to CHF using the conversion_rate (or the automatic rate for the posting date). The original amount and rate stay stored on each journal entry. Any leftover CHF rounding difference from the per-entry conversion is booked automatically to the organization's financial income/expenses account (default 6900/6950).
VAT Handling
The system automatically determines which side (debit/credit) to apply VAT based on account types:
- Revenue transactions: VAT on revenue side
- Expense transactions: VAT on expense side
- Asset transactions: Special handling for fixed assets (accounts 1400-1999)
Single Transaction Example
json
{
"date": "2024-03-15",
"message": "Office equipment purchase",
"debit_account_id": "acc_equipment_id",
"credit_account_id": "acc_bank_id",
"amount": 1080.0,
"vat_rate_id": "vat_77_id",
"currency": "CHF"
}Compound Transaction Example
json
{
"date": "2024-03-15",
"message": "Multi-account expense allocation",
"entries": [
{
"account_id": "acc_office_supplies",
"debit_amount": 500.0,
"description": "Office supplies",
"vat_rate_id": "vat_77_id"
},
{
"account_id": "acc_marketing",
"debit_amount": 300.0,
"description": "Marketing materials"
},
{
"account_id": "acc_bank",
"credit_amount": 800.0
}
]
}Update Transaction Example
json
{
"id": "existing_booking_id",
"date": "2024-03-16",
"message": "Updated: Office equipment purchase",
"debit_account_id": "acc_equipment_id",
"credit_account_id": "acc_bank_id",
"amount": 1200.0,
"file": "[uploaded file]"
}Reversed bookings
A booking that has already been reversed can't be updated. The request returns 422, regardless of the immutableJournal setting.
Creating a booking returns 201, updating it in place returns 200.
When the organization has the immutable journal enabled and the booking was created before today, the update does not change the booking in place. Instead the original is reversed on reversal_date and a new booking is created with the submitted data. The response contains the new booking and has status 201. The new booking gets a copy of the original's attachment unless the request uploads a new file. A reversal_date in a closed fiscal year or VAT period returns 422 on reversal_date and nothing is changed. A booking that was never booked (no journal transaction) can't be reversed either and returns 422 on id.
Import Source
Manual bookings created by an integration or import carry a source value inside custom_fields (e.g. stripe, lightspeed, shopify, bexio, banana, abasalary, selectline, csv_import). The resource exposes this as a read-only import_source attribute; older Bexio and Banana imports without a source value are derived from their bexio_id/banana_id custom field. Updating a booking never removes its custom fields, so provenance survives manual edits.
Automatic Behaviors
The system automatically handles several aspects of manual booking management:
Transaction Numbering
- Automatic numbering: New transactions receive sequential numbers (
number_sequence) and formatted numbers (number_full) - Number format: Uses system-defined prefix + sequence number (e.g., "TX-1001")
Fiscal Year Management
- Auto-creation: Missing fiscal years are automatically created when transactions are posted
- Validation: Transaction dates must fall within open (non-closed) fiscal years
- Updates blocked: Transactions cannot be updated if their fiscal year is closed
- Deletions blocked: Transactions cannot be deleted if their fiscal year is closed
VAT Period Management
- Auto-creation: VAT periods are automatically created for transaction dates when missing
- Swiss compliance: Ensures proper VAT reporting period structure
User Attribution
- Member tracking: Transactions are automatically attributed to the current user's member record
- Audit trail: Maintains who created/modified each transaction
Manual Booking Cleanup
- Cascade deletion: When deleting manual booking transactions, the associated manual booking document is also removed
Fiscal Year Restrictions
- Transactions in closed fiscal years cannot be updated or deleted
- The system will throw a
FiscalYearClosedExceptionif these operations are attempted - Always verify fiscal year status before attempting modifications
Resolve a conversion rate
get
/journal/conversion-rate
Resolves the conversion rate to CHF for a currency on a given date, using the organization's own exchange rates first and falling back to the official BAZG monthly average rate. Useful for prefilling the conversion_rate of a foreign currency booking.
Like creating a booking, this endpoint requires both the manage accounting and the create journal_transaction permission.
| Parameter (* required) | Type | Description |
|---|---|---|
currency * | String | Currency code (e.g. EUR, JPY) |
date | Date? | Date to resolve the rate for - defaults to today |
Example response
json
{
"currency": "JPY",
"rate": 0.00585,
"units": 100,
"display_rate": 0.585
}rate is the per-unit rate (what conversion_rate expects). units reflects how the currency is officially quoted (1, 100 or 1000) and display_rate is the rate for that quantity (e.g. 100 JPY = 0.585 CHF). rate is null when no rate is available for the currency.
Reverse a manual booking
post
/journal_transaction/{id}/reverse
Reverses a manual booking (Storno). The endpoint creates a new manual booking whose entries mirror the original with debit and credit swapped, in the same currency and at the same exchange rate. The two bookings are linked: the original returns the reversal in reversed_by, the reversal returns the original in reverses.
{id} is the ID of the journal transaction. The endpoint requires the same permissions as creating a booking.
| Attribute (* required) | Type | Description |
|---|---|---|
date | Date? | Posting date of the reversal (YYYY-MM-DD). Must lie in an open fiscal year and outside closed (submitted) VAT periods. Defaults to the original booking date if its fiscal year and VAT period are still open, otherwise to the first open date (see default_reversal_date) |
reason | String? | Reason for the reversal (max 255 chars) |
Example request
json
{
"date": "2024-04-02",
"reason": "Wrong account"
}Example response
Returns the journal transaction of the reversal. Its description is localized to the language of the organization or user, for example Reversal of TX-1001 (English), Storno von TX-1001 (German) or Extourne de TX-1001 (French). When a reason was given, it is appended after a colon, for example Reversal of TX-1001: Wrong account (cut at 255 characters).
json
// HTTP 201 Created
{
"id": "k3wQpLm8xa",
"number": "TX-1002",
"posted_at": "2024-04-02",
"description": "Reversal of TX-1001: Wrong account",
"document_type": "manual_booking",
"reverses": {
"id": "v7rnRjBn9o",
"number": "TX-1001"
},
"reversed_by": null
// ...
}Reversal Restrictions
The endpoint returns a 422 validation error when
- the transaction is not a manual booking (error on
id), - the booking was already reversed (error on
id), - the original manual booking has no journal transaction because it was never booked, for example after a failed booking (error on
id: "This booking isn't posted, so it can't be reversed."), datelies in a closed fiscal year or VAT period (error ondate).
A booking in a closed fiscal year can still be reversed: without date, the reversal is posted on the first open date.
Delete a manual booking
delete
/journal_transaction/{id}
Transaction Deletion Restrictions
- Locked transactions cannot be deleted
- Transactions from closed fiscal years cannot be deleted (system enforced)
- Manual booking transactions will also delete their associated document
- With the immutable journal enabled, bookings created before today can't be deleted. The request returns
422withcode: "immutable_journal". Reverse the booking instead - A booking that has been reversed can't be deleted, regardless of the setting. The request returns
422withcode: "reversed_booking". Delete the reversal booking first - Consider the impact on financial reports before deletion
Example response
json
// HTTP 204 No ContentWhen deleting is blocked by the immutable journal (the message is localized):
json
// HTTP 422 Unprocessable Entity
{
"message": "The journal is immutable. Bookings from earlier days can't be deleted – reverse them instead.",
"code": "immutable_journal"
}When the booking has been reversed:
json
// HTTP 422 Unprocessable Entity
{
"message": "This booking has been reversed and can't be deleted. Delete the reversal first.",
"code": "reversed_booking"
}Immutable journal
Organizations can enable the immutableJournal organization setting (default false). While it is enabled, a manual booking created before today (time zone Europe/Zurich) can neither be deleted nor edited in place:
- Deleting it returns
422withcode: "immutable_journal". - Updating it via
POST /journalwithidreverses the original onreversal_date(default:default_reversal_date) and creates a new booking with the submitted data. The response is the new booking (201), which gets a copy of the original's attachment unless a newfileis uploaded. - Reversing it works as usual.
Bookings created today can still be edited and deleted. Once enabled, the setting can't be disabled again (422).
The journal transaction fields can_delete, can_edit_in_place, edit_requires_reversal and can_reverse tell clients which of these actions are available for a booking, see Reversal and immutability fields.