Using Atomic Operations

What is an Atomic Operation

When an API in AIR is called, it may complete multiple actions within the platform. Examples of these actions are Crediting Points, Redeeming Accounts, and Creating Wallet Transactions.

Each one of the actions against an object is called an Atomic Operation. All of these actions are contained within the event message produced. The structure of the event message can be seen here.

Atomic Operations have a common base data structure. The different Atomic Operation Entities have their own data structures, which are always consistent for the given entity type. These Entity structures are the same data structures as are returned in API responses.

Atomic Operations exist to provide the downstream system consuming those events all the relevant data that has been created, updated or deleted. This enables many use cases to be implemented:

  • Data analytics
  • Event Triggers
  • Customer history analytics
  • Data Audit

This page walks through each of these Atomic Operations and the details within them.

Atomic Operations in Events

In some endpoints, the same data object might be both created and subsequently updated. In these instances, both the action which created the data entity and the update to that entity will be represented in the event payload. This point needs to be taken into account when building the mechanics to process events.

Given that a call to an endpoint might generate multiple Atomic Operations inside a single event definition, it makes more sense from a data storage and processing perspective to not focus on the event names, but rather the Atomic Operations within an event payload.

For example, if we wanted to create a data lake that holds details of all the accounts a customer has had created and redeemed in a wallet, we would look at all the incoming event payloads, look for any Atomic Operation of type walletAccountTransactionEntity and store the data in the data lake relevant to that data object.

Similarly, if we wanted to understand the number of points being transacted through the loyalty platform, we would look for the same walletAccountTransactionEntity and extract any account transactions that relate to a points account.

If we focus on the event name, it's possible that future enhancements or usages of the AIR platform will introduce a new event type that is not yet being consumed by the data warehouse, leading to a loss of data until work is completed to include this new event. By focusing on the Atomic Operations, we are able to immediately recognise all the transactional data flowing through the loyalty scheme with no further changes.

Data Structure

{
    "objectType": "walletAccountTransactionEntity",
    "operationType": "CREATE",
    "objectValue": {
        "foo": "bar"
    }
}

Every Atomic Operation has the structure as outlined above. This structure gives the downstream system the information it requires to identify the object type and the actions taken against that object as well as the full payload of that object as stored in AIR.

KeyDescription
objectTypeThe objectType field will contain the name of the entity described by this Atomic Operation. The list of possible values for this field are described below and provides the context of why they exist.
operationTypeThis describes the action taken on a data object. This can be CREATE, UPDATE or DELETE. The value in this field relates directly to the entity described by the objectType field.
objectValueThis is a JSON payload that holds the details of the entity. The structure of these objects changes based on the entity type and are described below.

Atomic Operation Object Types

The below table outlines the different Atomic Operation types that can be generated by AIR and a description of each of them.

Object Type

Description

walletTransactionEntity

A Wallet Transaction entity describes a Wallet Transaction in AIR. An example of when one is created might be the completion of a POS Checkout journey.

There are several actions in AIR that generate a Wallet Transaction, for example a POS Connect Settle, an Exchange, or a Goodwill transaction.

walletEntity

A Wallet entity describes a Wallet in AIR. When a new wallet is created, an event will contain a Wallet entity.

walletIdentityEntity

A Wallet Identity entity describes an identity value in AIR. When an identity is created in the platform, an event containing a Wallet Identity entity will be generated.

campaignEntity

A Campaign Entity is generated when a Campaign is created, updated or deleted in AIR. This object describes the full campaign payload that is created inside AIR.

schemeEntity

A Scheme Entity is generated when a Scheme is created, updated or deleted in AIR. This object describes the full Scheme payload that is created inside AIR.

walletAccountTransactionEntity

A Wallet Account Transaction Entity is created at any time an Account Transaction is completed against an account inside a Wallet in AIR (when called via the Wallet APIs).

For example, if a coupon is created in a member's wallet, a Wallet Account Transaction Entity will be created with the event of CREATE, or if progress is marked against a continuity account, an Entity will be created of type CREDIT.

Wallet Account Transaction Entities are the most frequently generated Atomic Operation as all transactional elements, be that a POS transaction, Digital Interaction or Call centre interaction will generate an Account Transaction.

walletConsumerEntity

A Wallet Consumer Entity is generated when a Wallet Consumer is created, updated or deleted.

tierMembershipEntity

A Tier Membership Entity is created each time a member has a Tier Membership added to their points Account. This might be automatically generated upon the creation of a Points Account.

tierMembershipTransactionEntity

A Tier Membership Transaction Entity is generated each time an action happens against a Tier Membership. For example, if a Checkout Transaction awards a member 100 tier points, a Tier Membership Transaction Entity will be generated providing details of what changes took place.

pointsRewardBankEntity

A Points Reward Bank Entity is generated when a Reward Bank is created, updated or deleted. This will contain details of the reward bank itself and not any of the rewards inside the reward bank. (See pointsRewardBankRewardEntity below).

pointsRewardBankRewardEntity

A Points Reward Bank Reward Entity contains details of a reward stored against a Reward Bank. It contains the details of the reward campaignId, the cost as well as the other data represented in this object.

pointsRewardBankWalletLinkEntity

A Points Reward Bank Wallet Link Entity is generated when retailers are using personalised reward banks. This entity holds the details of the object stored in AIR and it's status.

walletAccountEntity

A Wallet Account Entity is not generated automatically inside AIR but is used by backend jobs to synchronise data to a customer upon a specific request. Account information is shared via the walletAccountTransactionEntity. This object is nested inside the aforementioned entity.

planEntity

A Plan Entity is generated when a subscription object is created, updated or deleted inside AIR. It will contain all the details of the configured plan along with all of it's entitlements.

supplierUnitEntity

A Supplier Unit Entity is used to link a business entity to a Fund in AIR. A Supplier Unit Entity is created when these objects are created, updated or deleted inside AIR.

fundEntity

When using Funding, a Fund is created to repsrent a "pot" of available budget for an offer. When a fund is created, updated or deleted a Fund Entity is created.

fundTransactionEntity

When a transaction occurs that impacts a fund, a Fund Transaction Entity is created detailing the changes made to that Fund.

walletInviteEntity

A Wallet Invite Entity is generated when actions are completed against a Wallet Invite.

Atomic Operation Entity Structures

The data structures of the individual entities is the same structure as returned from an API. For example, when a Checkout transaction is completed and a Wallet Transaction is created, the same data object that represents the Wallet Transaction is returned on the API response as that included in the events.

Processing Notes

AIR holds the current state of an object. For example, an Identity might be created in AIR with a state of EARNONLY and then subsequently gets updated to EARNBURN. AIR does not need to know this historic state of the entity, however each time a change is made, an event is generated meaning a downstream system can know the full history.

These data requirements are managed by the downstream system. It's not possible to retrieve the history of objects in AIR. It's important to understand these data requirements in the downstream platform so the correct handling of the data can be considered and these considerations taken into the design of the downstream data systems.

Atomic Operation Handling Strategies

There are several use cases, each of them requiring different methods to store or process the data. It is also possible that multiples of these scenarios are possible at the same time.

Transactional Audit

If the objective of storing data from AIR is to provide an audit of actions taking place in the loyalty platform, the primary data object to be considering are a combination of walletAccountTransactionEntity and walletTranactionEntitiy objects. These two give a clear view of the transactional actions taking place within the loyalty scheme for a individual customer.

It will frequently be a requirement to link an account to the relevant resource to which it is linked (scheme, campaign etc). If this is required then the additional entities (E.g. campaignEntity etc) can also be stored.

Each walletAccoutTransacitonEntity contains the details of the id of the owning Resource, e.g. Campaign, Scheme, Programme, from which an account is linked, the walletId for a customer (where applicable), the type of event (E.g. CREDIT, REDEEM, EARN etc), the value of that action (for a CREDIT the amount will be the number of points credited. For a REDEEM, this is the value of the reward given) as primary data points of interest.

If Tiering is enabled in the loyalty scheme, other transaction types of interest are the tierMembershipEntity and the tierMembershipTransactionEntity. These detail the changes made to the customers tier progression. A tier membership transaction entity is generated for CREDIT, DEBIT, ADJUST and MOVE transactions. These can be used to determine what actions are being completed and when a member moves tier (Either up or down based on earning and expiry periods).

An example of a walletAccountTransactionEntity is shown below:

{
    "objectType": "walletAccountTransactionEntity",
    "operationType": "UPDATE",
    "objectValue": {
        "accountTransactionId": "9631067530",
        "parentAccountTransactionId": null,
        "accountId": "4826198300",
        "account": {
            "accountId": "4826198300",
            "walletId": "249838676",
            "campaignId": "1577698",
            "type": "POINTS",
            "clientType": "RETAILPOINTS",
            "status": "ACTIVE",
            "state": "EARNONLY",
            "dates": {
                "start": "2025-11-21T16:52:26+00:00",
                "end": "2038-01-19T03:14:07+00:00"
            },
            "meta": [],
            "dateCreated": "2025-11-21T16:52:26+00:00",
            "lastUpdated": "2025-11-24T11:57:44+00:00",
            "overrides": [],
            "balances": {
                "current": 93055,
                "usable": 93055,
                "locked": 0,
                "lifetime": 93055,
                "pending": 0,
                "lifetimeSpend": 0,
                "lifetimeSpendValue": 0
            },
            "relationships": [],
            "mobileWallet": "https://sb.uk.mypass.is/a/4826198300/53b96ca801c5c13"
        },
        "event": "EARN",
        "value": 1125,
        "source": 1,
        "balancesBefore": {
            "current": 91930,
            "lifetime": 0
        },
        "balancesAfter": {
            "current": 93055,
            "lifetime": 0
        },
        "transactionDetails": {
            "merchant_store_id": "102",
            "merchant_store_parent_id": "zonaltest"
        },
        "properties": [],
        "dateCreated": "2025-11-24T11:57:44+00:00",
        "lastUpdated": "2025-11-24T11:57:44+00:00"
    }
}
{
    "objectType": "tierMembershipTransactionEntity",
    "operationType": "CREATE",
    "objectValue": {
        "tierMembershipTransactionId": "73039957",
        "tierId": "74",
        "tierIdAfter": null,
        "tierMembershipId": "16516350",
        "transactionType": "CREDIT",
        "parentTierMembershipTransactionId": null,
        "walletTransactionId": "541376305",
        "dateCreated": "2025-11-24T11:57:44+00:00",
        "lastUpdated": "2025-11-24T11:57:44+00:00",
        "actions": [
            {
                "balanceType": "POINTS",
                "amount": 1125,
                "balanceBefore": 90930,
                "balanceAfter": 92055
            },
            {
                "balanceType": "TRANSACTIONS",
                "amount": 0,
                "balanceBefore": 0,
                "balanceAfter": 0
            },
            {
                "balanceType": "SPEND",
                "amount": 0,
                "balanceBefore": 0,
                "balanceAfter": 0
            }
        ]
    }
}
{
    "objectType": "tierMembershipEntity",
    "operationType": "UPDATE",
    "objectValue": {
        "tierMembershipId": "16516350",
        "tierId": "74",
        "status": "ACTIVE",
        "accountIds": [
            "4826198300"
        ],
        "balances": {
            "points": 92055,
            "spend": 0,
            "transactions": 0
        },
        "expiryDate": "2026-05-31T23:59:59+00:00",
        "tierBalancesResetDate": "2026-05-31T23:59:59+00:00",
        "dateCreated": "2025-11-21T16:52:26+00:00",
        "lastUpdated": "2025-11-21T16:52:26+00:00"
    }
}

Data Modification Audit

If the requirement is to know what and how data is changed, then storing a version history of entities is important. As AIR only sends the current state of an object, the data lake needs to keep the history for a given entity. Each entity in AIR will have it's own "primary" identifier. For example a Wallet Transaction has a walletTransactionId and an Account Transaction has the accountTransactionId.

It is worth noting that once created, items like an Account Transaction are highly unlikely to ever change (there are edge cases where transactions are enriched later with information but this is a very edge case scenario). It is far more likely that a "Resource" will be updated:

  • Campaigns
  • Scheme
  • Reward Bank
  • etc.

Triggered Actions

Another use case for Atomic Operation events is for a downstream platform to trigger an action based on updates in AIR. These transactional events can be fed into a downstream system, the message processed in what ever way necessary and then discarded as there is no requirement to store the data longer term.

Actions like this a common for triggering marketing emails to customers. For example: "Congratulations on your first shop using your loyalty card. We hope you had a pleasant experience. We'd like to hear your feedback" type messages.