Decision responses
A successful submission returns 201 with the message Transaction evaluated and OneView's decision. The decision
is final for the synchronous call, but OneView never moves money: your system enforces the outcome. This page
shows a complete response for every outcome, and what your integration must do with each one.
Response structure
{
"status": true,
"message": "Transaction evaluated",
"data": {
"transaction": {
"id": "1a3a72bc-425d-4dfd-b63c-abb80d9b2e5d",
"transactionReference": "TXN-20260921-000101",
"direction": "OUTBOUND",
"receivedAt": "2026-09-21T09:20:00.318Z",
"replayed": false
},
"decision": {
"outcome": "PASS",
"riskScore": 0,
"evaluatedAt": "2026-09-21T09:20:00.329Z",
"evaluationMs": 9.214,
"complete": true,
"rulesEvaluated": 18,
"rulesetSnapshot": "9ffa3b395a9799b1a1104ab1b536ed1667b487510829ceefc27593941fafb35e",
"gradingVersion": 3,
"triggeredRules": []
}
}
}
| Field | Meaning |
|---|---|
transaction.id | OneView's ID for the stored transaction. Store it: you need it to look the transaction up later. |
transaction.transactionReference, transaction.direction | Echo the values you sent. Together they identify the transaction. |
transaction.receivedAt | When OneView first stored the transaction. A replay returns the original time. |
transaction.replayed | false for a new transaction, true when this is a repeat of an earlier identical request. |
decision.outcome | PASS, FLAG, HOLD, BLOCK or NOT_EVALUATED. |
decision.riskScore | An integer from 0 to 100, set by the outcome. See Risk score bands below. |
decision.evaluatedAt, decision.evaluationMs | When the decision was made and how long evaluation took, in milliseconds. |
decision.complete | true when every rule in scope ran. false means the decision is partial. |
decision.rulesEvaluated | How many live rules applied to this transaction. |
decision.rulesetSnapshot | A fingerprint of the exact rules and thresholds used, or null when rules did not run. |
decision.gradingVersion | The version of your risk score grading that set the score, or null. |
decision.triggeredRules | Live rules that matched, up to 20. Each entry has ruleId, ruleVersion, name and action. |
decision.skippedRules | Present only when complete is false. Rules that could not run, with a reason. |
The response does not include rule evidence, per-rule reasons, or a case or alert ID. Analysts see the full
explanation in OneView. Rules running in shadow mode are evaluated but never appear in triggeredRules and never
change the outcome.
What to do with each outcome
| Outcome | What it means | What your system must do |
|---|---|---|
PASS | No control produced a hit. | Continue. |
FLAG | The transaction needs analyst review after the fact. | Continue. The money moves, and there is nothing to release later. |
HOLD | An analyst must decide before the transaction completes. | Suspend the transaction, then follow it up until an analyst records RELEASED or REJECTED. See Following up a held transaction below. |
BLOCK | The transaction must not proceed. | Stop or reverse the transaction in your own systems. |
NOT_EVALUATED | No control ran, so OneView made no risk judgement. | Apply your own policy. Never treat it as approval. |
HOLD and BLOCK have effect only if your system enforces them.
PASS
{
"status": true,
"message": "Transaction evaluated",
"data": {
"transaction": {
"id": "30a24bb5-77b9-4729-b766-848340173042",
"transactionReference": "TXN-20260921-000201",
"direction": "OUTBOUND",
"receivedAt": "2026-09-21T08:12:44.918Z",
"replayed": false
},
"decision": {
"outcome": "PASS",
"riskScore": 0,
"evaluatedAt": "2026-09-21T08:12:44.930Z",
"evaluationMs": 7.806,
"complete": true,
"rulesEvaluated": 18,
"rulesetSnapshot": "9ffa3b395a9799b1a1104ab1b536ed1667b487510829ceefc27593941fafb35e",
"gradingVersion": 3,
"triggeredRules": []
}
}
}
All 18 rules in scope ran and none matched, and no other control produced a hit. A PASS with
rulesEvaluated: 0 means no rule applied to this kind of transaction, for example because every rule is scoped to
other payment methods.
FLAG
{
"status": true,
"message": "Transaction evaluated",
"data": {
"transaction": {
"id": "ca7bc841-d264-4074-8cea-a6552785b59d",
"transactionReference": "TXN-20260921-000102",
"direction": "OUTBOUND",
"receivedAt": "2026-09-21T09:25:03.412Z",
"replayed": false
},
"decision": {
"outcome": "FLAG",
"riskScore": 35,
"evaluatedAt": "2026-09-21T09:25:03.431Z",
"evaluationMs": 11.482,
"complete": true,
"rulesEvaluated": 18,
"rulesetSnapshot": "9ffa3b395a9799b1a1104ab1b536ed1667b487510829ceefc27593941fafb35e",
"gradingVersion": 3,
"triggeredRules": [
{
"ruleId": "4bf544b6-957d-46cf-a55b-ea89a3ff9e02",
"ruleVersion": 4,
"name": "Large outbound transfer",
"action": "FLAG"
}
]
}
}
}
One rule matched with the action FLAG. name is the rule's name in the version that ran, and ruleVersion tells
you which version that was. Let the transaction complete. It appears in the analyst review queue.
HOLD
{
"status": true,
"message": "Transaction evaluated",
"data": {
"transaction": {
"id": "4f4624e4-304a-4c17-90dc-ea4954fc4076",
"transactionReference": "TXN-20260921-000901",
"direction": "OUTBOUND",
"receivedAt": "2026-09-21T02:14:26.705Z",
"replayed": false
},
"decision": {
"outcome": "HOLD",
"riskScore": 50,
"evaluatedAt": "2026-09-21T02:14:26.722Z",
"evaluationMs": 13.057,
"complete": true,
"rulesEvaluated": 18,
"rulesetSnapshot": "9ffa3b395a9799b1a1104ab1b536ed1667b487510829ceefc27593941fafb35e",
"gradingVersion": 3,
"triggeredRules": [
{
"ruleId": "d8790c99-5ec6-4258-8af8-9c37e11427c2",
"ruleVersion": 2,
"name": "Night-time transfer through VPN",
"action": "HOLD"
},
{
"ruleId": "4bf544b6-957d-46cf-a55b-ea89a3ff9e02",
"ruleVersion": 4,
"name": "Large outbound transfer",
"action": "FLAG"
}
]
}
}
}
Two rules matched. The most severe action wins (FLAG is less severe than HOLD, which is less severe than
BLOCK), so the outcome is HOLD and the score is the start of the HOLD band. Suspend the transaction and keep the
transaction.id to follow it up.
BLOCK from rules
{
"status": true,
"message": "Transaction evaluated",
"data": {
"transaction": {
"id": "d3a3b00d-a659-484d-a756-6a2cbe42c638",
"transactionReference": "POS-20260921-000714",
"direction": "OUTBOUND",
"receivedAt": "2026-09-21T18:22:07.264Z",
"replayed": false
},
"decision": {
"outcome": "BLOCK",
"riskScore": 75,
"evaluatedAt": "2026-09-21T18:22:07.281Z",
"evaluationMs": 15.339,
"complete": true,
"rulesEvaluated": 12,
"rulesetSnapshot": "5c0e8a1f93d24b6e8f7a2c1d0b9e8f7a6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f",
"gradingVersion": 3,
"triggeredRules": [
{
"ruleId": "c7f14528-daa8-486d-8743-6dd3cec7931c",
"ruleVersion": 1,
"name": "Card used at more than 5 merchants in 10 minutes",
"action": "BLOCK"
}
]
}
}
}
Stop the transaction. For a card purchase, decline the authorisation or reverse it in your card system.
BLOCK from screening, with no triggered rules
{
"status": true,
"message": "Transaction evaluated",
"data": {
"transaction": {
"id": "2b7d9e41-6c3a-4f58-9a0e-1d4c7b3e5f60",
"transactionReference": "SWF-20260921-000031",
"direction": "OUTBOUND",
"receivedAt": "2026-09-21T12:10:00.583Z",
"replayed": false
},
"decision": {
"outcome": "BLOCK",
"riskScore": 75,
"evaluatedAt": "2026-09-21T12:10:00.611Z",
"evaluationMs": 8.947,
"complete": true,
"rulesEvaluated": 18,
"rulesetSnapshot": "9ffa3b395a9799b1a1104ab1b536ed1667b487510829ceefc27593941fafb35e",
"gradingVersion": 3,
"triggeredRules": []
}
}
}
This is a valid response, not a fault. triggeredRules lists rules only. Rules are one of several controls that
can decide the outcome:
- blacklist screening, when a value in the transaction matches your blacklist,
- PEP and sanction screening of the account holder's and counterparty's names,
- geography risk, when the transaction is linked to a high-risk jurisdiction.
An administrator chooses whether a hit from each of these controls flags, holds or blocks. A hit raises the outcome
and the score in the same way a rule does, with the most severe result winning across all controls, but it never
adds an entry to triggeredRules. Here all 18 rules ran and none matched, yet the payment is blocked because
another control produced a hit. Analysts see which control it was in OneView. Enforce the outcome exactly as you
would for a rule-driven BLOCK.
NOT_EVALUATED
{
"status": true,
"message": "Transaction evaluated",
"data": {
"transaction": {
"id": "7e5c3a19-0b2d-4f86-a1c4-9d8e2f6b0a37",
"transactionReference": "TXN-20260921-000105",
"direction": "OUTBOUND",
"receivedAt": "2026-09-21T09:40:11.106Z",
"replayed": false
},
"decision": {
"outcome": "NOT_EVALUATED",
"riskScore": 0,
"evaluatedAt": "2026-09-21T09:40:11.109Z",
"evaluationMs": 0.412,
"complete": true,
"rulesEvaluated": 0,
"rulesetSnapshot": null,
"gradingVersion": null,
"triggeredRules": []
}
}
}
The transaction was stored, but no control ran. This happens when the licence includes transactions but not rules,
and no other control is licensed and enabled. It can also happen briefly when the rule set is not ready yet, in which
case complete is false and skippedRules is an empty array.
If rules did not run but another control, such as blacklist screening, ran without a hit, the outcome is PASS
rather than NOT_EVALUATED, because a control did run.
NOT_EVALUATED is never approval. Decide in advance what your system does with it, for example let low-value
transactions through and send the rest for manual review.
Partial decisions
When some rules could not run, the response is still 201, but complete is false and skippedRules names the
rules that were skipped.
{
"status": true,
"message": "Transaction evaluated",
"data": {
"transaction": {
"id": "5a8f1c27-3d9e-4b60-8e2a-7c1f0d9b4e53",
"transactionReference": "TXN-20260921-000106",
"direction": "OUTBOUND",
"receivedAt": "2026-09-21T10:05:42.871Z",
"replayed": false
},
"decision": {
"outcome": "FLAG",
"riskScore": 35,
"evaluatedAt": "2026-09-21T10:05:42.903Z",
"evaluationMs": 31.625,
"complete": false,
"rulesEvaluated": 18,
"rulesetSnapshot": "9ffa3b395a9799b1a1104ab1b536ed1667b487510829ceefc27593941fafb35e",
"gradingVersion": 3,
"triggeredRules": [
{
"ruleId": "4bf544b6-957d-46cf-a55b-ea89a3ff9e02",
"ruleVersion": 4,
"name": "Large outbound transfer",
"action": "FLAG"
}
],
"skippedRules": [
{
"ruleId": "8d2e6f14-7a3b-4c95-b0d1-3e9a5c7f2b86",
"reason": "AGGREGATES_UNAVAILABLE"
},
{
"ruleId": "b1c4e7a2-9f6d-4380-a5e2-6d0b8c3f1a79",
"reason": "RULE_EVALUATION_FAILED"
}
]
}
}
}
A skipped rule neither matched nor passed. The outcome comes only from the rules and controls that did run, so a skipped rule could have raised it.
reason | What it means |
|---|---|
AGGREGATES_UNAVAILABLE | The rule needs history, such as a count or total over a time window, and that history could not be read for this transaction. |
AGGREGATE_BUDGET_EXCEEDED | The rules in scope together needed more history calculations than one evaluation allows, so rules that need history were skipped. Your administrator can simplify or consolidate rules. |
RULE_EVALUATION_FAILED | The rule could not be evaluated, for example because of a problem in its definition. Your administrator should review the rule. |
EVALUATION_DEADLINE_EXCEEDED | Evaluation ran out of time before reaching this rule, so the rule was skipped to keep the response fast. |
How to handle a partial decision is your risk policy. Common choices are to act on the returned outcome as usual
(fail-open), or to treat a partial PASS or FLAG on higher-value transactions as needing manual review. Either way,
record complete with the decision.
Risk score bands
riskScore is not a sum of points. It is the lowest score of the band for the winning outcome, taken from the
risk score grading your administrator configured. The default bands are:
| Outcome | Default band | Default riskScore |
|---|---|---|
PASS | 0 to 34 | 0 |
FLAG | 35 to 49 | 35 |
HOLD | 50 to 74 | 50 |
BLOCK | 75 to 100 | 75 |
NOT_EVALUATED | None | 0 |
More hits with the same outcome do not raise the score, and they never push a transaction into the next band. Base
your logic on outcome, not on the score: if your administrator changes the grading, the scores change but the
outcomes do not. Keep gradingVersion with the decision if you keep your own audit record. See
Risk score grading.
Replays
If you send the same transactionReference and direction again with an identical body, OneView does not evaluate
the transaction again. It returns 200 with the message Transaction already recorded.
{
"status": true,
"message": "Transaction already recorded",
"data": {
"transaction": {
"id": "ca7bc841-d264-4074-8cea-a6552785b59d",
"transactionReference": "TXN-20260921-000102",
"direction": "OUTBOUND",
"receivedAt": "2026-09-21T09:25:03.412Z",
"replayed": true
},
"decision": {
"outcome": "FLAG",
"riskScore": 35,
"evaluatedAt": "2026-09-21T09:26:10.057Z",
"evaluationMs": 0.031,
"complete": true,
"rulesEvaluated": 0,
"rulesetSnapshot": null,
"gradingVersion": 3,
"triggeredRules": []
}
}
}
A replay returns a reduced decision:
transaction.idandreceivedAtare the original values, andreplayedistrue.outcome,riskScoreandgradingVersionare the stored decision. Act onoutcomeas usual.triggeredRulesis empty,rulesEvaluatedis0,rulesetSnapshotisnullandcompleteistrue. These do not mean the original evaluation was empty.evaluatedAtandevaluationMsdescribe the replay itself.
Keep the original 201 response if you need its triggered rules.
"Identical" means identical after validation. Key order and equivalent timestamps, such as 2026-09-21T10:15:00+01:00
and 2026-09-21T09:15:00Z, do not matter. Amount text does: "150000.00", "150000.0" and 150000 are different.
A different body under the same reference and direction is rejected with 409 IDEMPOTENCY_CONFLICT. So when you
retry, resend exactly the bytes you sent the first time.
The same reference with the other direction is not a replay. It is a separate transaction, which is how the two legs of an intra-bank transfer are sent. See Internal user counterparty.
Following up a held transaction
OneView does not call your system back. To learn an analyst's decision on a HOLD, look the transaction up with the
list endpoint, filtered by the transaction.id from the ingest response:
GET /api/transactions?transactionId=4f4624e4-304a-4c17-90dc-ea4954fc4076
Authorization: Bearer <oneview-service-account-api-key>
{
"status": true,
"message": "Transactions retrieved",
"data": {
"items": [
{
"id": "4f4624e4-304a-4c17-90dc-ea4954fc4076",
"transactionReference": "TXN-20260921-000901",
"externalReference": null,
"direction": "OUTBOUND",
"accountHolderType": "CUSTOMER",
"customerUniqueId": "CUS-100231",
"accountNumber": "0012345678",
"counterpartyType": "EXTERNAL_BANK",
"counterpartyAccountNumber": "9000000020",
"paymentMethod": "NIP",
"channel": "MOBILE_APP",
"status": "POSTED",
"amount": "480000.0000",
"currency": "NGN",
"decisionOutcome": "HOLD",
"riskScore": 50,
"executedAt": "2026-09-21T02:14:26.000Z",
"receivedAt": "2026-09-21T02:14:26.705Z",
"reviewedAt": "2026-09-21T08:03:19.442Z",
"reviewDisposition": "CLEARED",
"releaseDecision": "RELEASED"
}
],
"pagination": {
"page": 1,
"pageSize": 20,
"total": 1
}
}
}
| Field | Meaning |
|---|---|
decisionOutcome | The current stored outcome. It can be higher than the synchronous one after late screening. |
reviewedAt | null until an analyst reviews the transaction. |
reviewDisposition | The analyst's finding: CLEARED, SUSPICIOUS, ESCALATED or FALSE_POSITIVE. |
releaseDecision | For a held transaction: RELEASED (complete it) or REJECTED (do not complete it). null until reviewed, and always null for other outcomes. |
Poll at an interval that suits your process, and stop once releaseDecision is set. Other useful filters on the
same endpoint are decisionOutcome=HOLD with reviewState=unreviewed to list every held transaction still waiting,
and page and pageSize (1 to 100, default 20) to page through results.
Use this list endpoint. The single-transaction path GET /api/transactions/{id} is not available to API service
accounts and returns 403 SERVICE_ACCOUNT_INTEGRATION_ONLY.
Late screening
PEP and sanction screening, when enabled, normally finishes before OneView responds. If it does not finish in time,
the response is returned with the decision as it stands, and screening continues in the background. A late match can
later raise the stored outcome to HOLD or BLOCK. OneView does not notify your system when this happens.
If your process needs the final state, for example before releasing a high-value payment, read the transaction again
through the list endpoint by transactionId and act on decisionOutcome.