Platform internals
Card events & transactions
Everything that happens to a card lands in cardevents. That table is where support questions get answered, so learn its shape well.
The menu item is Activity. The page is titled Transactions for Bridge Admins and Redemptions for org users. All of it is this one table. The only real difference is the Excel export: the redemption export filters to Purchase, Return, and Void events.
How events arrive
InComm decides at the register, then tells us afterward. The pipeline:
- InComm's webhook posts the result to our ingest service. We store the raw body in
incommtransactioningestsand acknowledge immediately. Nothing is processed on the request. - A background job picks up pending ingests in batches (100 at a time, 10 in parallel), with retries and a manual-review state for failures.
- The job writes the
cardeventsrow (idempotent per event id), the per-itemincommskuleveltransactionsrows, and updates the card balance. For prefunded programs it also decrementsprograms.TotalCurrentBalance.
Consequence: portal activity always lags the register by a little, and a stuck job means missing events, not missing purchases.
It carries everything that matters: the result, the retailer, the amounts, the purses, and the per-item detail. Start there and usually end there. You rarely need incommtransactioningests; it is only the raw webhook queue, useful when you suspect an event never got processed at all.
The 13 event types
| Type | Meaning |
|---|---|
Creation | The card row was created. |
Activation | The member activated the card. |
IssuerActivation | The same event as Activation. The name is Fiserv's: Walmart's processor calls its activation transactionType "issuerActivation", and we keep the label. |
FirstTransaction | First spend on the card. |
Purchase | An approved purchase. |
Return | A refund at the register. |
Void | A voided transaction. The portal can display a void-of-return as "Purchase", so check the raw event. |
HardDecline | InComm refused the transaction. See the decline table below. |
Expiration | The card expired. |
Deactivation | The card was deactivated. |
CardUnload / BalanceAdjustment / ForcedAdjustment | Balance was removed or adjusted outside a purchase. |
Inside SourceData
cardevents.SourceData is a JSON document stored as text in MySQL. Its useful branches:
| Path | What you find there |
|---|---|
result | code ("Approval" or a decline code), description, timestamp. |
origination | retailerId, retailerName, store, lane, local time. store is the retailer's own store number, not a location. lane is the register. |
transaction | Amounts: original, clamp, approved. Plus authorization id and type. |
cardholder | pan and ingenId. |
marketBasket.purses[] | One entry per purse touched. Carries the purse name, remaining balance, amount authorized, and the covered items[]. |
marketBasket.purses[].items[] | Per covered item: code (a 14-digit InComm UPC), quantity, redemption amount, price adjustment. |
The items[] array lists only items the purse covered. On a decline the array is empty, and that emptiness proves nothing about what the member scanned. Use receipt arithmetic to reason about the rest of the basket.
Per-SKU rows are also denormalized into incommskuleveltransactions (one row per item, joined by CardEventId). Walmart redemptions arrive by settlement file into walmartfileimporttransactions instead. See The Walmart triangle.
Hard declines
The decline reason sits in SourceData.result.description (the code field just says HardDecline). Newer events carry plain-English labels; older ones carry camelCase IngenDecline* names for the same reasons. The full set seen in production, measured 2026-08-03 across 10,673 hard declines:
| Reason (as shown) | Older label | Meaning |
|---|---|---|
| UNMET ITEM QUALIFICATION | IngenDeclineUnmetQualification | No eligible items in the basket. Stores report it as "APL - No Eligible Items". |
| INSUFFICIENT BALANCE | AccountErrorInsufficientFunds | The purse cannot cover the eligible amount. |
| CLIP DELETED | IngenDeclineClipDeleted | The clip was removed from the customer. See the clip-deleted orphan trap in The clip lifecycle. |
| Card/PIN/ESN is Invalid | The card details were entered wrong at the register. Usually cashier handling. | |
| CUSTOMER STATUS INACTIVE | The card is not active: never activated, deactivated, or expired. | |
| RETAILER NOT INCLUDED | The store's banner is not on the offer's banner list. | |
| CUSTOMER LIMIT EXCEEDED | An offer limit (clip, purchase, customer) was hit. | |
| Transaction not supported on Partner / external system error | An InComm-side or retailer-side processing failure, nothing to fix on ours. |
Sometimes this decline fires even though the basket did contain eligible items. That is an InComm-side problem we are working with them to resolve. Until it is resolved, treat this code as a starting point, not proof that the items were ineligible; this page will be updated when the fix lands.
In records from before July 2025, the decline code and description fields are swapped. Query both fields when you search history.
Retailer and store identification
- Retailer name:
incommskuleveltransactions.RetailerName, orSourceData.origination.retailerName, or a readableSourceLocationCodelikeretailerId-retailerName. - Retailer id:
RetailerId/SourceData.origination.retailerId. - Store: only the retailer's internal store number. A numeric code alone cannot give you banner, city, or state without an external mapping.