Skip to main content

Receiving the events

Cash Management publishes every movement of a safe, and every safe audit, as events. An external system subscribes through External Events. Hii Retail services subscribe to the Pub/Sub topics directly.

External Events sourceEvent Source IDPub/Sub topicPublished when
CMM: Cash management transactions (transactions)cmm.transactions.v2cmm.public.event.transactions.v2anything moves cash into or out of a safe: one message per movement, see What moves the safe
CMM: Safe Audits (safe-audits)cmm.safe-audits.v1cmm.public.event.safe-audits.v1a safe audit is submitted

Each source's JSON Schema is linked from External Events. The schemas are also on the event format pages: Transactions and Safe audit.

Which one to take:

  • Accounting and ERP: transactions. Every movement of cash into and out of the safe, with the safe's balance after it. This is the one to book.
  • Control and follow-up at head office: safe-audits, to see each store's daily check, its deviation and its explanation.

šŸ›‘ Book the transactions, not the safe audits. A safe audit's deviation and bank drop are already published as a Correction and a BankDeposit transaction. Booking the safe audit's amounts as well counts them twice.

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:

EventDeduplication key
transactionseventId + eventType. Neither is unique on its own: a till audit's InitialBalance and Reconciliation share the till audit's id, and one safe audit's Correction, BankDeposit and customer-defined fields share the safe audit's id. Each line also has its own transactionId.
safe-auditssafeAuditId. The schema lists it as optional because it was added within version v1, without a new version. A message without it can be deduplicated on repositoryId + eventDateTime.

2. Do not depend on the arrival order​

Events can arrive in a different order from the one they were booked in.

  • eventDateTime is when the movement happened: the count, the transfer, or the user's action.
  • exportDateTime is when Cash Management booked it. The balances on a transaction are the safe's balance at that moment, so to keep the latest balance, take it from the transaction with the latest exportDateTime.
  • sequenceNumber numbers the store's transactions of one event type in the order they were booked.

3. Read the direction from the repository fields​

Each line of a transaction names the repository the cash left, the one it entered, or both:

One transaction line from a bank deposit, with each field explained. eventType BankDeposit: what happened. currencyId NOK and amount 2 625: how much. repositoryIdFrom: the safe the cash left; repositoryFromBalance 8 000: the safe's balance after it. referenceId BAG-1042: the bank bag. Because the cash left the safe, the safe went down by 2 625.

The line hasMeaningThe safe
repositoryIdTocash went into this repository+ amount
repositoryIdFromcash left this repositoryāˆ’ amount

amount is positive, except on a BankDeposit or BankWithdrawal that reverses an earlier mistake, which keeps its minus sign and carries a reasonCode: a BankDeposit of āˆ’500 from the safe puts 500 back into it. The rule above still gives the right result.

The other end of a movement, a till drawer or the bank, is not a repository. A float or a deposit from a drawer carries workstationId and, where the store counts per cashier, operatorId.

4. Know which numbers are levels and which are changes​

EventFieldsMeaning
transactionsamountthe change to the repository
transactionsrepositoryFromBalance, repositoryToBalancethe repository's level in that currency after the change
safe-auditsreconciledAmount, cashChangerAmountthe cash from till audits and cash changers in the period the audit covers
safe-auditsreconciledDeviationAmount, cashChangerDeviationAmountthe till audits' and cash changers' total deviation in that period
safe-auditssafeAmountthe level counted by hand: the rest of the safe, without that period's till and cash-changer cash
safe-auditssafeDeviationAmountsafeAmount minus what was expected. Negative means short. Booked as a Correction.
safe-auditsbankDepositAmountthe bank drop. Booked as a BankDeposit.

See The safe audit for how the safe-audit amounts fit together.

5. Read the business date, not the publishing time​

businessDate is the trading day a movement belongs to. A till audit counted after midnight, or recounted the next morning, still belongs to its own day, and so does the cash it puts into the safe. See The business date.

6. Read tolerantly​

  • Handle event types you do not recognise. The list grows, and each tenant's customer-defined safe-audit fields add their own. See What moves the safe.
  • Ignore fields you do not recognise. Fields are added within a version, and the published schemas do not reject extra fields.
  • Fields with no value are left out rather than sent as null. 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:

AttributeValue
Tenant-Idthe tenant
Business-Unit-Idthe store
Correlation-Ida trace ID for the request that led to the event

The events​

Transactions​

One message per movement: the store, the event type, who and which till, and one line per currency with the repository and its balance after the movement. Till 1's counted cash from What moves the safe:

{
"tenantId": "tenant-1",
"businessUnitId": "store-12",
"eventId": "tenant-1-store-12-WORKSTATION-CASH_DRAWER-415",
"eventType": "Reconciliation",
"eventDateTime": "2026-09-30T18:05:12.481000000Z",
"correlationId": "f3a6c1d0-5b8e-4a5e-9a7a-2f0d3c9b1e44",
"exportDateTime": "2026-09-30T18:05:13.207114000Z",
"businessDate": "2026-09-30",
"createdBy": "8c1f0d0e-3b6a-4c39-9d0b-5c2e7f1a6b10",
"workstationId": "till-1",
"transactions": [
{
"transactionId": "be7659ea-75d4-42f4-a120-74a51f072b94",
"currencyId": "NOK",
"amount": 1620,
"repositoryIdTo": "e6d2560c-8919-44bb-ab27-ac984364124e",
"repositoryToBalance": 10620,
"sequenceNumber": 1287
}
]
}

Format: Transactions.

The safe audit​

One message per submitted safe audit: who, which safe, and per currency the cash from the tills, the count, the deviation, the bank drop and the customer-defined fields. The audit from The safe audit:

{
"tenantId": "tenant-1",
"businessUnitId": "store-12",
"safeAuditId": "9b1c4e7a-2f3d-4b8e-a6c1-0d5e7f9a3b21",
"repositoryId": "e6d2560c-8919-44bb-ab27-ac984364124e",
"userId": "2a4e6c8b-1d3f-4a5c-8e7b-9c0d1e2f3a4b",
"eventDateTime": "2026-09-30T20:10:44.120000000Z",
"businessDate": "2026-09-30",
"audits": [
{
"currencyId": "NOK",
"reconciledAmount": 1630,
"reconciledDeviationAmount": 0,
"safeAmount": 8995,
"safeDeviationAmount": -5,
"bankDepositAmount": 2625,
"referenceId": "BAG-1042",
"additionalProperties": []
}
]
}

It produces two transactions, both with eventId equal to the safeAuditId:

One submitted safe audit produces three messages. The safe-audits message carries safeAuditId 9b1c…. A Correction transaction takes 5 out of the safe, leaving 10 625, and a BankDeposit transaction takes 2 625 out for bag BAG-1042, leaving 8 000. Both transactions carry eventId 9b1c…, the same id, so they can be linked to the audit and deduplicated on eventId plus eventType.

eventTypeLine
CorrectionNOK 5, repositoryIdFrom the safe, repositoryFromBalance 10 625
BankDepositNOK 2 625, repositoryIdFrom the safe, repositoryFromBalance 8 000, referenceId BAG-1042

Format: Safe audit.