curl --request POST \
--url https://loyalty.xeniamoments.com/v1/onlinepos/sale \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--header 'OnlinePOS-Signature: <api-key>' \
--data '
{
"calculationId": "6f1d2c3e-8a4b-4c5d-9e0f-1a2b3c4d5e6f",
"status": "applied",
"transactionId": "987654321",
"receiptNumber": 10234,
"loyaltyId": "rk_8f2a1c9d4b7e",
"currency": "DKK",
"paidTotal": 16400,
"loyaltyDiscountTotal": 2600,
"occurredAt": "2026-10-08T21:14:09+02:00",
"context": {
"venueId": "123",
"cashRegisterId": "7",
"baxId": "601553",
"terminalId": "1001",
"clerkNumber": 12,
"channel": "pos"
}
}
'{
"calculationId": "6f1d2c3e-8a4b-4c5d-9e0f-1a2b3c4d5e6f",
"result": "recorded"
}Record sale outcome
Sent by the OnlinePOS backend exactly once per calculationId, after the transaction is finalised
or the payment is abandoned. Never blocks checkout. Replaces Version 1’s
loyaltySaleFinalization, loyaltySaleCancellation and loyaltyOfflineSale.
Retry on 429, 5xx and network errors with exponential backoff (up to 24 h) and the same
Idempotency-Key. Stop on any other 4xx.
The REKOM backend always answers 200 for a well-formed, authenticated event, including for a
calculationId it does not recognise (result: unknown_calculation), so that the sender never retries forever.
Field names and structures are directional (see the API description).
curl --request POST \
--url https://loyalty.xeniamoments.com/v1/onlinepos/sale \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--header 'OnlinePOS-Signature: <api-key>' \
--data '
{
"calculationId": "6f1d2c3e-8a4b-4c5d-9e0f-1a2b3c4d5e6f",
"status": "applied",
"transactionId": "987654321",
"receiptNumber": 10234,
"loyaltyId": "rk_8f2a1c9d4b7e",
"currency": "DKK",
"paidTotal": 16400,
"loyaltyDiscountTotal": 2600,
"occurredAt": "2026-10-08T21:14:09+02:00",
"context": {
"venueId": "123",
"cashRegisterId": "7",
"baxId": "601553",
"terminalId": "1001",
"clerkNumber": 12,
"channel": "pos"
}
}
'{
"calculationId": "6f1d2c3e-8a4b-4c5d-9e0f-1a2b3c4d5e6f",
"result": "recorded"
}Authorizations
HMAC-SHA256 request signature: t=<unix seconds>,v1=<hex> where
v1 = HMAC_SHA256(secret, "<t>.<raw body>"). Timestamp window ±300 s.
One secret per environment. No scheduled rotation; when a secret is swapped, old and new stay
active with an overlap so nothing is interrupted. See Configuration and security.
Headers
The calculationId. Same key with the same body replays the stored response; same key with a different body is rejected with 409.
Fresh UUID per HTTP attempt, echoed in the response for log correlation.
Body
Outcome of one calculation, sent exactly once per calculationId.
transactionId is required for every status except cancelled; failureReason is required when status is failed.
Lower-case UUID with hyphens (36 characters).
"6f1d2c3e-8a4b-4c5d-9e0f-1a2b3c4d5e6f"
applied— returned basket charged.unchanged— no-change response; original basket charged.member_not_found— no member; original basket charged.failed— call failed or response rejected; original basket charged.cancelled— payment abandoned after calculation; nothing charged.
applied, unchanged, member_not_found, failed, cancelled ISO 4217 currency code.
^[A-Z]{3}$"DKK"
"NOK"
"SEK"
"EUR"
Time the transaction was finalised or the payment abandoned.
"2026-10-08T21:14:09+02:00"
Where the basket is being sold. Field meanings mirror the OnlinePOS REST transaction API so reconciliation is a join.
baxId and cashRegisterId are required so REKOM can attribute every order to a venue and a till (agreed after 7 Oct 2026).
Show child attributes
Show child attributes
Required when status is failed.
timeout, connection_error, http_error, invalid_response, offline, pos_error OnlinePOS transaction id (transaction_id). Absent for cancelled.
"987654321"
OnlinePOS receipt number (receipt_number).
10234
Opaque REKOM member identifier as returned by Nexi Engage (getasset / Softpay). Contains no PII.
Exact format pending Nexi confirmation; treat as an opaque string.
1 - 64"rk_8f2a1c9d4b7e"
Amount actually charged. Absent for cancelled.
x >= 06500
Loyalty discount on the finalised transaction (0 unless applied).
x >= 06500
Response
Event accepted.
Lower-case UUID with hyphens (36 characters).
"6f1d2c3e-8a4b-4c5d-9e0f-1a2b3c4d5e6f"
recorded— stored.duplicate— an event for thiscalculationIdwas already stored; nothing changed.unknown_calculation— REKOM has no calculation with this GUID; the event is stored for reconciliation. Final; do not retry.
recorded, duplicate, unknown_calculation