Wallet Risk API quickstart

A 0-100 risk score for a cryptocurrency wallet address, derived from real, publicly published attribution data (GraphSense TagPacks): one API call, POST /v1/wallet-risk/score, part of the same Nexiel Screen service you may already be integrated with.

Preview. This endpoint, its sandbox mode, and the OpenAPI spec below are all live today. There is no pricing tier for it yet, so use it to evaluate wallet risk scoring rather than as a dependency in production traffic for now.

What this is, and what this isn't

This is not the same thing as wallet address sanctions screening. Nexiel Screen's existing POST /v1/screen endpoint already has an optional walletAddresses field: that is exact-match screening against a list of addresses a sanctions authority (currently OFAC SDN) has directly, publicly designated. This endpoint is a different kind of check entirely: a scoredrisk signal, built from GraphSense's real, MIT-licensed TagPacks attribution corpus (labels like “exchange”, “ransomware”, “terrorism_financing”, each with its own confidence level). The two checks run independently of each other. Use either, both, or neither.

Scope: what this catches, and what it doesn't

Sandbox mode

Same service as Nexiel Screen. Check liveness and mode directly:

curl -s https://compliance.nexiel.io/screen-docs/health
{"status":"ok","mode":"production"}

The examples below show sandbox-mode responses (a self-run instance with no SCREEN_API_MODE set) so you can see a full request/response round trip without an access token first. Against https://compliance.nexiel.io itself, add -H "Authorization: Bearer <access_token>", per the Screen API quickstart's Authentication section, and expect a real, ingested-address score rather than these seeded sandbox examples.

Score a sandbox address

Sandbox mode works immediately, with no setup step. A small set of synthetic fixture addresses scores correctly as soon as you call the endpoint, using the exact same scoring formula and thresholds a live score would use. Every fixture address and label is obviously fake and never resembles a real entity.

curl -s -X POST https://compliance.nexiel.io/v1/wallet-risk/score \
  -H "Content-Type: application/json" \
  -d '{"network": "BTC", "address": "1SandboxLowRiskExchangeFAKE00001"}'
200 response
{
  "network": "BTC",
  "address": "1SandboxLowRiskExchangeFAKE00001",
  "addressLookup": "1SandboxLowRiskExchangeFAKE00001",
  "score": 6,
  "classification": "LOW",
  "matchedTags": [
    {
      "network": "BTC",
      "address": "1SandboxLowRiskExchangeFAKE00001",
      "label": "Sandbox Synthetic Exchange (fake — for API testing only, not a real entity)",
      "category": "exchange",
      "abuse": null,
      "concepts": [],
      "actorId": null,
      "confidence": "service_data",
      "confidenceLevel": 50,
      "categoryWeight": 12,
      "contributionScore": 6,
      "isSanctionsConcept": false,
      "source": null,
      "packFile": "sandbox/synthetic-fixtures",
      "packTitle": "Nexiel Sandbox Synthetic Fixtures (fake data — never from GraphSense TagPacks)",
      "lastmod": null,
      "isClusterDefiner": false
    }
  ],
  "excludedTags": [],
  "sameActorAddresses": [],
  "frozenDecisionArtifact": {
    "engineVersion": "0.1.0",
    "riskWeightsHash": "be3b2339f08e725fa510d37c5fdc0b4364a1143601dcd209cf355b946146f298",
    "tagpackEtlRunId": null
  },
  "meta": {
    "mode": "sandbox",
    "sandboxOverride": false
  }
}

tagpackEtlRunId is always null in sandbox mode: synthetic fixture data has no ETL ingestion lineage to cite. A HIGH-severity example, same request shape, a different fixture address (1SandboxHighRiskTerrorismFAKE003): score 100, classification: "HIGH". An address absent from the fixture set entirely returns classification: "UNTAGGED", score: null: an absence of evidence, not a clean result.

Live mode: a real, ingested address

Live mode requires the same Keycloak-issued client-credentials bearer token every other Nexiel Screen production request does (see the Screen API quickstart's Authentication section). There is no separate key or scope for this endpoint. Example against a ransomware-tagged row at 60% confidence (95 × 0.6 = 57):

curl -s -X POST https://compliance.nexiel.io/v1/wallet-risk/score \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <access_token>" \
  -d '{"network": "BTC", "address": "<a real address>", "mode": "live"}'
200 response (fields trimmed for brevity)
{
  "score": 57,
  "classification": "ELEVATED",
  "frozenDecisionArtifact": {
    "engineVersion": "0.1.0",
    "riskWeightsHash": "be3b2339f08e725fa510d37c5fdc0b4364a1143601dcd209cf355b946146f298",
    "tagpackEtlRunId": "56a6a30d-6737-49be-9722-3273bc4719dd"
  },
  "meta": {
    "mode": "live",
    "sandboxOverride": false
  }
}

This is Nexiel's frozen decision artifact, applied to wallet risk scoring: engineVersion and riskWeightsHash (a SHA-256 hash of the entire scoring config in force) are stable and real; tagpackEtlRunIdcites the exact ingestion snapshot the score was computed against, so a regulator asking what logic and what data snapshot produced this score months later gets a re-derivable answer, not just proof the stored record wasn't tampered with.

Idempotency, rate limits, and authentication

This endpoint works exactly the same way as POST /v1/screen for every cross-cutting concern:

Reference

See the Wallet Risk tag on the Screen API reference for every parameter and response schema, including example LOW/HIGH/UNTAGGED sandbox results.

Roadmap