Skip to main content

Messages to other services

When a store's rates change, Exchange Rates publishes a message so that the other Hii Retail services can follow.

TopicTells youReceived today by
cer.public.event.rates-changed.v1the rates a store uses: new, changed and removedStore Data

The topic is for Hii Retail services. It is not offered through External Events: a customer's own system is normally where rates come from, and it can read the current state from the Exchange Rates API.

The topic's name starts with cer, after the Currency & Exchange Rates service that Exchange Rates replaced. The name and the format are kept on purpose, so that the services already subscribing keep working unchanged.

One message per store​

A rate set for a group or for the whole company reaches many stores. Exchange Rates works out which rates each affected store now uses and sends a message for each store whose rates changed. So a subscriber never has to know the company tree.

The company's SALES rate for EUR changes to 11.3205. Exchange Rates sends a message for each store that uses it, Stockholm and Strömstad, with the rate both ways under updatedRates. Landvetter has a rate of its own, which did not change, so it gets no message. The store is in the Business-Unit-Id attribute.

A message carries the rates that changed for its store, in three lists:

ListMeans, for this store
createdRatesa rate the store did not have before
updatedRatesa rate the store already had, with a new value, or the rate with a new markup
deletedRatesa rate the store no longer has

A list with nothing in it is left out. Treat a missing list and null the same way.

  • Both directions are always there. When a rate is set in one direction only, the message also carries the other direction, worked out as 1 ÷ rate.
  • A store with a rate of its own is not affected by a change above it, and gets no message for that change.
  • Nothing is sent when nothing changed. The nightly ECB import sends messages only on the nights when a rate actually moves.

The message​

{
"tenantId": "your-tenant-id",
"businessUnitId": "stockholm",
"updatedRates": [
{
"exchangeRateType": "SALES",
"sourceCurrencyCode": "SEK",
"targetCurrencyCode": "EUR",
"rateValue": 11.3205,
"rateValueWithMarkup": 11.09409,
"createdDate": "2026-03-02T02:00:04.512Z"
},
{
"exchangeRateType": "SALES",
"sourceCurrencyCode": "EUR",
"targetCurrencyCode": "SEK",
"rateValue": 0.088335,
"rateValueWithMarkup": 0.086568,
"createdDate": "2026-03-02T02:00:04.512Z"
}
]
}
Field
tenantId, businessUnitIdalways present. businessUnitId is the store the message is about.
exchangeRateTypealways present: SALES, PURCHASE or INTERCOMPANY
sourceCurrencyCodealways present: the currency the rate is counted in, the API's currencyIdFrom
targetCurrencyCodealways present: the currency being priced, the API's currencyIdTo
rateValuealways present: the rate, up to six decimals
rateValueWithMarkupthe rate with the store's markup: lowered by that percentage, in both directions, so the store keeps its margin whether a customer pays in the currency or gets money back in it. Equal to rateValue when there is no markup.
createdDatealways present: when the store first got this rate. It stays the same when the rate changes later.
createdBy, lastModifiedcan be present, can be absent

A rate is identified by the store, exchangeRateType, sourceCurrencyCode and targetCurrencyCode together.

Message attributes:

Attribute
Tenant-Idthe tenant
Business-Unit-Idthe store the message is about
Correlation-Idthe id of the change

Reading a rate in a message​

One targetCurrencyCode is worth rateValue of sourceCurrencyCode. sourceCurrencyCode SEK, targetCurrencyCode EUR, rateValue 11.3205 means 1 EUR = 11.3205 SEK: multiply a euro amount by it to get kronor. It is the same reading as in the API, see Reading a rate.

🛑 Pick the row by both currencies, never by one. Between SEK and NOK the two directions are 1.0465 and 0.9555. A subscriber that takes the wrong row gets a number that looks reasonable and is about 9 % wrong. Both rows are in every message, so there is never a reason to turn a rate round.

Receiving it correctly​

  • A message can arrive more than once. Treat createdRates and updatedRates as this is the store's rate now and deletedRates as the store no longer has this rate. Applied that way, a repeated message changes nothing. Key each rate on the store, the type and both currencies.
  • Messages can arrive in any order. The body has no time of change: createdDate is when the store first got the rate. If the same rate changes twice in quick succession, use the Pub/Sub publish time to keep the newest, or read the store's rates again from GET /api/v1/exchange-rates?businessUnit={businessUnitId}.
  • Filter on the attributes. A subscription can filter on Tenant-Id or Business-Unit-Id to receive only the tenants or stores it serves.
  • Choose between rateValue and rateValueWithMarkup deliberately. rateValue is the rate as it was set or imported, and rateValueWithMarkup is the same rate with the store's markup taken off. Without a markup they are equal, so a subscriber that uses the rate with markup works for stores with and without one.
  • Ignore fields you do not recognise. The format is kept as it is for existing subscribers, and a reader that tolerates extra fields is safe either way.

Sending the current state again​

A Hii Retail service that starts subscribing, or has lost its copy, can ask for the current state to be sent again:

POST https://exchange-rates.retailsvc.com/api/v1/initial-state

{ "tenantId": "your-tenant-id", "businessUnitId": "optional-store-id" }

Leave out businessUnitId to have every store of the tenant sent. The API answers 200 with a correlation id. Permission: exr.initial-state.create.

Exchange Rates then sends one message per store, listing every rate the store uses, both directions and with markup, in updatedRates.

⚠ The messages go to every subscriber of the topic, not only to the one that asked. Every subscriber must therefore cope with receiving again what it already has. A subscriber that follows the rules above does.