> Toden documentation. For the full index fetch /llms.txt. Every page is available as Markdown by appending .md to its URL. Identity verification uses /api/kyc, Risk Intelligence uses /api/v1; they are separate contracts with different error shapes. Never invent an endpoint, field or enum value: read the API reference page for the operation first.

# On-Demand Wallet Screening API

> Screen a wallet address with a secret API key and retrieve its risk result.

`POST /api/v1/wallet-screenings` screens one address. Use a secret `sk_test_` or `sk_live_` key on your backend. The key selects the environment. Use a new `Idempotency-Key` for each intended screening and keep that key for exact retries. A replay returns the same screening without another screening call or charge.

```bash
curl -X POST "https://sandbox-api.toden.co/api/v1/wallet-screenings" \
  -H "Authorization: Bearer $TODEN_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wallet-customer-42-20260930" \
  -d '{"network":"ethereum","address":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","asset":"USDT","sandboxScenario":"wallet.sanctions.direct"}'
```

`network` is the chain, such as `ethereum` or `TRX`; `asset` is separate, so USDT and USDC are never network values. Network identifiers may contain letters, numbers, periods, underscores, colons and hyphens, up to 40 characters. Toden translates known aliases and forwards other well-formed identifiers as supplied, without a fixed allowlist. The configured provider determines network and address acceptance. `sandboxScenario` works only with a test key. Optionally add `subject: {"externalUserId":"customer_42"}` to link an existing customer and enable linked alerts and cases.

With the Sandbox scenario above and the default direct-sanctions rule, these
selected response fields show what to inspect first. IDs and timestamps vary:

```json
{
  "screening": {
    "id": "wsc_test_example",
    "environment": "sandbox",
    "kind": "address",
    "status": "screened",
    "wallet": {
      "address": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "blockchain": "ETH",
      "asset": "USDT"
    },
    "risk": { "score": 92, "level": "critical" },
    "decision": "block",
    "exposures": [
      { "category": "SANCTIONS", "direction": "source", "type": "direct", "hops": 0, "share": 65 },
      { "category": "EXCHANGE", "direction": "source", "type": "direct", "hops": 0, "share": 35 }
    ],
    "fundFlow": {
      "kind": "exposure_map",
      "totalExposures": 2,
      "truncated": false,
      "nodes": [
        { "id": "exposure-0", "exposurePosition": 0, "category": "SANCTIONS", "direction": "source", "type": "direct", "hops": 0, "share": 65, "entityName": "OFAC SDN wallet", "isHighRisk": true },
        { "id": "exposure-1", "exposurePosition": 1, "category": "EXCHANGE", "direction": "source", "type": "direct", "hops": 0, "share": 35, "entityName": "Sandbox Exchange", "isHighRisk": false }
      ]
    },
    "rulesTriggered": [
      { "name": "Direct sanctions exposure", "version": 1, "action": "BLOCK" }
    ]
  }
}
```

The response also contains the full permitted profile, exposure details, rule
conditions, case links and timestamps. `block` is Toden's API value for a
decline decision. The `rulesTriggered` array records the matched rule version.

```ts
const response = await fetch(`${process.env.TODEN_API_BASE_URL}/wallet-screenings`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.TODEN_SECRET_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'wallet-customer-42-20260930',
  },
  body: JSON.stringify({ network: 'ethereum', address: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' }),
});
const { screening } = await response.json();
console.log(screening.id, screening.decision, screening.rulesTriggered);
```

Responses use the Toden contract: `screening.id`, `environment`, `wallet`, `risk`, `decision`, `exposures`, `rulesTriggered`, `provider`, `screenedAt` and `createdAt`. The provider section contains only identity and references, never raw screening data. `decision` is `allow`, `review` or `block`. A new result returns `201`, an idempotent replay `200` and an in-progress request `202`.

## Wallet verification & screening

The address overview uses the saved screening evidence. These fields are also
returned for each counterparty wallet in a [transaction assessment](/risk-intelligence/monitoring/events#transaction-assessment-response).

| Address overview        | Response field                                                                                                                                   |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| Screening target        | `kind` (`address` for a wallet address)                                                                                                          |
| Wallet address          | `wallet.address`                                                                                                                                 |
| Provider                | `provider.upstreamProvider` when returned, such as `merklescience` (Merkle Science); `provider.name` identifies the integration, such as `didit` |
| Score                   | `risk.score`, out of 100                                                                                                                         |
| Risk level              | `risk.level`, the Toden severity band                                                                                                            |
| Blockchain              | `wallet.blockchain`                                                                                                                              |
| Owner                   | `profile.owner`                                                                                                                                  |
| Owner type              | `profile.ownerType` and optional `profile.ownerSubtype`                                                                                          |
| User                    | `profile.user`                                                                                                                                   |
| User type               | `profile.userType` and optional `profile.userSubtype`                                                                                            |
| First transaction time  | `profile.firstTransactionAt`                                                                                                                     |
| Latest transaction time | `profile.latestTransactionAt`                                                                                                                    |
| Date added              | `profile.providerDateAdded`                                                                                                                      |

Owner and user labels are separate provider attributions, not proof of wallet
control. Missing fields are `null`; Toden does not copy owner into user or
substitute the screening date for a missing transaction date. Existing
`profile.entityLabel` and `profile.entityType` fields remain available.
`provider.riskLevel` preserves the provider's severity separately from Toden's
risk band. An empty profile does not mean the wallet is clean.

Use `GET /api/v1/wallet-screenings/{id}` to read one result and `GET /api/v1/wallet-screenings?limit=50&cursor=...` for scoped history. A different organisation or environment cannot read the screening.

`GET /api/v1/wallet-screenings/{id}/report` downloads a Toden PDF of the saved decision, exposures and triggered rule versions. It does not rerun screening and is not cryptographically signed. For a completed Didit address result, `GET /api/v1/wallet-screenings/{id}/signed-report` renders the vendor-signed PDF from the retained exact provider response. It returns `404` when no signed report exists and `502` when the vendor cannot render it. Neither route exposes the raw provider JSON. Both require the same scoped secret key as the result read.

`fundFlow` is a bounded exposure map: the first 40 normalised exposure rows, with `totalExposures` and `truncated` indicating whether more were retained in `exposures`. It does not claim a verified transfer path or expose the provider's raw graph.

| Result                                    | Next step                                                                                               |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `201` created                             | Save `screening.id` and act on `decision`.                                                              |
| `200` replay                              | Reuse the original result; no second screening or charge was created.                                   |
| `202` in progress                         | Read the returned screening or assessment status before acting.                                         |
| `422` invalid wallet or malformed network | Correct the input (`invalid_wallet` or `invalid_network`). No provider call or billable usage was made. |
| `409` idempotency conflict                | Use the original payload with that key, or choose a new key for a new screening.                        |

If the provider rejects a network after receiving the request, Toden records the failed attempt. This is separate from pre-call input validation; do not blindly retry it or treat it as a clean result.

Operational uncertainty becomes a reviewable result, never a clean wallet. To
receive updates, subscribe to [Wallet Intelligence webhooks](/integrate/webhooks/events/wallet-intelligence).