Card-routing decision engine · UAE
Every purchase, routed to the right card.
The best card, the exact reward, and the reason it won — from one deterministic call.
One call
Merchant, amount, wallet. One decision.
The request carries card names, a merchant descriptor and an amount. No PAN, no track data, no CVV — there is nothing in this call that puts you in PCI scope.
{
"merchant": "CARREFOUR MOE DUBAI", // raw descriptor
"amountAed": 420,
"cards": [ // names, never PANs
"Dubai First Cashback",
"ADCB TouchPoints Infinite",
"ADCB Traveller"
]
}
{
"bestCard": "Dubai First Cashback",
"bestRewardAed": 21,
"bestRatePct": 5,
"category": "groceries",
"categoryResolved": true,
"deltaVsRunnerUpAed": 19.99,
"reason": "5% on groceries — the best of your 3 cards.",
"perCard": [ … ], // all cards, best first
"rulebookVersion": "2.0.0", // replay this decision
"rulebookReviewedAt": "2026-07-17",
"latencyMs": 0.02
}
Use this card
Dubai First Cashback
5% on groceries. The best of the 3 cards in this wallet.
Earns
AED 21.00
on AED 420 · groceries
+AED 19.99 vs next-best
this call: 0.02 ms · across 20,000 mixed calls: median 0.03 ms, p99 0.08 ms · Rulebook 2.0.0
AED 1 on the wrong card. AED 21 on the right one.
That gap repeats on every purchase your cardholders make. Sikka closes it in the moment, and keeps your cards top of wallet.
What it is
Six properties, no asterisks.
Deterministic
No model in the path. Same inputs and same Rulebook version give the same answer, every time.
Verified Rulebook
Rates read from published T&Cs and signed off by a human. No verified rule means a card earns zero — never an estimate.
Stateless
Nothing is written. Run it inside your own perimeter and the call makes no outbound connection.
Explainable
Every decision carries the reason, the rate, the category, and the value against the next-best card.
Merchant resolver
202 descriptor patterns resolve noisy transaction text to one of 7 categories. Unresolved merchants are flagged, not guessed.
Multi-issuer
One neutral layer across portfolios. 16 verified UAE cards today, from 5 issuing banks.
How it decides
Three steps, one function.
1
Resolve
The raw descriptor maps to a category — groceries, fuel, dining. Anything unresolved routes at the everyday base and is flagged.
2
Score
Every held card is scored at its highest verified rate for that category. A monthly cap is applied to this purchase alone — see the note below on what stateless costs you.
3
Route
Return the winner, the gap to the runner-up, and the reason. Points and cashback compare on one AED scale.
Buy, not build
The one question every team asks.
The maintenance grind
Rates, caps and exclusions change constantly. Keeping the Rulebook true is a permanent job. It is ours, not your roadmap.
The accuracy gap
Resolving noisy merchant descriptors is the hard part, and it is the part that is never finished.
Neutral by design
One shared layer, credible across portfolios. An in-house build is captive to a single book.
Live today
Deterministic, demoable, ready to pilot. Every number on this page comes from the running engine.
Integration
A drop-in decision call.
Send the wallet and the purchase
POST /v1/decision with the raw merchant, the amountAed, and the card names held.
Get the decision back
Deterministic JSON: best card, reward, gap to runner-up, reason, and the Rulebook version it was computed against.
Surface it on your rails
A pre-purchase "use this card" nudge, a post-authorisation reward attribution, or a statement line.
Host it in your perimeter
Stateless, with no outbound calls. The Rulebook ships versioned and freshness-checked.
const decision = await decide(txn, { timeoutMs: 40 }) .catch(() => null); // timeout or unreachable if (decision === null) { // authorise anyway. The nudge is dropped, // the payment is never held up. return authorise(txn); } if (decision.deltaVsRunnerUpAed > 1) { nudge(cardholder, decision.bestCard, decision.reason); }
Fail-open
The engine sits beside authorisation, never inside it. On timeout or unavailability the caller authorises and drops the nudge. It cannot decline a payment, and it is not a dependency of your uptime.
PCI scope: zero
The request carries a merchant descriptor, an amount, and card names. No PAN, no track data, no CVV, no cardholder identifier crosses the boundary — so nothing here enters your card-data environment.
Auditable
Every response carries rulebookVersion and rulebookReviewedAt. Replay the same inputs against the same version and you get the same decision — so any nudge can be defended months later.
Sikka provides
- The maintained UAE Rulebook — rates, caps and exclusions, versioned and freshness-checked.
- The merchant resolver — descriptor to category, no model in the path.
- The routing engine — the decision, the value gap, and the reason.
- Multi-issuer neutrality, and the maintenance grind off your roadmap.
You provide
- The rails — BIN, scheme, PCI, tokenisation, the authorisation flow.
- The wallet context — which cards a cardholder holds.
- The surface — nudge, statement, or product.
- The distribution to cardholders.
API reference
The decision contract.
POST /v1/decision — request
| Field | Type | Description |
|---|---|---|
| merchant | string | The raw merchant descriptor from the transaction feed. Passed through as-is; the resolver handles the noise. |
| amountAed | number | The purchase amount in AED. |
| cards | string[] | The wallet, as card names. A catalog entry matches when its key is contained in the name you send, case-insensitively — so "ADCB TouchPoints Infinite Credit Card" resolves. Never a PAN or a token. |
200 /v1/decision — response
| Field | Type | Description |
|---|---|---|
| bestCard | string | null | null when no held card earns a verified reward here. A null answer is a real answer. |
| bestRewardAed | number | The AED the winning card earns on this purchase. |
| bestRatePct | number | The effective reward rate as a percentage. |
| category | string | The resolved category. Falls back to everyday. |
| categoryResolved | boolean | false when the descriptor could not be resolved. Routed at base, never guessed. |
| deltaVsRunnerUpAed | number | The AED gap to the next-best card — what a wrong tap would have cost. |
| reason | string | A human-readable explanation, ready for a nudge or a statement line. |
| perCard | object[] | Every card scored, best first. Each entry is { card, rewardAed, ratePct, condition }, where condition is the min-spend or cap gate on that rate, or null. Render it: a gated rate the cardholder has not unlocked will not pay out. |
| rulebookVersion | string | The Rulebook release the decision was computed against. Required to replay it. |
| rulebookReviewedAt | string | ISO date that release was last reviewed. |
| latencyMs | number | Time to compute, measured live on the call. |
Deployment
The engine runs inside your perimeter — /v1/decision is the contract it exposes there, not a public endpoint we host. It is a pure function over the Rulebook: no database, no outbound connection, no shared state between calls.
Categories
Coverage, as of 13 August 2026
| Dimension | Now | Detail |
|---|---|---|
| Market | UAE | The engine itself is market-agnostic. Only the UAE catalog is published here. |
| Verified cards | 16 | Across 5 issuing banks: FAB including the Dubai First brand (7), ADCB (4), HSBC (2), Mashreq (2), Wio (1). |
| Merchant patterns | 202 | A case-insensitive substring table, resolving to 7 categories. No fuzzy matching: a descriptor that matches nothing returns unresolved. The table is shared across markets, so it also carries non-UAE brands. |
| Decision latency | 0.03 ms | Median. p99 0.08 ms, p99.9 0.15 ms, worst observed 4.2 ms — over 20,000 decisions on wallets of 3 to 16 cards. The integration budget is 100 ms, which is why fail-open costs you nothing. |
| Rulebook | 2.0.0 | Last reviewed 2026-07-17. Returned on every decision. |
Talk to us
Send us a wallet. We will route it, live.
Then decide whether to buy it or build it. Either answer is useful. What we are proposing is a paid, non-exclusive pilot on one card portfolio — fixed scope, a named integration owner, and a path measured in weeks.
praneet@usesikka.comBuilt by engineers from low-latency exchange systems, where deterministic sub-millisecond lookups are the everyday.