Fraud & Fraud AI
Decision Logs

Decision Logs

Every fraud decision is recorded permanently. The records exist for reconciliation: afterwards you read which decision a payment received, which rules matched, and which channel the decision came from.

GET /api/v1/fraud/decision-logs            (list)
GET /api/v1/fraud/decision-logs/{traceId}  (single)

Authentication is the same as the Decision API: a bearer token carrying the fraud:view_decisions scope. Only your own account's records are returned — the tenant scope is resolved from the token.

List

Filters

ParameterTypeMeaning
paymentIdstringThe payment reference you sent in the decision request
decisionstringallow · review · block · force_3d
integrationModestringstandalone for external callers
productContextstringThe product surface (for example direct_api, payment_form)
limitnumberPage size. Default 50, valid range 1–200
cursorstringThe pageInfo.nextCursor value of the previous response

limit is a page size, not a ceiling. Pass the page size you want and fetch the following pages with cursor; you reach every record however many there are.

Paging

The order is newest first, and the sort key is the record's creation instant combined with its id — so two decisions created in the same millisecond are never skipped at a page boundary.

pageInfo is a sibling of data, not nested inside it:

{
  "status": "success",
  "code": "SUCCESS",
  "data": [ { "traceId": "…" } ],
  "pageInfo": {
    "hasMore": true,
    "nextCursor": "eyJ2IjoxLCJ0IjoiMjAyNi0xMC0wNVQwOToxMjo0MC4xMjM0NTZaIiwiaWQiOiIwMUo4WlUifQ",
    "totalCount": null
  },
  "requestId": "req_01J8ZV..."
}
  • When hasMore is false the list is exhausted.
  • totalCount is always null on this endpoint: a total count is expensive and is not needed for paging.
  • The cursor is opaque. Do not parse it; send it back verbatim. A corrupted cursor does not silently fall back to the first page — it returns 400 INVALID_CURSOR.

The full platform paging contract: Pagination.

Single record

GET /api/v1/fraud/decision-logs/{traceId}

You take traceId from the data.traceId field of the decision response. If you want to keep a reference to a decision, that is the only value you need to store.

Record fields

FieldMeaning
traceIdThe decision's unique id; the value to quote in a support request
decisionallow · review · block · force_3d
scoreRisk score
reasonCodesThe reason codes that produced the decision
matchedRules / matchedRuleGroupsMatched rules and rule groups
flowPathThe flow nodes the decision passed through
flowId / flowName / flowVersionId, name and version of the flow that decided
explainabilityJsonExplainability summary and group results
tags / actionsTags attached to the decision and the actions that fired
channel / surface / stage / operationKindThe request's channel taxonomy
authModeRequested / authModeAppliedRequested and applied authentication mode
integrationMode / productContextIntegration mode and product surface
subMerchantRefSub-merchant reference, if you sent one
transactionRefTransaction reference, if you sent one
assertionOutcomeVerification outcome of the identity artefact (on the merchant_form channel)
paymentId / customerIdThe references you sent in the request
latencyMsHow long the decision took to produce
createdAtWhen the decision was created

The record also carries the request that produced the decision. That is part of the audit trail: when asked why a decision was made, the context it was made with can be seen too.

Reading the decision for a payment

For payments taken through the Lunixi payment orchestrator, fraud already runs inside authorisation; you do not need to call evaluate separately. You read that payment's decision with the paymentId filter:

curl "https://api-gateway.lunixi.com/api/v1/fraud/decision-logs?paymentId=pi_123&limit=1" \
  -H "Authorization: Bearer $LUNIXI_TOKEN"

Next