Appearance
Transactions
Transactions represent card payment events associated with members. Ingesting transaction data enables the platform to match purchases against active deals, trigger cashback rewards, and track loyalty program progress.
The publisher software will be continuously syncing anonymized records of all ingested transaction data to the central K42 Platform for routing.
Transaction Format
Each transaction event represents a card payment and includes the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
member_id | string | yes | The member this transaction belongs to |
external_id | string | yes | Your unique identifier for this transaction |
auth_code | string | yes | Authorization code from the issuing bank |
network | string | yes | Card network: "visa", "mastercard", "amex", or "other" |
bin | string | yes | Bank Identification Number (first 6-8 digits of card) |
merchant_id | string | yes | Merchant identifier at the payment processor |
statement_descriptor | string | no | Statement descriptor from the merchant |
amount | integer | no | Transaction amount in minor units (e.g. cents) |
currency | string | no | ISO 4217 currency code (e.g. "GBP", "USD") |
authed_at | string | yes | ISO 8601 timestamp of when the card was authorized |
cleared_at | string | no | ISO 8601 timestamp of when the transaction settled |
reverted_at | string | no | ISO 8601 timestamp of when the transaction was refunded |
Amounts
All monetary amounts are expressed in minor units (the smallest denomination of the currency). For example:
- GBP 25.99 →
2599 - USD 100.00 →
10000
Ingesting Transactions
Send transaction events in batches via the ingest endpoint:
bash
curl -X POST https://<your-instance-url>/publisher.v1/transaction/ingest \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"events": [
{
"member_id": "m_x1y2z3",
"external_id": "txn_98765",
"auth_code": "AUTH123",
"network": "visa",
"bin": "411111",
"merchant_id": "MID_STORE_01",
"statement_descriptor": "ACME Co",
"authed_at": "2024-01-15T10:30:00Z"
}
]
}'Transaction Lifecycle
Transactions follow a lifecycle from authorization through to settlement or refund:
authorized → cleared (settled)
→ reverted (refunded)You can and should ingest transactions as soon as the card authorization occurs (with only authed_at set), then update it later when it clears or is reverted.
To update the transaction simply resubmit it with the additional recorded data making sure you reference the same external_id. For example, to mark a transaction as cleared (settled) set the cleared_at to the relevant timestamp:
json
{
"member_id": "m_x1y2z3",
"external_id": "txn_98765",
"authed_at": "2024-01-15T10:30:00Z",
...
"cleared_at": "2024-01-16T08:00:00Z"
}Just keep in mind that:
- A transaction that has been cleared cannot be reverted.
- A transaction that has been reverted cannot be cleared.
Attempting an invalid state transition results in an error.
Sending Finalized Data Directly
While you should send pre-finalized transaction data as soon as possible, you can also just ingest the completed event in one go with both authed_at and cleared_at or reverted_at set.
Idempotency
Transactions are deduplicated by external_id. Sending the same transaction event multiple times will not create duplicates, making it safe to retry failed requests.
Choosing an external_id
The external_id should be a unique identifier you generate for each transaction. The format of this identifier has a significant impact on ingestion performance so care should be taken here.
Use a time-sortable format like UUIDv7. Time-sortable identifiers are monotonically increasing which makes them efficient for BTree indexes - inserts and range scans perform well because new values are always appended near the end of the index rather than causing random page splits.
Avoid purely random identifiers (e.g. UUIDv4) as they fragment indexes over time and degrade write and query performance at scale.
What Gets Shared with the Platform
Transaction data you ingest is not sent verbatim to the K42 platform. The software is designed to protect you and your members' financial privacy.
Transaction Identifiers
Instead of forwarding raw card details, the Publisher computes transaction identifiers - one-way SHA-256 hashes derived from a combination of payment fields:
- Authorization code (
auth_code) - Card network (
network) - Bank Identification Number (
bin) - Merchant identifier (
merchant_id)
Multiple transaction identifiers may be computed containing a variety of combinations.
These identifiers allow the platform to correlate your publisher-side transactions with retailer-side payment records without either party exposing raw card data.
What the Platform Receives
| Data | Notes |
|---|---|
| Transaction identifiers (SHA-256 hashes) | Opaque, irreversible |
| Merchant ID | Required for routing to the correct merchant |
| Statement Descriptor | Required for routing to the correct merchant |
Timestamps (authed_at, cleared_at, reverted_at) | For lifecycle tracking and matching |
| Associated deal clip IDs | For cashback processing |
| Loyalty program enrollment IDs | For stamp tracking |
How Matching Works
Software running within the retailer independently computes the same hashes from their payment card details received from their acquirer.
The platform routes transactions to the relevant retailer using the provided mid and possibly the statement_descriptor if the MID is not enough to disambiguate.
The retailer-side software then correlates transactions by finding identical hash values from both sides. When a match is found and the transaction is associated with a clipped deal or loyalty program, the platform triggers the appropriate reward (cashback credit, stamp collection progress, etc.).
This design ensures that transaction matching works without any party having access to another party's raw payment data.
Transaction Lifecycle
After you ingest a transaction, it flows through the system and can trigger events that are communicated back to you via webhooks:
You ingest transaction
→ Publisher computes transaction identifiers
→ Platform routes to retailer for matching
→ Match found against clipped deal or loyalty program
→ Platform triggers reward and sends webhook eventCashback Rewards
When a transaction matches a clipped deal:
cashback.created- A cashback reward is triggered. You receive the amount, currency, and a reference back to the originating transaction viatransaction_ref(yourexternal_id).cashback.cleared- The cashback settles (when the underlying transaction clears).cashback.reverted- The cashback is reversed (if the underlying transaction is refunded).
Loyalty Program Progress
When a transaction matches a loyalty program:
qualifying_purchase.created- The transaction counts as a stamp toward the member's enrolled loyalty program.qualifying_purchase.voided- The stamp is voided (e.g. due to a refund).
When enough stamps accumulate to meet the program's threshold, a redemption is triggered automatically and a cashback.created event is sent with a loyalty-program-redemption source.
Correlating Events to Your Data
All webhook events include a transaction_ref field that corresponds to the external_id you provided when ingesting the transaction. Use this to correlate platform events back to your internal transaction records.
See the Cashback Events and Loyalty Events documentation for the full event payload reference.