Receiving the events
Reconciliation publishes what happens to till audits and store days as events. An external system subscribes through External Events. Hii Retail services subscribe to the Pub/Sub topics directly.
| External Events source | Version | Pub/Sub topic | Published when |
|---|---|---|---|
REC: Reconciliation completed (reconciliation-completed) | v2 | rec.public.event.reconciliation-completed.v2 | a till audit is approved, by a count within the limit or by approval with an explanation |
REC: Reconciliation rejected (reconciliation-rejected) | v2 | rec.public.event.reconciliation-rejected.v2 | a count is outside the limit, once per attempt |
REC: Reconciliation recount (reconciliation-recount) | v2 | rec.public.event.reconciliation-recount.v2 | a completed till audit is recounted |
REC: Reconciliation change shift (reconciliation-change-shift) | v1 | rec.public.event.reconciliation-change-shift.v1 | a till audit is completed: its deviation per shift |
REC: Reconciliation end of day (reconciliation-end-of-day) | v1 | rec.public.event.end-of-day-completed.v2 | a store's day is submitted and closed |
REC: Cash balance (cash-balance) | v1 | rec.public.event.cash-balance.v1 | nightly, where switched on: the expected cash left in each store |
⚠ The end-of-day source is version v1 in External Events, and reads the Pub/Sub topic version v2. Subscribe to
reconciliation-end-of-day in External Events, or to rec.public.event.end-of-day-completed.v2 on Pub/Sub. Both
deliver the same message.
Each source's JSON Schema is linked from External Events. The schemas are also on the event format pages.
Which one to take:
- Accounting and ERP:
reconciliation-end-of-day, one message per store and business date with the totals per tender. - Following each till:
reconciliation-completedandreconciliation-recount. - Loss prevention and statistics: add
reconciliation-rejectedfor every failed attempt, andreconciliation-change-shiftfor the deviation per shift.
Rules for consuming the events
1. Expect duplicates, and know the key
Delivery is at least once, so a message can arrive twice. Skip duplicates by:
| Event | Deduplication key |
|---|---|
reconciliation-completed | reconciliationId. A till audit completes once. |
reconciliation-rejected | reconciliationId + timestamp. A till audit can be rejected several times, once per attempt. |
reconciliation-recount | recountId |
reconciliation-change-shift | reconciliationId |
reconciliation-end-of-day | businessUnitId + endOfDayDate + timestamp. A day can be summarised more than once. |
cash-balance | the tenant + businessDate |
2. Do not depend on the arrival order
Events can arrive in a different order from the one they happened in. Order them by timestamp: the time of the
count, approval or submission that produced the event.
3. Know which numbers are levels and which are changes
| Event | Fields | Meaning |
|---|---|---|
reconciliation-completed, -rejected | expectedAmount, actualAmount, deviationAmount, and the quantities | levels for this till audit. deviationAmount is counted minus expected, so negative means short. |
reconciliation-recount | expectedAmount, actualAmount | levels, as recounted |
reconciliation-recount | amountCorrection, quantityCorrection | changes: recounted minus the previous count of the same till audit |
reconciliation-end-of-day | expectedAmount, actualAmount, deviationAmount, and the quantities | levels for the whole day |
reconciliation-end-of-day | the fields ending in Delta | changes since the previous summary of the same store and date |
reconciliation-change-shift | deviationAmount / deviationAmountInShift | the deviation at the count / the change during the shift |
End of day: totals and deltas
The first summary of a store's day carries the totals, with each delta equal to its total. If the day is summarised again, because more till audits for the date completed later, the next summary carries the new totals, with the deltas holding the difference from the previous summary.
🛑 Never add up the totals of two summaries for the same day. Either book the deltas of every summary, or replace the day with the totals of the newest one.
4. A recount changes a till audit you have already received
A reconciliation-completed is not necessarily the last word on a till audit. A recount can follow, until the day is
closed. If you book till audits individually, book the recount's corrections too. If you only take the end-of-day
summary, recounts are already included in it, because a till audit cannot be recounted after the day is closed.
5. Read the business date, not the publishing time
businessDate on a till audit, and endOfDayDate on a summary, is the trading day the figures belong to. A
summary can be published days after its date. A till audit that spans midnight carries transactionTenderDates: its
amounts split by the date of each transaction, for reports that need calendar days.
6. Read tolerantly
- Ignore fields you do not recognise. Fields are added within a version, and the published schemas do not reject extra fields.
- Handle enum values you do not recognise.
reconciliationTypeisCASH_DRAWERorCASH_CHANGERtoday. - Many amount and quantity fields are
nullwhen they do not apply: a tender counted only by quantity has no counted amount, and a read-only tender has no deviation. Only the fields the schema lists as required are always present.
Message attributes
On Pub/Sub, each message carries these attributes, which you can use for filtering:
| Attribute | Value |
|---|---|
Tenant-Id | the tenant |
Business-Unit-Id | the store. Not set on cash-balance, which covers every store of a tenant. |
Correlation-Id | a trace ID for the request that led to the event |
The events
Reconciliation completed and rejected
Both use the same format: the till audit, every tender with its expected, counted and allowed amounts, the counted
items, the transaction IDs included, and who counted. reconciliationType tells a drawer from a cash changer.
comment holds the explanation when the till audit was approved after a deviation.
{
"tenantId": "tenant-1",
"businessUnitId": "store-12",
"correlationId": "f3a6c1d0-5b8e-4a5e-9a7a-2f0d3c9b1e44",
"reconciliationId": "tenant-1-store-12-WORKSTATION-CASH_DRAWER-415",
"reconciliationType": "CASH_DRAWER",
"workstationId": "till-1",
"operatorId": null,
"userId": "8c1f0d0e-3b6a-4c39-9d0b-5c2e7f1a6b10",
"comment": null,
"timestamp": "2026-09-30T18:05:12.481Z",
"timezoneOffset": "+02:00",
"businessDate": "2026-09-30",
"tenders": [
{
"tenderId": "CASH-NOK",
"currencyId": "NOK",
"isReadOnly": false,
"initialBalance": 2000,
"expectedAmount": 1630,
"actualAmount": 1620,
"deviationAmount": -10,
"allowedDeviationAmount": 20,
"expectedQuantity": 3,
"actualQuantity": null,
"deviationQuantity": null,
"allowedDeviationQuantity": null,
"isCashChanger": false,
"denominations": [
{ "denominationValue": 500, "quantity": 2 },
{ "denominationValue": 200, "quantity": 3 },
{ "denominationValue": 20, "quantity": 1 }
]
}
],
"items": [],
"transactionIds": ["…"],
"transactionTenderDates": [
{ "tenderId": "CASH-NOK", "dateValue": "2026-09-30", "amount": 630, "quantity": 4 }
]
}
The example is till 1 from How the expected amount is built: 1 630 expected, 1 620 counted. The
two quantities measure different things. expectedQuantity counts tender lines in the drawer, where the deposit to
the safe counts as −1, so 4 sales and returns less 1 deposit give 3. transactionTenderDates.quantity counts the
transactions on each date: 4.
Format: Reconciliation completed and rejected.
Reconciliation recount
The till audit as recounted, with each tender's correction against the previous count. In the example the manager found the missing 10:
{
"tenantId": "tenant-1",
"businessUnitId": "store-12",
"correlationId": "0b7d2e94-61c3-4f0e-8a54-3e1f9c7d2a65",
"reconciliationId": "tenant-1-store-12-WORKSTATION-CASH_DRAWER-415",
"recountId": "6d2a9f41-0c8e-4b7a-9e35-1f4b8c2d7e90",
"reconciliationType": "CASH_DRAWER",
"workstationId": "till-1",
"operatorId": null,
"userId": "2a4e6c8b-1d3f-4a5c-8e7b-9c0d1e2f3a4b",
"comment": "10 note found under the drawer tray",
"timestamp": "2026-09-30T19:40:03.112Z",
"businessDate": "2026-09-30",
"tenders": [
{
"tenderId": "CASH-NOK",
"currencyId": "NOK",
"isReadOnly": false,
"expectedAmount": 1630,
"actualAmount": 1630,
"amountCorrection": 10,
"expectedQuantity": 3,
"actualQuantity": null,
"quantityCorrection": null,
"isCashChanger": false
}
],
"items": []
}
Format: Reconciliation recount.
Reconciliation change shift
The till audit's counts in time order, each a shift, with who counted and the deviation per tender, at the count and within the shift. See Shifts.
Format: Reconciliation change shift.
Reconciliation end of day
One store and business date: the till audits included, and per tender and item the day's expected, counted and
deviation totals with their deltas. comment holds the store manager's explanation when the store was outside its
limit.
Format: Reconciliation end of day.
Cash balance
One message per tenant and business date: per store and currency, the cash expected to remain in the drawers and cash changers at the end of the day. See Closing the day.
Format: Cash balance.
Events used inside Hii Retail
Reconciliation also publishes reconciliation-created (a till audit started, with its float), transfers (cash loans
and deposits) and a warning when a drawer's expected amount crosses its limits. They feed Cash Management and Hii
Retail's store applications, and are not offered through External Events.