PlatformXeDocs
Get API Key

Decide

Render a synchronous fraud verdict for a subject + action + resource.

Render a synchronous risk verdict. Always returns within the engine's p99 budget; the durable audit write is dispatched asynchronously so it never blocks the response.

Endpoint

POST /api/v1/fraud/decide

Scope: fraud:decide Plan gate: Detection Pack addon (Pro or Enterprise) Rate limit: 10,000 requests / hour per API key

Request body

FieldTypeRequiredDescription
subject.idstringYesThe actor performing the action (e.g. user id).
subject.kindstringYesTenant-defined kind, e.g. user, merchant, device.
subject.attributesobjectNoFreeform attributes evaluated by ABAC rules in Phase 6B.
actionstringYesThe action being attempted, e.g. transfer, withdraw, login.
resource.idstringNoThe resource the action targets, e.g. transaction id.
resource.kindstringYesThe resource type, e.g. transaction, withdrawal, account.
resource.attributesobjectNoFreeform resource attributes (amount, recipient, etc.).
context.ipstringNoClient IP. Redacted before persistence — present in audit trail as [REDACTED].
context.deviceFingerprintstringNoSHA-256 hash of a tenant-computed device fingerprint.
context.userAgentstringNoClient user-agent. Redacted before persistence.
context.geoHintstringNoTenant-supplied geo hint (country code).
context.amount{ value: number, currency: string }NoTransactional amount, evaluated by amount-based rules.
context.externalScore{ value: number, source: string }NoOptional behavioural-ML score the tenant ran upstream. 0–100.
idempotencyKeystringNoIf present and a recent decision exists for the same key on the same org, the cached verdict is returned.

Response

{
  "success": true,
  "data": {
    "decisionId": "dec_2j7yhq8wcs9...",
    "verdict": "allow",
    "score": 0,
    "reasons": [
      {
        "kind": "default",
        "code": "phase_6a_default_allow",
        "weight": 0,
        "detail": "No rules evaluated — Phase 6B wires the rule engine. Default allow."
      }
    ],
    "ttlSeconds": 30,
    "notice": "This verdict is informational and is provided for risk assessment. The tenant remains the decision authority for any action taken. See https://docs.platformxe.com/legal/fraud-detection-terms for full terms.",
    "decidedAt": "2026-05-03T09:14:21.412Z",
    "shadow": false,
    "latencyMs": 4
  }
}
FieldTypeDescription
decisionIdstringUnique id for this verdict; surface it to your reviewers and support tooling.
verdict"allow" | "review" | "step_up" | "block"The recommended outcome. The tenant remains the decision authority.
scorenumber (0–100)Aggregated risk score. Always 0 in Phase 6A; rule-driven in Phase 6B.
reasonsArray<{ kind, code, weight, detail }>Structured chain explaining the verdict. Safe to surface to ops/support. No PII.
ttlSecondsnumberIdempotency window — replaying the same idempotencyKey within this window returns the cached verdict.
noticestringMandatory liability disclaimer surfaced on every response.
decidedAtstring (ISO-8601)When the engine rendered the verdict.
shadowbooleantrue only for /shadow-decide responses.
latencyMsnumberEngine latency budget for this call (excludes network).

Verdict semantics

VerdictMeaningRecommended caller behaviour
allowNo risk signals triggered.Proceed with the action.
reviewSuspicion score above the review threshold but below step-up.Queue for human review or run a soft challenge.
step_upStronger signal — escalate authentication.Trigger an OTP / passwordless / MFA challenge before proceeding.
blockHigh-confidence risk or hard rule (sanctions hit).Reject the action and (optionally) open a case.

Error responses

HTTPCodeCause
400BAD_REQUESTMissing required fields or malformed body.
401UNAUTHORIZEDMissing or invalid API key.
402DETECTION_PACK_REQUIREDThe org has not enabled the Detection Pack addon.
403FORBIDDENAPI key has no organization context, or wrong scope.
429RATE_LIMITED10,000/hr ceiling exceeded.
500INTERNAL_ERRORUnexpected engine failure.

Examples

curl

curl -X POST https://api.platformxe.com/api/v1/fraud/decide \
  -H "Content-Type: application/json" \
  -H "x-api-key: pxk_live_your_api_key_here" \
  -H "x-idempotency-key: txn_8a91" \
  -d '{
    "subject": { "id": "usr_001", "kind": "user" },
    "action": "transfer",
    "resource": { "id": "txn_8a91", "kind": "transaction" },
    "context": {
      "amount": { "value": 50000, "currency": "NGN" }
    }
  }'

TypeScript SDK

import { PlatformXe } from '@caldera/platformxe-sdk';

const px = new PlatformXe({ apiKey: process.env.PLATFORMX_API_KEY! });

const verdict = await px.fraud.decide({
  subject: { id: 'usr_001', kind: 'user' },
  action: 'transfer',
  resource: { id: 'txn_8a91', kind: 'transaction' },
  context: { amount: { value: 50_000, currency: 'NGN' } },
  idempotencyKey: 'txn_8a91',
});

if (verdict.data.verdict === 'block') {
  // Tenant decides what blocking means in their system
  console.warn('blocked:', verdict.data.decisionId, verdict.data.reasons);
}

Performance

PercentileTarget
p5015ms
p9560ms
p99120ms

The hot path reads from Upstash Redis only. Durable persistence to fraud_decisions is fanned out via Inngest after the response is returned.