---
module: transactions.processing-rules
title: Journal Processing Rules
tab: Internal
order: 20
audience: internal
---

Ops/support view of the **Journal Processing Rules** screen (Transactions app › Settings). The screen lives in the Transactions SPA but edits data owned by **hiveops-journal** — it reads and writes the `journal-rules.yml` file that drives the journal parser. It does **not** touch a database table.

## Who can use it

- Every admin endpoint behind this screen is gated by `@PreAuthorize("hasAnyRole('MSP_ADMIN','BCOS_ADMIN')")` on `AdminController` (hiveops-journal).
- A non-admin JWT gets **403** from the journal service even though the Transactions BFF let the request through (the BFF role check is broad; journal enforces the real gate). If a user sees an empty grid or a load error, check their role first.

## What Save & Apply actually does

1. Frontend PUTs the full rule set to `PUT /api/journal/admin/rules/structured`.
2. `JournalRulesService.saveStructuredRules()` rewrites the **entire** `transaction_types` map and `system_events` list into the YAML file at `JOURNAL_RULES_PATH`, then hot-reloads (`reloadRules()` clears the rules cache + all compiled patterns).
3. Response returns a new `version`; the UI shows `Saved and applied (vN)`.

Hot-reload takes effect immediately, no restart — **but only for journals parsed after the save**. Already-parsed files are not re-classified automatically (see below).

## First things to check when it misbehaves

- **"Save failed" with a message about `JOURNAL_RULES_PATH is not configured"`** — the upload container is running classpath-only. `saveStructuredRules`/`saveRulesContent` throw `IllegalStateException` (HTTP 400) when `JOURNAL_RULES_PATH` / `journal.rules.path` is blank. Fix: set the env var to a writable file path and redeploy; reads still work without it, only writes fail.
- **Grid loads empty / 500 on load** — `GET /api/journal/admin/rules/structured` failed. Usually the YAML on disk is malformed. Check `docker logs hiveiq-journal` for "Error loading structured rules". Fall back to the raw-YAML endpoints (`GET/PUT /api/journal/admin/rules`) to inspect/repair.
- **404 on any admin endpoint** — you hit a **worker** container, not the upload container. `AdminController` is `@ConditionalOnProperty(journal.role=upload, matchIfMissing=true)` — it only exists on the upload (front-end) role. Route admin traffic to `hiveiq-journal`, never `hiveiq-journal-worker-*`.
- **Changes don't show up in old data** — expected. Rule edits only affect new parses. To apply new rules to historical files, run a reparse (`POST /api/journal/admin/reparse-all`, optional `atmId` / `journalType`). This deletes existing transactions for those files and re-queues them — heavy operation, use deliberately.
- **A transaction is labelled wrong** — remember **first match wins, top to bottom**. A broad rule sitting above a specific one will swallow it. Reorder with the ↑/↓ arrows and Save.

## Transaction Types vs System Events — practical differences

- **Transaction Types** classify each transaction block. A rule matches on `JPR Contains` (text inside the JPR block) and/or `Op Code Matches` (one or more op codes). Blank `JPR Contains` = match any content; blank op codes = skip op-code matching. Only `WITHDRAWAL`, `DEPOSIT`, `REVERSAL` types are re-published downstream as Kafka transaction events (see Architect tab).
- **System Events** are pattern rules matched against journal lines **outside** transaction blocks. The picklist of event types is hard-coded in the frontend (`EVENT_TYPES`, 13 values: `NETWORK_CONNECTED/DISCONNECTED`, `POWER_ON/OFF`, `ATM_IN/OUT_OF_SERVICE`, `SUPERVISOR_ENTER/EXIT`, `OPERATOR_ACTION`, `CASSETTE_STATUS`, `CARD_READER_FAIL`, `CASSETTE_LOW`, `DISPENSER_JAM`). Adding a new event type means editing the Svelte component, not just the YAML.

> ⚠️ **Known gap (verify before promising a customer):** in the current parser, a System-Event match only increments the file's `event_count` — it does **not** create an incident event or a `journal_events` row. The UI copy ("Each match generates an incident event") overstates today's behavior. Don't debug a "missing incident from a system-event pattern" as a config problem; it's not wired to emit. Cassette-inventory events are the exception — those are written by a separate service path, not by this screen.

## Safe-change checklist

1. Confirm you're editing on the right environment (this screen writes to whichever `hiveiq-journal` upload container the API is pointed at).
2. Put specific rules above broad ones.
3. Save & Apply, watch for the `vN` bump.
4. If historical data must reflect the change, schedule a `reparse-all` (off-peak; it re-queues every matched file).
5. Verify with a fresh journal upload for a test ATM, then check the Transactions browser.
