Oracle03

Screening & verdicts

A screen is one POST with an address; the answer is a verdict you can act on, verify and replay. Everything the pipeline looked at is in the response, including the evidence, so the same decision can be reproduced later from the ledger entry.

Request

POST /api/v1/screen costs 0.01 USDC over x402. Same-origin browser calls (the hosted Explorer) are free; with X402_DISABLED=1 every call is free. The response carries X-CompliR-Decision-Id.

http
POST /api/v1/screen
Content-Type: application/json
PAYMENT-SIGNATURE: <x402 payload>        # omitted on the first call; the 402 tells you what to sign

{
  "address":    "0x098b716b8aaf21512996dc57eb0615e2383e2f96",   // required, EVM address (lower-cased); this one is a real OFAC-listed wallet
  "name":       "Acme Supplies Ltd",                             // optional, 2–200 chars, name-based sanctions matching
  "amountUsdc": 120,                                             // optional, ≥ 0; ≥ 1,000 forces the second hop
  "payeeBook":  [{ "label": "Lela", "address": "0x12ab…9f3c" }], // optional, ≤ 500 entries; lookalike + payee-change detection
  "clientId":   "treasury-7",                                    // optional, ≤ 100 chars; scopes the network signal and watches
  "depth":      2                                                // optional, 1 | 2; otherwise the pipeline decides
}

Validation errors are 400 with { "error": { "code": "VALIDATION_ERROR", "message", "details" } } and are never charged. A paused service answers 503 SERVICE_PAUSED.

Response

json
{
  "address": "0x…", "score": 37, "tier": "MEDIUM",
  "recommendedLimits": { "maxPerTxUsdc": 250, "maxPerDayUsdc": 1000 },
  "reasons": [
    { "code": "FUNDER_2HOP_SANCTIONED", "weight": 15,
      "evidence": { "hop": 2, "count": 1, "hits": [{ "funder": "0xSDN", "path": ["0xSDN", "0xB", "0x…"], "txs": ["0x…"], "sanctions": [ … ] }] } },
    { "code": "FRESH_WALLET", "weight": 8, "evidence": { "firstSeen": "2026-10-06T…", "txCount": 3, "triggers": { "youngerThan7d": true, "fewerThan5Txs": true } } }
  ],
  "rationale": "…written by the Risk Analyst, identifiers verified against the evidence…",
  "eddRequested": ["Please confirm the relationship with 0xB and the source of funds behind it."],
  "edd": { "id": "edd_…", "url": "https://trycomplir.xyz/edd/edd_…" },
  "nextRescreenAt": "2026-10-10T03:00:00Z",
  "attestation": {
    "typedData": { "domain": { "name": "CompliR", "version": "1", "chainId": 5042, "verifyingContract": "0x…" },
                   "types": { "Attestation": [ … ] }, "primaryType": "Attestation",
                   "message": { "subject": "0x…", "tier": 2, "score": 37, "decisionHash": "0x…", "issuedAt": 1760000000, "validUntil": 1760086400 } },
    "signature": "0x…", "attester": "0x…", "validUntil": 1760086400, "registryTx": null
  },
  "decisionId": "dec_…", "screenedAt": "2026-10-09T…",
  "evidence": { "subject": { … }, "funders": [ … ], "lookalikes": [], "payeeChanged": null, "concentration": null,
                "priorTier": null, "network": { … }, "communityTips": [], "depth": 2, "collectedAt": 1760000000 }
}

Verdict anatomy

score
0–100 integer from the deterministic rules. Sum of reason weights, clamped; a hard hit floors it at 61.
tier
LOW 0–20 · MEDIUM 21–60 · HIGH 61–100. On-chain enum: UNKNOWN 0, LOW 1, MEDIUM 2, HIGH 3.
recommendedLimits
Per-tx and per-day USDC the analyst recommends, always inside the tier band (below). Adopt them directly or take the minimum with your own policy.
reasons[]
Each { code, weight, evidence }. Codes are a closed set; evidence carries whatever a reviewer needs to click: paths, tx hashes, entity ids, timestamps. Sorted by weight desc, code asc.
rationale
Plain English from the Risk Analyst (≤ 120 words). Identifiers it cites are checked against the evidence; unknown ones are removed.
eddRequested
0–3 Enhanced Due Diligence questions, MEDIUM/HIGH only. When present, `edd.url` is a public page the counterparty can answer; answers are hashed into the ledger and trigger a re-screen.
nextRescreenAt
ISO time the analyst chose inside the tier window. Watches use it as their schedule.
attestation
EIP-712 typed data, signature, attester address, validUntil and the registry tx (null until the on-chain write lands). Verify offline with the SDK or MCP.
decisionId
Id of the SCREEN / RESCREEN entry in the hash-chained log: GET /api/v1/decision/:id or /log/:id.
evidence
The full bundle the rules scored: subject facts, hop-1 and hop-2 funders with paths, lookalikes, payee change, concentration, prior tier, anonymised network signal, community tips, depth and collectedAt.

Limit bands

The analyst picks values inside the band for the tier; clampLimits guarantees it can never recommend more, never a negative or non-numeric value, and that the daily limit is at least the per-transaction limit. HIGH is always 0 / 0.

TierScoreMax per txMax per dayAttestation valid
LOW0–2010,000 USDC50,000 USDC7 days
MEDIUM21–60250 USDC1,000 USDC24 hours
HIGH61–100001 hour

These are also the defaults when the model is unavailable: the fallback returns the band ceiling, the top of the re-screen window, and a rationale that cites the top three reasons.

Free reads after a screen

  • GET /api/v1/attestation/:address - the latest stored verdict and attestation (source: "store"); an address never seen is screened once (source: "fresh").
  • GET /api/v1/decision/:id - the ledger entry: payload, prev/this hash, anchor tx.
  • GET /api/v1/network/:address - the anonymised cross-client signal (held elsewhere, concentration hint, blocklist events).
  • GET /api/v1/badge/:address.svg - a "Screened by CompliR" badge from the stored attestation.