Oracle04
Check & address pages
Most people who need the oracle are not integrators; they are about to pay someone. /check takes whatever they have (an address, an EIP-681 or CompliR payment link, an invoice PDF), runs the same pipeline every API client gets, and answers in three sentences. The answer lives at /a/<address>, a public page anyone can open, share and re-check.
Plain-language rules
The headline comes from the tier, the advice from the strongest one or two reasons. No model is involved; the mapping is deterministic and tested.
- HIGH
- “Don't pay.” + the top reasons, e.g. “This wallet is on the OFAC sanctions list and Circle has blocked it on USDC.” (SANCTIONS_DIRECT + USDC_BLACKLISTED merge into one sentence)
- MEDIUM
- “Pay up to N USDC.” where N is the analyst's
maxPerTxUsdc, + the top reasons, e.g. “Its funders two hops back include a sanctioned wallet.” - LOW
- “Safe to pay.” + “No sanctions, no blocklist, normal history.” or the mild reasons seen, followed by “Nothing serious.”
- Ranking
- reasons sorted by weight, CLEAN never counts; the sanctions list is named OFAC when a hit's datasets include an OFAC/SDN source, otherwise “a sanctions list”
Inputs: address, link, invoice
- Address
- 0x + 40 hex characters; lowercased
- Payment link
- EIP-681
ethereum:0x…@5042?value=…(native) orethereum:<token>@5042/transfer?address=0x…&uint256=…(ERC-20: recipient fromaddress=, amount fromuint256=at 6 dp); a CompliRhttps://…/pay/<id>link (address from the query/path, else the id is resolved via/api/v1/pay/<id>when that module answers); any URL carrying an address in path, query or hash - Invoice (PDF)
POST /api/v1/check/invoice(multipartfile, ≤ 5 MB, ≤ 20 pages) extracts the text server-side and returns every address, a best-effort vendor name (a “From:” / “Vendor:” / “Payee:” line, else the first name-like line) and the total. Nothing is stored. The browser keeps a vendor → address list inlocalStorage; the same vendor with a different address raises “Address changed”.
POST /api/v1/check # free · same-origin unmetered · 20/min per IP otherwise
{ "address": "0x098b716b8aaf21512996dc57eb0615e2383e2f96", "source": "address" } // source: address | link | invoice; name?, amountUsdc?, full?
→ 200
{
"address": "0x098b…2f96", "tier": "HIGH", "score": 100,
"headline": "Don't pay.",
"advice": "This wallet is on the OFAC sanctions list and Circle has blocked it on USDC.",
"limits": { "maxPerTxUsdc": 0, "maxPerDayUsdc": 0 },
"reasons": ["SANCTIONS_DIRECT", "USDC_BLACKLISTED"], // strongest first, CLEAN omitted
"decisionId": "dec_…", "attestation": { … }, "screenedAt": "…",
"shareUrl": "https://trycomplir.xyz/a/0x098b716b8aaf21512996dc57eb0615e2383e2f96"
}Checks run under the internal client id check, so they are attested and ledgered like any screen but never counted as outside traction. Pass full: true to also receive the complete verdict (evidence, funder graph, rationale); the developer view on the address page does.
Address page & history
/a/<address> shows the latest headline, advice, limits and tier chip; counters; guarantee / bond and inbound CCTP flags when another module attached them; open due diligence; the network signal from /api/v1/network/<address>; then every verdict ever issued (date, tier, score, who asked as a class, attester, registry transaction), tier changes, and a collapsible developer view with the full VerdictView. “Re-check now” runs a fresh same-origin check and refreshes the page.
GET /api/v1/address/0x098b716b8aaf21512996dc57eb0615e2383e2f96 # free · never runs the pipeline
{
"address": "0x098b…2f96",
"latest": { "decisionId", "tier", "score", "screenedAt", "headline", "attester", "registryTx", "validUntil", "expired" },
"verdicts": [ { "decisionId", "screenedAt", "tier", "score", "reasonCodes", "requester": "own|outside|unknown", "attester", "registryTx", "validUntil", "tierChanged" }, … ],
"tierChanges": [ { "at", "from", "to", "decisionId", "via": "verdict|watch" } ],
"openEdd": [ { "id", "url", "createdAt", "questions" } ],
"counts": { "verdicts", "high", "medium", "low", "outsideRequests" },
"firstSeenAt", "anonymised": true, "generatedAt"
}Requesters are shown as own (CompliR's own UI and workers), outside (a paying integrator) or unknown; a client id never leaves the store. The former /explorer redirects here: /explorer → /check, /explorer?address=0x… → /a/0x….