Skip to content

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 ​

TypeDescription
SingleSimple transaction with exactly two journal entries (one debit, one credit)
CompoundComplex 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)TypeDescription
idString?ID of existing booking to update (omit for new transaction)
reversal_dateDate?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 *DateTransaction posting date (must be within open fiscal year)
message *StringTransaction description (max 255 chars)
currencyString?Booking currency code (e.g. EUR, USD) - defaults to CHF. Foreign currencies must be configured for the organization
conversion_rateNumber?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)
typeString?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 *StringAccount ID for debit entry
credit_account_id *StringAccount ID for credit entry
amount *NumberTransaction amount in the booking currency (must be > 0)
vat_rate_idString?VAT Rate ID
Compound Transaction Mode
entries *ArrayArray of journal entries (min 2 entries)
entries.*.account_id *StringAccount ID
entries.*.debit_amountNumber?Debit amount (≥ 0)
entries.*.credit_amountNumber?Credit amount (≥ 0)
entries.*.vat_rate_idString?VAT Rate ID
entries.*.descriptionString?Entry-specific description (max 255 chars)
entries.*.currencyString?Entry currency - defaults to the booking currency
File Attachment
fileFile?Supporting document (max 10MB, PDF/images/Office docs)

Validation Rules

  • Single mode: Requires debit_account_id, credit_account_id, and amount
  • Compound mode: Requires entries array with balanced debits/credits
  • Exactly one mode must be used: a request without entries must contain the single-mode fields, and vice versa. A request providing neither is rejected with 422 (not silently accepted)
  • type: single additionally prohibits entries; type: compound additionally requires entries
  • 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 currency must 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 FiscalYearClosedException if 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)TypeDescription
currency *StringCurrency code (e.g. EUR, JPY)
dateDate?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)TypeDescription
dateDate?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)
reasonString?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."),
  • date lies in a closed fiscal year or VAT period (error on date).

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 422 with code: "immutable_journal". Reverse the booking instead
  • A booking that has been reversed can't be deleted, regardless of the setting. The request returns 422 with code: "reversed_booking". Delete the reversal booking first
  • Consider the impact on financial reports before deletion

Example response ​

json
// HTTP 204 No Content

When 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 422 with code: "immutable_journal".
  • Updating it via POST /journal with id reverses the original on reversal_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 new file is 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.