ChainZap
Contents


EnglishРусскийBahasa Indonesia
Dashboard

Search stays in your browser. Use ↑ ↓ to choose a result and Enter to open it.

Transaction Decision API

Integrate transaction checks for TRON, Ethereum and TON, understand coverage and handle retries.

Check a transaction before recording a payment

ChainZap checks a blockchain transaction before your application records a payment as received. One response combines transaction facts, risk signals and, if you ask for it, a comparison with the recipient, token and amount you expected.

What you can check

TRON, Ethereum mainnet and TON mainnet adapters are implemented. TRON checks are live for every new organization. TON is a preview whose checks of executed transactions currently return LIMITED_DATA; Ethereum checks are activated by support on request. Sign in with your ChainZap account to the developer dashboard, create an API key and start with the canonical transaction quickstart.

  • POST /v1/transaction/check: the canonical detailed contract 0.3 response. The network is required; expected payment details enable verification.
  • POST /v1/check: send a transaction hash. The network and the expected payment details are optional. The response is a short assessment with full evidence in details.
  • POST /v1/wallet/check: send a wallet address. The network is read from its format. The response is a state, a coverage level and reason codes with per-module facts in details; see wallet check.
  • GET /v1/usage: your organization's quota and recent usage.

Call the API from your server and send your API key in the X-API-Key header.

Interpret the whole response

A successful HTTP response does not mean a payment is confirmed or free of risk. For /v1/check, read state, risk_level and coverage together with the transaction state, the matching flags, the freshness data and the reasons in details. The canonical /v1/transaction/check returns those detailed fields at the top level. null means unknown or not requested, depending on the field. Never treat missing evidence as a clean result.

Continue with the quickstart, then read data completeness and idempotency.

Your first transaction check

Get access

Sign in with your ChainZap account to the developer dashboard. An organization is created for you automatically and you become its owner; an invitation code is needed only to join an existing organization. Create a key on the API keys page and store it in your server's secret store. TRON checks are live at once: a sandbox key lets you test for free, and a plan for live checks is bought on the Plan and billing page. Contact support to invite colleagues to your organization.

Send your first request

This example uses a fictional key and transaction hash. Replace them on your server. All amounts are decimal strings. Store your idempotency key together with the payment operation, so that it survives a process restart.

cURL
curl --include --max-time 15 'https://api.chainzap.io/v1/transaction/check' \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: cz_live_000000000000.fictional_example_only' \
  -H 'Idempotency-Key: merchant-order-1001-check-1' \
  -H 'X-Request-ID: merchant-order-1001-attempt-1' \
  --data '{"network":"tron","expected_recipient":"TJRabPrwbZy45sbavfcjinPJC18kjpRTv8","expected_token":"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t","expected_amount":"1250.000001","tx_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}'

Read X-Request-ID in the response headers and request_id in the JSON body. Keep this identifier for support. This canonical endpoint requires network. See status axes before consuming a result; POST /v1/check is the optional short response.

Verify expected payment details

JSON
{
  "network": "tron",
  "tx_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "expected_recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
  "expected_token": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
  "expected_amount": "1250.000001"
}

The example addresses are public sample addresses, not payment instructions. Match an exact token contract, not a symbol. Read all three match flags; they refer to the same selected transfer. See matching.

Consume contract 0.3

Use verification_final and integrity_status for payment verification, then evaluate risk_status and recommended_action separately. Pending is NOT_EVALUATED, never MATCH. Final MATCH with risk_status: INSUFFICIENT confirms the payment comparison but requires MANUAL_REVIEW, not automatic credit or payout approval. Read risk_retryable independently from retryable. The response examples show these branches.

Close a payout automatically

Without context about your side, a final MATCH with risk_status: NO_MATERIAL_SIGNAL and no reason to review gives CONFIRM_INTERNAL_STATE: the chain and the risk screen are in order, but ChainZap cannot see whether this payment is the one you owe or whether it was already settled. It is not a refusal: confirm in your own records that the payout is still owed and not already marked paid, then close it. The same action without a MATCH (no expected fields sent) has not compared the payment: do not close a payout on it.

To reach PROCEED, tell ChainZap what the payment is for: send direction: "OUTGOING", workflow_type: "PAYOUT" and the payout's external_reference together with the expected fields, or record the obligation first and send its external_reference with the check. A clean, final match against that recorded outgoing obligation is PROCEED; without an external_reference the best answer stays CONFIRM_INTERNAL_STATE, unless the check also sends customer_context.credit_status: "NOT_CREDITED", which alone is enough for PROCEED unless allocation_status is UNKNOWN (see statuses). direction and workflow_type are accepted without the obligation ledger, but on their own they never reach PROCEED; with direction: "OUTGOING", a payout that is not found also carries ATTEMPT_MAY_STILL_LAND: do not pay again. external_reference and the other obligation fields need the obligation ledger and answer 403 ledger_not_enabled where it is off.

Handle uncertainty

If the connection closes before you receive a response, retry with the same body and the same Idempotency-Key. Never create a new key to get around an uncertain result. Retry a received error only when error.retryable is true; honor Retry-After and cap the number of attempts. A stored error can be replayed unchanged: retryable does not force a new provider check.

See the Python and TypeScript examples for bounded retries and usage for quota inspection.

Check a wallet too

The same key screens a counterparty's address: POST /v1/wallet/check with {"address": "..."} returns a state, coverage and reason codes in the same shape, and is billed when a state is produced. See wallet check.

One transaction hash, one check

POST /v1/check needs only tx_hash. The API reports facts, risks and data gaps. Your application decides what to do with the payment. Sign in with your ChainZap account to the developer dashboard and create a server key on the API keys page.

A first result

Set CHAINZAP_API_KEY, TX_HASH and a persisted operation ID CHECK_ID on your server. These examples make one request only when you run them. For test data without provider calls, use a sandbox key. Never put a key in browser code. The request takes fewer than ten lines of application code; check the HTTP status before interpreting the result.

cURL
curl --include --max-time 15 https://api.chainzap.io/v1/check \
  -H "X-API-Key: $CHAINZAP_API_KEY" -H "Idempotency-Key: $CHECK_ID" \
  -H 'Content-Type: application/json' --data "{\"tx_hash\":\"$TX_HASH\"}"
Node.js
const response = await fetch("https://api.chainzap.io/v1/check", {
  method: "POST", signal: AbortSignal.timeout(15000),
  headers: {"Content-Type": "application/json", "X-API-Key": process.env.CHAINZAP_API_KEY,
    "Idempotency-Key": process.env.CHECK_ID},
  body: JSON.stringify({tx_hash: process.env.TX_HASH})
});
const result = await response.json();
if (!response.ok) throw new Error(result.error.code);
Python
import os, requests
response = requests.post("https://api.chainzap.io/v1/check", timeout=15,
    headers={"X-API-Key": os.environ["CHAINZAP_API_KEY"],
             "Idempotency-Key": os.environ["CHECK_ID"]},
    json={"tx_hash": os.environ["TX_HASH"]})
result = response.json()
response.raise_for_status()
PHP
<?php
$ch = curl_init('https://api.chainzap.io/v1/check');
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15,
  CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-API-Key: '.getenv('CHAINZAP_API_KEY'), 'Idempotency-Key: '.getenv('CHECK_ID')],
  CURLOPT_POSTFIELDS => json_encode(['tx_hash' => getenv('TX_HASH')])]);
$result = json_decode(curl_exec($ch), true);
if (curl_errno($ch) || curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) throw new RuntimeException($result['error']['code'] ?? 'transport_error');
curl_close($ch);

Network discovery

The accepted network names are tron, eth and ton; aliases are rejected. An explicit network restricts the search to that network. Hash shape, a 0x prefix, base64 and provider response order never determine the network, in the sandbox included.

Without network, ChainZap reads facts only in supported, activated networks enabled for your organization. details.checked_networks reports that scope; uniqueness says nothing about other chains. AML screening does not run across networks. All other checked networks must conclusively report no transaction. Provider failure, indexing gaps or timeouts do not count as absence. A single positive result plus an unknown result requires network.

Automatic detection uses the same provider-call budget as explicit selection, whether one network is in scope or several. Providers must still confirm that the transaction hash belongs to the network reported. A read whose price is not configured is recorded as unknown cost, never as zero.

NETWORK_AMBIGUOUS means several networks found the transaction. NETWORK_REQUIRED means the available context cannot prove a unique result: for example one network found it while another could not answer, or no networks are enabled. When every checked network confirms the transaction is absent, the answer is 404 transaction_not_found instead. Both use HTTP 422, state: insufficient_input, network: null, error.retryable: false, error.required_field: network, error.candidates and error.checked_networks. Specify the network and use a new idempotency key. Retrying the same body will replay the same error.

Short and detailed response

Field in the short response Meaning
request_id, network Correlation ID and resolved canonical network.
state clear, review, high_risk, limited_data: a check result, never a payment instruction.
risk_level low, moderate, high, severe, unknown. Sanctions raise this to severe; a fake token raises it to at least high.
coverage complete or partial, within the documented scope.
reason_codes Stable strings. Accept unknown future codes.
details Transaction/finality, selected index and all transfers with decimal-string raw_amount, matching, authenticity, AML/sanctions, source timestamps, limitations and full reason objects.

state is details.overall_state in lowercase. Detailed reason objects live in details.reasons; strings and objects are never mixed. Known high risks remain visible with partial coverage. A requested comparison with the value null is unfinished. All expected_* fields must match the same transfer; several full matches are ambiguous.

The canonical /v1/transaction/check keeps uppercase states, coverage: complete or partial and 2 to 5 reason objects. /v1/usage and X-API-Key are unchanged. /v1/check returns HTTP 404 transaction_not_found for a verified explicit-network absence; the canonical endpoint keeps HTTP 200 transaction_state: not_found. A pending check is HTTP 200 with review/limited_data, never clear, and is not charged. Both endpoints use the same limits, quota and costs. Idempotency keys are shared per organization: switching the endpoint or changing the body conflicts. Stored results, including the resolved network and the errors answered after the check was accepted, replay without another provider check; 429 and 5xx are never stored. Replay rechecks access.

Ethereum and TON coverage

Network Assets with configured KYT mapping Facts and limitations
TRON TRX; official USDT, USDC, USDD Existing dual-source transaction checks and the 19-confirmation solidification policy.
Ethereum mainnet ETH; official USDT, USDC, WETH ERC-20 Chain ID 1, receipt status, canonical block inclusion and finalized head, reconciled across two sources. The latest head or any block before the finalized head is insufficient. Ordered ERC-20 logs; no ERC-721 or Approval payments.
TON mainnet TON; official USDT jetton Mainnet ID -239, two indexed views, transaction hash, account/LT/shard, finalized masterchain inclusion and completed message links. Jetton master/wallet/owner verification and raw message bodies.

Ethereum internal native transfers need execution traces and are outside this version's scope. ERC-20 logs do not establish a recipient's net balance change for fee-on-transfer or rebasing tokens. Mint, burn and zero-value transfers require review. Unknown decimals leave amount: null; symbols never establish authenticity. Other tokens can have transfer facts but incomplete authenticity/KYT coverage.

TON accepts transaction hashes only, in 64-digit hex (optional 0x) or canonical 32-byte base64/base64url. A message hash or BOC is not silently reinterpreted as a transaction hash. Friendly addresses require a valid checksum; bounceable and non-bounceable normalize to workchain/account. Test-only addresses are rejected. expected_token means jetton master, expected_recipient means wallet owner. An isolated transfer_notification is not proof. Bounces, failed compute/actions, missing descendants and emulation cannot produce clear. Current wallet get-method results are cross-checked with the second index; historical identity changes require further investigation. Finality is provider-attested, not a locally verified validator signature proof.

Ethereum and TON checks are activated per organization by support after live acceptance. AML screening covers the assets the AML source supports on each network; a specific account's entitlement, actual costs and coverage still require live verification. Missing AML or sanctions evidence never defaults to low/clear. See source coverage.

Deterministic response example

This is an illustrative fixture, not a live observation. Expand the complete JSON below.

View full JSON response
JSON
{
  "coverage": "complete",
  "details": {
    "aml_risk_level": "low",
    "amount": "1250.000001",
    "amount_match": true,
    "checked_networks": [
      "tron"
    ],
    "confirmations": 27,
    "coverage": "complete",
    "coverage_limitations": {
      "product": [
        "AML_TRANSACTION_LEVEL_ONLY",
        "RISK_STATE_AS_OF_CHECK",
        "SANCTIONS_CONFIGURED_LISTS_ONLY"
      ],
      "runtime": []
    },
    "coverage_status": "OK",
    "decimals": 6,
    "decision_id": "0199a1b2-7c3d-7e4f-8a9b-0c1d2e3f4a5b",
    "direct_sanctions_match": false,
    "direction": "OUTGOING",
    "environment": "live",
    "evidence": {},
    "evidence_items": [
      {
        "coverage": "checked",
        "dependency": "required",
        "disagreement": null,
        "effective_at": "2026-09-03T11:59:00Z",
        "evidence_id": "ev_3b9e1c7a5d2f8e61",
        "evidence_version": "ev-1",
        "freshness": {
          "age_seconds": 2,
          "stale": false,
          "stale_after_seconds": null
        },
        "kind": "transaction_facts",
        "observed_at": "2026-09-03T11:59:58Z",
        "source": "chain_explorer",
        "source_reference": "block:60000000",
        "subject": {
          "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "type": "transaction"
        },
        "temporal_scope": "TRANSACTION_TIME"
      },
      {
        "coverage": "checked",
        "dependency": "required",
        "disagreement": null,
        "effective_at": "2026-09-03T11:59:00Z",
        "evidence_id": "ev_5f0c3a1d9b2e7f40",
        "evidence_version": "ev-1",
        "freshness": {
          "age_seconds": 2,
          "stale": false,
          "stale_after_seconds": null
        },
        "kind": "transaction_facts",
        "observed_at": "2026-09-03T11:59:58Z",
        "source": "chain_node",
        "source_reference": "block:60000000",
        "subject": {
          "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "type": "transaction"
        },
        "temporal_scope": "TRANSACTION_TIME"
      },
      {
        "coverage": "checked_freshness_unknown",
        "dependency": "required",
        "disagreement": null,
        "effective_at": null,
        "evidence_id": "ev_8a4d2e6f1c9b3a75",
        "evidence_version": "ev-1",
        "freshness": {
          "age_seconds": 0,
          "stale": true,
          "stale_after_seconds": null
        },
        "kind": "aml_risk",
        "observed_at": "2026-09-03T12:00:00Z",
        "source": "aml_provider",
        "source_reference": "transaction_risk_score",
        "subject": {
          "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "type": "transaction"
        },
        "temporal_scope": "CHECK_TIME"
      },
      {
        "coverage": "checked",
        "dependency": "required",
        "disagreement": null,
        "effective_at": null,
        "evidence_id": "ev_c27f5b0e9d4a1863",
        "evidence_version": "ev-1",
        "freshness": {
          "age_seconds": 0,
          "stale": false,
          "stale_after_seconds": 172800
        },
        "kind": "sanctions_list",
        "observed_at": "2026-09-03T12:00:00Z",
        "source": "ofac",
        "source_reference": "configured_address_index",
        "subject": {
          "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "type": "transaction_addresses"
        },
        "temporal_scope": "LIST_PUBLICATION"
      },
      {
        "coverage": "checked",
        "dependency": "required",
        "disagreement": null,
        "effective_at": "2026-08-28T00:00:00Z",
        "evidence_id": "ev_4e18a9d3b6c07f52",
        "evidence_version": "ev-1",
        "freshness": {
          "age_seconds": 0,
          "stale": false,
          "stale_after_seconds": 3024000
        },
        "kind": "sanctions_list",
        "observed_at": "2026-09-03T12:00:00Z",
        "source": "eu",
        "source_reference": "configured_address_index",
        "subject": {
          "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "type": "transaction_addresses"
        },
        "temporal_scope": "LIST_PUBLICATION"
      },
      {
        "coverage": "checked",
        "dependency": "required",
        "disagreement": null,
        "effective_at": "2026-08-28T00:00:00Z",
        "evidence_id": "ev_91d6f2a8c3e5b047",
        "evidence_version": "ev-1",
        "freshness": {
          "age_seconds": 0,
          "stale": false,
          "stale_after_seconds": 3024000
        },
        "kind": "sanctions_list",
        "observed_at": "2026-09-03T12:00:00Z",
        "source": "uk",
        "source_reference": "configured_address_index",
        "subject": {
          "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "type": "transaction_addresses"
        },
        "temporal_scope": "LIST_PUBLICATION"
      },
      {
        "coverage": "not_applicable",
        "dependency": "not_applicable",
        "disagreement": null,
        "effective_at": null,
        "evidence_id": "ev_6b03e7c1f5a92d48",
        "evidence_version": "ev-1",
        "freshness": {
          "age_seconds": null,
          "stale": false,
          "stale_after_seconds": null
        },
        "kind": "sanctions_list",
        "observed_at": null,
        "source": "un",
        "source_reference": "configured_address_index",
        "subject": {
          "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "type": "transaction_addresses"
        },
        "temporal_scope": "LIST_PUBLICATION"
      },
      {
        "coverage": "checked",
        "dependency": "required",
        "disagreement": null,
        "effective_at": null,
        "evidence_id": "ev_f0a5c8e2d7b41936",
        "evidence_version": "ev-1",
        "freshness": {
          "age_seconds": 0,
          "stale": false,
          "stale_after_seconds": null
        },
        "kind": "token_authenticity",
        "observed_at": "2026-09-03T12:00:00Z",
        "source": "token_registry",
        "source_reference": "official",
        "subject": {
          "ref": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
          "type": "token"
        },
        "temporal_scope": "CHECK_TIME"
      }
    ],
    "finality_policy": "tron_solidified_19_confirmations",
    "finalized": true,
    "freshness": {
      "checked_at": "2026-09-03T12:00:00Z",
      "max_source_age_seconds": 2,
      "risk_data_at": "2026-09-03T12:00:00Z",
      "token_data_at": "2026-09-03T12:00:00Z",
      "transaction_at": "2026-09-03T11:59:00Z"
    },
    "indirect_sanctions_exposure": false,
    "integrity": {
      "comparisons": {
        "amount": {
          "actual": "1250.000001",
          "delta": "0",
          "delta_raw": "0",
          "expected": "1250.000001",
          "result": "EXACT",
          "tolerance": null
        },
        "network": {
          "actual": "tron",
          "expected": null,
          "result": "NOT_EVALUATED"
        },
        "recipient": {
          "actual": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
          "expected": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
          "result": "MATCH"
        },
        "sender": {
          "actual": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
          "expected": null,
          "result": "NOT_EVALUATED"
        },
        "token": {
          "actual": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
          "expected": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
          "result": "MATCH"
        }
      },
      "relevant_event_count": 1,
      "selected_event_ref": "log:0",
      "status": "MATCH"
    },
    "integrity_status": "MATCH",
    "key_exposure": [],
    "limitations": [],
    "network": "tron",
    "obligation": null,
    "overall_state": "CLEAR",
    "policy_version": "b2b-tx-2026-10.4",
    "reasons": [
      {
        "category": "transaction",
        "code": "TX_FACTS_VERIFIED",
        "severity": "info"
      },
      {
        "category": "transaction",
        "code": "TX_FINALIZED",
        "severity": "info"
      },
      {
        "category": "risk",
        "code": "AML_LOW",
        "severity": "info"
      },
      {
        "category": "sanctions",
        "code": "NO_DIRECT_SANCTIONS_MATCH",
        "severity": "info"
      },
      {
        "category": "token",
        "code": "TOKEN_OFFICIAL",
        "severity": "info"
      }
    ],
    "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
    "recipient_match": true,
    "recommended_action": "CONFIRM_INTERNAL_STATE",
    "request_id": "req-3f34a12f",
    "required_confirmations": 19,
    "result_final": true,
    "retry_after_seconds": null,
    "retryable": false,
    "reuse": {
      "first_seen_at": null,
      "other_obligations": 0,
      "prior_decisions": 0,
      "status": "FIRST_SEEN"
    },
    "risk": {
      "driver": null,
      "driver_evidence_ids": [],
      "status": "NO_MATERIAL_SIGNAL"
    },
    "risk_exposure": false,
    "risk_final": true,
    "risk_retry_after_seconds": null,
    "risk_retryable": false,
    "risk_status": "NO_MATERIAL_SIGNAL",
    "sanctions_hit": false,
    "schema_version": "0.3",
    "selected_transfer_index": 0,
    "sender": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
    "source_coverage": {
      "risk_sources": {
        "aml_provider": {
          "checked_at": "2026-09-03T12:00:00Z",
          "data_updated_at": null,
          "error_code": null,
          "retrieved_at": "2026-09-03T12:00:00Z",
          "scope": "transaction_risk_score",
          "stale_after_seconds": null,
          "status": "checked_freshness_unknown"
        }
      },
      "sanctions_sources": {
        "eu": {
          "checked_at": "2026-09-03T12:00:00Z",
          "data_updated_at": "2026-08-28T00:00:00Z",
          "error_code": null,
          "retrieved_at": "2026-09-01T09:00:00Z",
          "scope": "configured_address_index",
          "stale_after_seconds": 3024000,
          "status": "checked"
        },
        "ofac": {
          "checked_at": "2026-09-03T12:00:00Z",
          "data_updated_at": null,
          "error_code": null,
          "retrieved_at": "2026-09-03T06:00:00Z",
          "scope": "configured_address_index",
          "stale_after_seconds": 172800,
          "status": "checked"
        },
        "uk": {
          "checked_at": "2026-09-03T12:00:00Z",
          "data_updated_at": "2026-08-28T00:00:00Z",
          "error_code": null,
          "retrieved_at": "2026-09-01T09:00:00Z",
          "scope": "configured_address_index",
          "stale_after_seconds": 3024000,
          "status": "checked"
        },
        "un": {
          "checked_at": null,
          "data_updated_at": null,
          "error_code": null,
          "retrieved_at": null,
          "scope": "configured_address_index",
          "stale_after_seconds": null,
          "status": "not_applicable"
        }
      },
      "transaction_sources": {
        "chain_explorer": {
          "checked_at": "2026-09-03T12:00:00Z",
          "data_updated_at": null,
          "error_code": null,
          "retrieved_at": "2026-09-03T11:59:58Z",
          "scope": "transaction_facts",
          "stale_after_seconds": null,
          "status": "checked"
        },
        "chain_node": {
          "checked_at": "2026-09-03T12:00:00Z",
          "data_updated_at": null,
          "error_code": null,
          "retrieved_at": "2026-09-03T11:59:58Z",
          "scope": "transaction_facts",
          "stale_after_seconds": null,
          "status": "checked"
        }
      },
      "universal_clearance": false
    },
    "status_drivers": {
      "action": "CUSTOMER_STATE_UNKNOWN",
      "coverage": null,
      "integrity": null,
      "risk": null,
      "transaction": "TX_FINALIZED"
    },
    "token_authenticity": "official",
    "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
    "token_match": true,
    "token_symbol": "USDT",
    "trace_complete": null,
    "transaction_category": "trc20_transfer",
    "transaction_state": "confirmed",
    "transaction_status": "CONFIRMED",
    "transaction_type": "TriggerSmartContract",
    "transfers": [
      {
        "amount": "1250.000001",
        "asset_type": "trc20",
        "decimals": 6,
        "event_ref": "log:0",
        "execution_status": "success",
        "index": 0,
        "raw_amount": "1250000001",
        "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
        "sender": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
        "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
        "token_id": null,
        "token_symbol": "USDT"
      }
    ],
    "transfers_truncated": false,
    "tx_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "verification_final": true,
    "versions": {
      "contract": "0.3",
      "decision": "b2b-tx-2026-10.4",
      "evidence_schema": "ev-1",
      "reason_codes": "rc-1",
      "rules": {
        "allocation": "alloc-v1",
        "billing": "final_answers_only",
        "exposure": "b2b-exp-v2",
        "integrity": "int-v3",
        "reuse": "reuse-v1",
        "sanctions_freshness": "sf-v1"
      }
    },
    "workflow_coverage": "complete",
    "workflow_type": "PAYOUT"
  },
  "network": "tron",
  "reason_codes": [
    "TX_FACTS_VERIFIED",
    "TX_FINALIZED",
    "AML_LOW",
    "NO_DIRECT_SANCTIONS_MATCH",
    "TOKEN_OFFICIAL"
  ],
  "request_id": "req-3f34a12f",
  "risk_level": "low",
  "state": "clear"
}

Authentication

One ChainZap account

Use Continue with ChainZap to sign in with the account you use in the app. From Settings → Developer API → Dashboard, an existing browser login continues without another sign-in. Your organization, API keys and API allowance stay separate from personal app credits. A previous Google portal login is linked using the verified Google account identifier, never by matching email addresses.

Ordinary sign-in needs no invitation code. Invitations are only for joining an existing organization. If Telegram opens a separate browser that has no ChainZap session, sign in there once with the same account. Link Google and Telegram in the app's account settings before switching between them.

Server API keys

Send X-API-Key over HTTPS on every request to the business API. Never place a key in a URL, browser storage, analytics, a support message or source control. A key grants access to one organization; a request cannot select another organization.

Signing in to the developer dashboard is separate from API keys. Live keys start with cz_live_; sandbox keys start with cz_test_.

Dashboard roles:

  • Owner, admin and developer members create, rotate and revoke API keys.
  • Owner, admin and billing members view payment history, buy plans and extra checks packages and manage the subscription.
  • Viewer members only inspect usage and key metadata.

An API key cannot buy anything.

Key lifecycle

Give each integration its own key with a descriptive label. The full key is shown only once, on creation or rotation; lists show a prefix. Rotation revokes the old key and creates its replacement in one operation. Update your server's secret store right after rotation. To avoid an outage during a planned migration, create a separate key, move your integration to it and verify it, then revoke the previous key.

Access errors

  • 401 invalid_api_key: the key is invalid or revoked.
  • 403 tenant_suspended: the organization is suspended.
  • 403 network_not_enabled: the requested network is not enabled for the organization.

Correct access first. Retrying the same request without a change does not help.

POST /v1/transaction/check

Request

This strict JSON request requires network and a 32-byte tx_hash. Unknown fields are rejected. Expected addresses are validated and normalized for the selected network. expected_amount is a nonnegative decimal string; JSON numbers and exponent notation are rejected. For optional network context and the short response, use POST /v1/check.

Field What to send
network "tron", "eth" or "ton". Required.
tx_hash 64 hexadecimal digits with an optional 0x. TON also accepts canonical base64/base64url encoding of 32 bytes.
expected_recipient Optional: the recipient address on the selected network. For TON, the wallet owner.
expected_token Optional: the token contract on the selected network. For TON, the jetton master.
expected_amount Optional: the amount as a string, for example "10.25".

Address formats and exact comparison rules are in matching. Request bodies are limited to 4 KiB; a larger body is refused with 413 payload_too_large. Organizations on the pilot contract must send all three expected_* fields.

Send Content-Type: application/json, your server-side X-API-Key and an Idempotency-Key for the logical check operation. X-Request-ID links the request to your own operation log. The generated API reference lists the exact required fields, enums and response schemas.

Response

Read transaction_state independently from overall_state. The response includes confirmations, normalized transfers, the selected transfer, risk level, sanctions status, token authenticity, matching flags, coverage, freshness and 2 to 5 prioritized reasons. Transfer and exposure amounts are decimal strings. Handle nullable fields explicitly.

transfers_truncated=true means the transfer list was cut short. AMBIGUOUS_TRANSFER means several transfers satisfy your conditions. Both need attention. Do not treat a truncated list as complete and do not combine matching flags from different transfers.

Consume contract 0.3

Use verification_final and integrity_status for payment verification, then evaluate risk_status and recommended_action separately. Pending is NOT_EVALUATED, never MATCH. Final MATCH with risk_status: INSUFFICIENT confirms the payment comparison but requires MANUAL_REVIEW, not automatic credit or payout approval. Read risk_retryable independently from retryable. The response examples show these branches.

Billing

A final answer with coverage: complete uses one check from your quota, including a complete high-risk result: the transaction is confirmed past its finality policy, or it failed for good. A pending transaction, a transaction not found and a response with coverage: partial are not charged. Replaying the same idempotent operation never charges twice.

expected_token is a contract address (not a ticker); the legacy name expected_token_contract remains accepted. Send only one. The response carries schema_version: "0.3" and policy_version, the decision policy that produced the answer (store it with the answer), the three separate risk/sanctions flags, workflow_coverage and source_coverage. Read coverage before interpreting a clear result. Idempotency keys used before contract 0.2 return 409 idempotency_conflict; use a new key after handling the old result.

Check a wallet before you accept it or pay it

POST /v1/wallet/check screens one address the same way POST /v1/check screens one transaction: same key, same quota, same idempotency rules, same shape of answer. Send an address, receive a state, a coverage level and reason codes, and decide in your own workflow whether the counterparty needs a closer look.

Request

Field What to send
address Required. An Ethereum address (0x and 40 hex digits), a TRON address (T and 33 base58 characters) or a TON address (48-character friendly form or workchain:hash).
network Optional: "tron", "eth" or "ton". When omitted, the network is read from the address format. When sent, it must agree with the format; a mismatch is 422 invalid_tron_address, invalid_eth_address or invalid_ton_address.

The three formats cannot be confused with each other, so a wallet check never answers NETWORK_REQUIRED or NETWORK_AMBIGUOUS. A string that matches none of them is 422 invalid_address. Unknown fields are rejected and the body is limited to 4 KiB. Send Content-Type: application/json, your server-side X-API-Key and an Idempotency-Key for the logical operation; X-Request-ID links the request to your own log.

cURL
curl --include --max-time 35 'https://api.chainzap.io/v1/wallet/check' \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: cz_live_000000000000.fictional_example_only' \
  -H 'Idempotency-Key: onboarding-4711-wallet-1' \
  -H 'X-Request-ID: onboarding-4711-attempt-1' \
  --data '{"address":"TJRabPrwbZy45sbavfcjinPJC18kjpRTv8"}'

The address in the example is a public sample address, not a payment instruction. The response echoes address in its canonical spelling: Ethereum in lower case, TON as workchain:hash, TRON unchanged.

Response

Read the top level first, then details.

Field Meaning
state clear, review, high_risk or limited_data. A screening result, never an instruction to pay or to refuse.
coverage complete when every applicable module was fully screened, otherwise partial. A clear state is only ever returned with complete coverage.
reason_codes Stable strings, ordered by severity, at most ten. The same codes appear as objects with category and severity in details.reasons.
environment live or sandbox.

details explains where the state came from:

  • overall_state and data_coverage: the upper-case state and the engine's own coverage word, FULL, PARTIAL or INSUFFICIENT.
  • modules: seven rows in a fixed order, sanctions_aml, wallet_behaviour, token_authenticity, contract_controls, address_security, counterparties_services, flows_connections. Each row has a status (RISK_DETECTED, NO_MATCHES_FOUND, PARTIALLY_CHECKED, INSUFFICIENT_DATA, NOT_SUPPORTED, NOT_CHECKED) and a source_coverage (complete, partial, none, not_applicable). A module that was not screened says so; it is never reported as clean.
  • sanctions: direct_match against the configured OFAC, EU, UK and UN indexes (null when no index was screened), the listing authorities, whether the listing is attributed, and indirect_exposure reported by the AML provider (null when it did not run).
  • address_security: public denylist status and category, provider_flagged (whether the address-security provider flagged the address; null when it did not answer or its rights do not let ChainZap show it), and whether the stablecoin issuer has frozen the address. The provider's own labels are never returned; the deprecated provider_flags is ["PROVIDER_SECURITY_FLAG"] when provider_flagged is true and empty otherwise.
  • behaviour: whether the address is a contract, the transaction count and whether it was capped, the wallet age, first and last activity, and the number of counterparties.
  • counterparties and token_authenticity: how many counterparties were screened, unverified or flagged, and how many held tokens are official, fake, suspicious or unknown.
  • freshness and source_coverage: when the check ran, whether the analysis came from the analysis cache (5 minutes on Ethereum, 30 minutes on TRON and TON; the AML provider's answer is reused for up to 24 hours), the oldest source age, and one observation per source with the same status vocabulary as the transaction check. Sources are named by role: ofac, eu_sanctions, uk_sanctions, un_sanctions, denylist, issuer_restrictions, aml_provider, address_security_provider, onchain, chain_explorer, chain_node, graph_risk.
  • limitations: analysis_from_cache, transaction_sample_capped, aml_provider_unavailable, aml_provider_skipped_dominant_local_hit, counterparty_attribution_partial, network_assumed_eth, address_security_provider_withheld, sandbox_partial_fixture.

Every wallet reason code is listed with its meaning in reason codes.

What a wallet check is not

There is no numeric score anywhere in the response, by design. A number invites a threshold, and no threshold is honest across three networks with different sources. The response tells you what was found, what was screened and what could not be screened. It is not a compliance certificate and it does not know the internal rules of any exchange or bank. Treat clear as "nothing critical in the screened sources", review as "look before you proceed", high_risk as "a critical finding needs resolving", and limited_data as "we could not tell".

Billing

A wallet check uses one check from your quota when it produces a state: clear, review or high_risk, with complete or partial coverage. limited_data is free, as are errors, timeouts and idempotent replays. This differs from the transaction check, which is charged only with complete coverage: a wallet screen often reaches a firm verdict while one counterparty in fifty stays unattributed, and the paid sources have already answered. Sandbox keys never use paid checks.

Time budget and retries

A cold wallet check reads several providers and has a server budget of 30 seconds; use a client deadline of about 35 seconds. If the server answers 504 request_timeout, retry with the same body and the same Idempotency-Key: the analysis keeps running to completion on the server and lands in the shared cache, so the retry is usually answered at once and carries freshness.served_from_analysis_cache: true. The general retry rules apply unchanged.

Idempotency across kinds

Idempotency keys are shared per organization and environment across all check endpoints. Reusing a transaction's key for a wallet body, or a wallet's key for another address, answers 409 idempotency_conflict. One logical operation, one key.

Server examples

Python
import os, requests
response = requests.post("https://api.chainzap.io/v1/wallet/check", timeout=35,
    headers={"X-API-Key": os.environ["CHAINZAP_API_KEY"],
             "Idempotency-Key": os.environ["CHECK_ID"]},
    json={"address": os.environ["WALLET_ADDRESS"]})
result = response.json()
response.raise_for_status()
if result["state"] != "clear":
    print(result["state"], result["reason_codes"])
TypeScript
const response = await fetch("https://api.chainzap.io/v1/wallet/check", {
  method: "POST", signal: AbortSignal.timeout(35000),
  headers: {"Content-Type": "application/json", "X-API-Key": process.env.CHAINZAP_API_KEY!,
    "Idempotency-Key": process.env.CHECK_ID!},
  body: JSON.stringify({address: process.env.WALLET_ADDRESS})
});
const result = await response.json();
if (!response.ok) throw new Error(result.error.code);
if (result.state !== "clear") console.info(result.state, result.reason_codes);

Deterministic response example

This is an illustrative fixture, not a live observation. Expand the complete JSON below.

View full JSON response
JSON
{
  "address": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
  "coverage": "complete",
  "details": {
    "address_security": {
      "denylist_category": null,
      "denylisted": false,
      "issuer_blocked": false,
      "issuer_blocked_symbols": [],
      "provider_flagged": false,
      "provider_flags": []
    },
    "behaviour": {
      "contract_verified": null,
      "counterparties_count": 37,
      "first_activity_at": "2024-09-20T10:15:00Z",
      "is_contract": false,
      "last_activity_at": "2026-09-19T08:02:11Z",
      "transaction_count": 412,
      "transaction_count_limited": false,
      "wallet_age_days": 731
    },
    "counterparties": {
      "flagged": 0,
      "screened": 37,
      "unverified": 0
    },
    "coverage_scope": "supported_deep_sources",
    "data_coverage": "FULL",
    "evidence": {
      "live": true,
      "source": "live"
    },
    "freshness": {
      "analyzed_at": "2026-09-21T08:59:58Z",
      "checked_at": "2026-09-21T09:00:00Z",
      "oldest_source_age_seconds": 2,
      "served_from_analysis_cache": false
    },
    "limitations": [],
    "modules": [
      {
        "key": "sanctions_aml",
        "source_coverage": "complete",
        "status": "NO_MATCHES_FOUND"
      },
      {
        "key": "wallet_behaviour",
        "source_coverage": "complete",
        "status": "NO_MATCHES_FOUND"
      },
      {
        "key": "token_authenticity",
        "source_coverage": "complete",
        "status": "NO_MATCHES_FOUND"
      },
      {
        "key": "contract_controls",
        "source_coverage": "complete",
        "status": "NO_MATCHES_FOUND"
      },
      {
        "key": "address_security",
        "source_coverage": "complete",
        "status": "NO_MATCHES_FOUND"
      },
      {
        "key": "counterparties_services",
        "source_coverage": "complete",
        "status": "NO_MATCHES_FOUND"
      },
      {
        "key": "flows_connections",
        "source_coverage": "complete",
        "status": "NO_MATCHES_FOUND"
      }
    ],
    "overall_state": "CLEAR",
    "reasons": [
      {
        "category": "sanctions",
        "code": "NO_DIRECT_SANCTIONS_MATCH",
        "severity": "info"
      },
      {
        "category": "risk",
        "code": "AML_LOW",
        "severity": "info"
      },
      {
        "category": "address",
        "code": "NO_ADDRESS_SECURITY_FLAGS",
        "severity": "info"
      },
      {
        "category": "behaviour",
        "code": "NO_BEHAVIOUR_FLAGS",
        "severity": "info"
      }
    ],
    "sanctions": {
      "attributed": false,
      "authorities": [],
      "direct_match": false,
      "exposure_source": "aml_provider",
      "indirect_exposure": false
    },
    "schema_version": "0.1",
    "source_coverage": {
      "ofac": {
        "checked_at": "2026-09-21T09:00:00Z",
        "data_updated_at": "2026-09-20T18:00:00Z",
        "error_code": null,
        "retrieved_at": "2026-09-21T09:00:00Z",
        "scope": "sanctions_aml",
        "stale_after_seconds": 172800,
        "status": "checked"
      }
    },
    "token_authenticity": {
      "fake": 0,
      "official": 3,
      "suspicious": 0,
      "total": 3,
      "unknown": 0
    },
    "universal_clearance": false
  },
  "environment": "live",
  "network": "tron",
  "reason_codes": [
    "NO_DIRECT_SANCTIONS_MATCH",
    "AML_LOW",
    "NO_ADDRESS_SECURITY_FLAGS",
    "NO_BEHAVIOUR_FLAGS"
  ],
  "request_id": "8a7f1c2e9d3b4f5a8c6d7e9f0a1b2c3d",
  "state": "clear"
}

Match a transfer to a payment

Use the expected_* fields to verify the recipient, token contract and amount of a specific payment. Send all three fields or only the comparisons you need.

The following comparisons apply after chain finality. While transaction_status: PENDING, integrity and every requested comparison are NOT_EVALUATED, and all match flags are null, even when an observed transfer has the expected values. A final MATCH verifies payment details independently of KYT; insufficient risk still requires MANUAL_REVIEW.

One transfer, three comparisons

A transaction can contain several transfers. ChainZap first looks for a transfer that satisfies every supplied condition. If none does, it selects the transfer with the most matches. Ties are resolved consistently using the transfer index.

recipient_match, token_match and amount_match all describe that same transfer. If several transfers satisfy every condition, the response includes AMBIGUOUS_TRANSFER. A transfer that did not execute, such as one in a reverted transaction, is never a match: its three fields are null, and a failed transaction in which nothing executed is NOT_EVALUATED, never LIMITED_DATA for missing transfer rows.

Contract 0.3 states the comparison in full in integrity: status (see status axes), selected_event_ref, relevant_event_count (executed transfers to the expected recipient in the expected token) and comparisons with the expected and actual value and a result for recipient, sender, token, amount and network. The amount result is EXACT, WITHIN_TOLERANCE, UNDERPAID, OVERPAID, NOT_EVALUATED or INSUFFICIENT, with the signed delta and delta_raw.

Relevant transfers Answer
One, amount matches MATCH.
One, amount differs MISMATCH with UNDERPAID or OVERPAID.
Several, exactly one amount matches MATCH and ADDITIONAL_RELEVANT_TRANSFERS: the transaction may pay twice.
Several of one asset, none matches but together they make the amount MATCH and SPLIT_WITHIN_TX. Movements of different assets never add up.
Several that fit equally AMBIGUOUS; nothing is allocated.
Result Meaning
true The value matches the one you supplied.
false The value could be checked but does not match.
null You did not request this comparison, or there was not enough data to make it.

How values are compared

If you provide an expected value but its transfer data is unavailable, the corresponding match field is null, coverage is partial, and the reasons include EXPECTED_DATA_UNAVAILABLE. Missing token decimals, for example, do not prove an amount mismatch. Do not treat null as a successful match.

Addresses are validated and normalized for the selected network:

Network Accepted addresses and comparison
tron Base58Check with a valid checksum. Compare the TRON address exactly.
eth 0x followed by 40 hexadecimal digits. Uniform lowercase or uppercase is accepted; mixed case must pass EIP-55. Comparisons use lowercase addresses.
ton Raw workchain:account or a friendly address with a valid checksum. Only workchains 0 and -1 are accepted; test-only addresses are rejected. Bounceable and non-bounceable forms normalize to the same account.

For TON, expected_recipient is the wallet owner and expected_token is the jetton master, not the jetton wallet.

Tokens are compared by exact contract, never by name or symbol; the same symbol does not prove the same token. Amounts are converted from decimal strings to raw integer units using the known token decimals and compared exactly; no floating-point tolerance is applied. To accept a small difference, send amount_tolerance: an absolute amount in token units, symmetric, 0 by default. A difference within it is WITHIN_TOLERANCE and adds AMOUNT_WITHIN_TOLERANCE; a tolerance with more decimal places than the token answers 422 invalid_amount_tolerance. expected_sender compares the sending address the same way as the recipient. Digits beyond the token's decimals are accepted when they are zeros: "10.0000000" equals "10" for a 6-decimal token. A nonzero digit beyond them returns 422 invalid_expected_amount.

Mismatch example

If you expect "10.000001" but the selected transfer contains "10", the response includes amount_match=false and EXPECTED_AMOUNT_MISMATCH.

A different recipient produces EXPECTED_RECIPIENT_MISMATCH; a different token contract produces EXPECTED_TOKEN_MISMATCH. A matching amount does not prove that the recipient and token also match. Before accepting a payment, check every required comparison, the transaction state and the overall assessment.

Pilot contract

An organization on the design-partner pilot contract must send all three fields on every check: a check without expected_recipient, expected_token or expected_amount is refused with 422 expected_fields_required, and an expected_token other than official USDT on TRON with 422 token_not_enabled. Refusals happen before the check is accepted: nothing is charged or stored, and sandbox keys follow the same rules.

Network context

These comparisons work with both check endpoints. When you already know the payment network, supply it explicitly. The API then validates the expected addresses against that network and never switches to another chain. See network discovery for requests without network.

Obligations, attempts and allocations

A single check answers whether one transaction pays what you expect. The obligation ledger answers whether a business obligation, a payout or a deposit you are owed, was paid: once, in full, by which transactions, and whether the same payment is presented for something else. It keeps your instructions, your execution attempts, the payment events ChainZap verified and how they were allocated, and never rewrites any of them.

The ledger is switched on per deployment. While it is off, a check with any ledger field and every ledger endpoint answer 403 ledger_not_enabled.

The model

Record What it is
Obligation One business reference (external_reference, unique per organization and environment) with a workflow_type (PAYOUT, DEPOSIT, INVOICE, REFUND, SETTLEMENT, OTHER) and a direction (OUTGOING, INCOMING).
Instruction version What was owed: network, recipient, optional sender, token contract, amount and amount_tolerance, with instruction_effective_at and approval_reference. Append-only.
Execution attempt One try at paying it (attempt_reference), with channel, provider_reference, status and optional amount and tx_hash. A retry or a switch of payment provider is a new attempt.
Payment event One verified on-chain movement, identified by network, transaction and event_ref (for example log:3). One transaction can hold several events.
Allocation How much of one event settles one obligation, with its status (MATCHED, CUSTOMER_DECLARED, UNKNOWN, DISPUTED) and source (CHAINZAP_MATCH, CUSTOMER, BATCH; OPERATOR is reserved). Reversed, never deleted.
Decision ChainZap's answer to one check, kept as it was given. See decisions.

external_reference, attempt_reference and approval_reference are up to 128 printable ASCII characters. Do not put personal data in them.

Check against an obligation

Add the obligation's reference to an ordinary transaction check:

JSON
{
  "tx_hash": "…",
  "network": "tron",
  "external_reference": "payout-2026-10-0001",
  "attempt_reference": "attempt-1",
  "direction": "OUTGOING",
  "workflow_type": "PAYOUT",
  "expected_recipient": "T…",
  "expected_token": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
  "expected_amount": "100.00"
}

The first check of a new reference records the obligation and its instruction (version 1, or the instruction_version you send). Later checks may send only the reference: the recorded instruction is what the transaction is compared with. The answer adds an obligation block (status, expected_amount, allocated_amount, residual_amount, attempts, events, pending_events) and a reuse block.

When the transaction matches, or pays the right recipient and token a different amount, its event is allocated to the obligation. Only final, executed events are allocated; a pending one is counted in pending_events and never closes the obligation. When several movements fit equally (integrity_status: AMBIGUOUS), each is linked to the obligation as UNKNOWN with nothing allocated, until you declare which one paid. A check that omits direction takes the obligation's recorded one, or else infers it from workflow_type (PAYOUT and REFUND are outgoing, DEPOSIT and INVOICE incoming); a payment whose direction stays unknown is never PROCEED.

Obligation status Meaning
OPEN Nothing allocated yet.
PARTIALLY_ALLOCATED Less than the amount, minus the tolerance, is allocated. residual_amount is what remains.
ALLOCATED The amount is covered within the tolerance.
OVER_ALLOCATED More than the amount plus the tolerance is allocated. When two or more transactions did that, the answer adds DUPLICATE_EXECUTION.
CANCELLED Reserved for a cancelled obligation; the API does not set it yet.

Instructions never change in place

An instruction version is immutable. Sending the same version with different terms is refused with 409 instruction_conflict; sending different terms without a version is refused with 409 instruction_version_required. A changed instruction is a new version, recorded with POST /v1/obligations and a higher instruction_version; earlier versions stay on record and a check can name the version it was paid under. A version you never recorded is not reconstructed: the check answers integrity_status: INSUFFICIENT with INSTRUCTION_UNAVAILABLE. A payment in a block before instruction_effective_at adds INSTRUCTION_NOT_EFFECTIVE.

Record obligations and attempts up front

Endpoint Purpose
POST /v1/obligations Record an obligation, or a new version of its instruction.
POST /v1/obligations/lookup The obligation with its instructions, attempts, allocations and decisions. Send {"external_reference": "…"} in the body: a reference never travels in a URL, where proxies log it.
POST /v1/obligations/attempts Record an attempt or a new status of one (CREATED, SUBMITTED, BROADCAST, TIMEOUT, FAILED, CANCELLED, PROVIDER_CONFIRMED, UNKNOWN). Every status change is kept in status_history. The same attempt_reference with another channel, provider_reference, amount or transaction is refused with 409 attempt_conflict, and so is a check that names it with another tx_hash: a retry or a provider switch is a new attempt.

An attempt is not an execution. execution_confirmed turns true only when a payment event of its transaction is allocated to the obligation; a TIMEOUT attempt followed by a successful retry leaves two attempts and one execution. When attempts carry an amount, a check made with that attempt_reference compares the transaction with the attempt's amount, so a payout split into legs matches leg by leg.

Shared payments and reuse

One payment can settle several obligations, and one obligation can be paid by several payments.

  • Several obligations, one payment. ChainZap never splits a payment on its own. Declare the shares with POST /v1/allocations (network, tx_hash, event_ref, allocations: [{external_reference, amount}], and an optional owner that stamps any earlier allocation the declaration replaces). The event exists once a check naming an obligation recorded it with complete coverage (404 payment_event_not_found otherwise); it must have executed and be final (409 payment_event_not_settled), be in the obligation's token (409 allocation_token_mismatch), and the shares cannot exceed it (409 allocation_exceeds_event). A check of one share then compares that share, not the whole payment, with what the obligation is owed.
  • One obligation, several payments. Each check under the same reference adds its event; residual_amount falls until the obligation is ALLOCATED.
  • Reversal. POST /v1/allocations/{allocation_id}/reversal with a reason (CUSTOMER_CORRECTION, DISPUTED, DUPLICATE_RECORD, OTHER) stamps the allocation as reversed, and DISPUTED marks it DISPUTED. It stays on record, and a later check never restores it; only a declaration can.

reuse.status says what the ledger already knows about the payment event:

Status Meaning
FIRST_SEEN Nothing about this transaction is on record.
REPEAT_CHECK It was checked before, but nothing claims it for an obligation. Asking again is normal and never a finding.
SAME_OBLIGATION It was already matched to this obligation. A recheck does not allocate it twice.
DECLARED_SHARED You declared that it settles several obligations, this one included.
OTHER_OBLIGATION It already settles a different obligation: TX_REUSED_ACROSS_OBLIGATIONS, REVIEW, and nothing is allocated to this one.

An idempotent replay (the same Idempotency-Key and body) returns the stored answer and is not a reuse.

Incoming payments

For a deposit, send direction: "INCOMING" and tell ChainZap what your system already did with it in customer_context: credit_status (NOT_CREDITED, CREDITED, PARTIALLY_CREDITED, UNKNOWN) and allocation_status (NOT_ALLOCATED, PARTIALLY_ALLOCATED, ALLOCATED, UNKNOWN), how far your books already applied it. ChainZap cannot see your books: without NOT_CREDITED a clean deposit is CONFIRM_INTERNAL_STATE, an UNKNOWN allocation is too, and a payment credited or applied even in part gives REVIEW with ALREADY_CREDITED. The same obligation, allocation and reuse rules apply to deposits and payouts.

Retention

Raw references, addresses and transaction hashes are kept for 90 days and then replaced by keyed digests; owners are removed. Amounts, tokens, statuses and versions remain, so the totals stay right. After that the ledger still recognises a reused payment and a repeated reference by digest, and the API returns redacted: true where a raw value is gone. A digest of an amount and a block time is pseudonymous, not anonymous.

Batches

A batch checks a file of payments, 50 to 200 rows, on the same engine as a single check: every row gets the same answer, reasons and ledger effects it would get alone. The file is validated and priced first; nothing runs and nothing is charged until you confirm. Batches use the obligation ledger, and answer 403 batch_not_enabled while it is off.

You can upload, run and export a batch in the developer dashboard on the Batches page, or through the API below. Both use the same service and limits.

The file

A CSV in UTF-8 (a byte order mark is fine), comma- or semicolon-separated, with a header row. Up to 200 rows and 256 KiB. One row is one payment of one obligation.

Column Required Meaning
external_reference yes The obligation the row is about.
network yes, or as a default tron, eth or ton (trx, trc20, ethereum, erc20 are read too).
tx_hash no The transaction to check. Without it the row only records the obligation, which then appears among the open obligations.
expected_recipient, expected_token, expected_amount the first time a reference is seen What is owed. expected_token is a contract or a registry symbol such as USDT.
amount_tolerance, expected_sender no As in a single check.
direction, workflow_type no OUTGOING or INCOMING; PAYOUT, DEPOSIT, INVOICE, REFUND, SETTLEMENT, OTHER.
instruction_version, instruction_effective_at, approval_reference no The instruction the row was paid under.
attempt_reference, attempt_status, channel, provider_reference, attempt_amount no The execution attempt behind the row.
allocated_amount no Your share of a payment that settles several obligations.

Common header names are recognised: order_id, payout_id, invoice_id, reference for the reference; txid, hash, transaction_hash for the hash; to, recipient, address, wallet for the recipient; token, contract, asset, currency for the token; amount, tolerance, from, sender, chain, attempt, provider, status. Unknown columns are ignored and listed in preflight.ignored_columns. Query parameters network, direction and workflow_type set defaults for rows that leave them empty. Download a template from the dashboard.

Upload and preflight

cURL
curl --max-time 30 'https://api.chainzap.io/v1/batches?network=tron' \
  -H 'X-API-Key: YOUR_SERVER_API_KEY' -H 'Content-Type: text/csv' \
  -H 'Idempotency-Key: payouts-2026-10-05' --data-binary @payouts.csv

201 returns the batch in PREFLIGHTED with:

  • preflight: rows, checks, obligations_only, invalid and duplicates, with errors and warnings counted by code. A row with an error is skipped (SKIPPED_INVALID) and the error names the field, for example invalid_tron_address or unknown_token_symbol; an identical repeated row is skipped as a duplicate. When rows give one reference different terms, the first stands and the later ones are refused (conflicting_rows_for_obligation). Warnings do not skip a row: tx_shared_across_references (one transaction under several references without allocated_amount), declared_shared_payment, several_transactions_for_obligation, and tx_seen_for_other_obligation when the ledger already ties the transaction to another obligation.
  • estimate: an upper bound, never a promise. distinct_transactions, billable_checks_at_most, provider_calls_at_most, provider_usd_at_most (priced as if nothing were cached), quota_available and quota_sufficient.

A file that is not UTF-8, is empty, has no header or data rows, or has a malformed row answers 422 invalid_csv; a header without an external_reference column or with a repeated column answers 422 invalid_csv_header; more than 200 rows answer 413 batch_too_large. An Idempotency-Key names one file: the same key with the same file returns the same batch, with another file 409 idempotency_conflict. An organization runs at most two batches at a time; starting a third answers 429 batch_limit_reached (uploading and preflighting cost nothing and are not limited this way).

Run

POST /v1/batches/{batch_id}/execute with {"confirm": true} queues the batch; without confirm it answers 422 confirmation_required. If the quota cannot cover every distinct transaction it answers 409 insufficient_quota; send "allow_partial": true to start anyway.

Rows are checked by a worker, never faster than half of your organization's rate limit, so your own traffic keeps working; batches of all organizations take turns, a slice at a time. Rows of one transaction run in file order. Each distinct transaction is charged once per batch: once a row of it produced a charged answer, later rows of the same transaction are checked against their own obligation for free (a row that was refused charges nothing, so the next one is charged). If the quota or the daily ceiling of unbilled checks runs out, the batch pauses (PAUSED, with pause_reason quota_exceeded or daily_unbilled_ceiling); execute it again to resume.

Batch status Meaning
PREFLIGHTED Validated and priced, waiting for your confirmation.
QUEUED, RUNNING Being checked.
PAUSED Stopped by quota or the unbilled ceiling; resume with execute.
CANCELLING, CANCELLED Stopped by you; rows already checked keep their answers.
COMPLETED Every row has an answer or an error.
FAILED The organization was suspended while the batch ran.
Row state Meaning
PENDING, CLAIMED Waiting, or being checked.
DONE Payment verification is final (verification_final: true), or the row only recorded an obligation. The risk part can still have a temporary gap: such a row keeps risk_retryable: true, usually with risk_status: INSUFFICIENT, and retry does not run it again.
RETRYABLE Verification is not final yet (pending, not found, a temporary chain-source gap) or the check failed with a 5xx.
FAILED The check was refused with a 4xx; fix the row and upload it again.
SKIPPED_INVALID, SKIPPED_DUPLICATE, CANCELLED Not checked.

POST /v1/batches/{batch_id}/retry runs again only the RETRYABLE rows of a completed or paused batch, up to five attempts per row; 409 nothing_to_retry when there are none. POST /v1/batches/{batch_id}/cancel stops it.

A row whose only gap is risk (a KYT outage) is DONE, not RETRYABLE: read its risk_retryable and risk_retry_after_seconds, and check that transaction again with a new Idempotency-Key when you need the risk part (see retries).

Results

  • GET /v1/batches lists your recent batches; GET /v1/batches/{batch_id} returns one with progress and, once it ran, summary.
  • GET /v1/batches/{batch_id}/rows?after=0&limit=100 pages through the rows with their compact answers (the four statuses, recommended_action, the amount result and delta, the reuse and obligation status, reason codes, decision_id). Filter with state= or review=true.
  • GET /v1/batches/{batch_id}/export.csv returns every row and its answer. Cells that a spreadsheet would read as a formula are prefixed with ', and every cell is quoted.

summary counts rows by state and by overall_state, gives the distribution of each status axis and of recommended_action, the number of rows with analyst_review_required, the errors, and the cost: usd_total, usd_per_transaction and usd_per_exception (any answer other than CLEAR), with cost_complete. obligations lists the open ones with what is allocated and what remains.

analyst_review_required marks a row a person must look at: a MANUAL_REVIEW or HOLD_AND_ESCALATE recommendation. A row whose answer is not final yet (RECHECK_LATER) asks for a retry, not for a person.

Sandbox

Sandbox keys run batches against sandbox fixtures and spend the sandbox allowance; the same file runs unchanged on a live key. Raw row values are kept for 90 days, like the ledger's, and then removed; counts, statuses and digests remain.

The Business Terms, Pilot Agreement, Pilot Data Annex and Privacy Policy are available from the portal legal links. Pre-incorporation templates still require the contracting and commercial fields before signature. The initial pilot profile is official USDT/TRON.

There is no dedicated integrity-only switch: checks invoke the risk layer and show insufficient/degraded coverage when required risk evidence is missing. The portal presents compact rows, review flags, known residuals and CSV; detailed decision evidence and reversal/correction operations use the API. A completed batch is a decision-time snapshot and does not automatically refresh after later ledger corrections. DONE means processed, not paid or a customer-confirmed outcome.

Escrow deals

An escrow deal holds USDT on TRON in its own contract. The money moves only when someone with the right acts: the buyer releases it, the seller refunds it, a party claims it after a deadline the other side let pass, both parties propose the same split in a dispute, or a resolver named before funding decides one. Your platform drafts the deal and shows it; the buyer and the seller sign the terms and move the funds with their own wallets. ChainZap creates the contract once both have signed and watches the deadlines, but it never holds the parties' keys, is never a resolver and cannot move the money.

Escrow deals are a closed pilot. Until your organization is enabled, every call answers 403 escrow_not_enabled. The sandbox runs on the TRON Nile test network with a test token that has no value.

Two modes

Every deal can be disputed. The only choice is whether a resolver may also decide a dispute.

No resolver (default) With an external resolver
Request resolver_enabled omitted or false "resolver_enabled": true, resolver_id, resolver_fee, timeline.resolve_period_seconds
Resolver none: the contract records the zero address, no fee and no window an external resolver your platform registered, fixed with its fee and window in the signed terms before funding; it can never be named or changed later
A dispute settles when both parties propose the same split the same, or by the resolver's ruling within its window
No settlement deadlock: the funds stay locked until the parties agree, the buyer releases or the seller refunds the same, once the resolver's window has passed
Resolver fee none paid from the escrow only if the resolver decides the dispute

Start without a resolver: it suits deliveries that pass or fail an objective check, and both parties can always end a dispute themselves.

What the contract allows

  • Each deal is its own contract, created by an immutable factory: no owner, no upgrade, no pause and no admin key.
  • Money moves only on a party's own act, or on the resolver's ruling within its window:
    • the buyer releases (release) and the seller refunds (refund) at any time after funding until the deal settles, a dispute included;
    • the buyer claims a refund (claimRefund) once the delivery deadline passed without delivery; the seller claims the payment (claimRelease) once inspection ended without acceptance or dispute;
    • in a dispute, the deal settles the moment both parties' proposals match (proposeSettlement), or when the resolver rules (resolve).
  • No timer decides a dispute, and a deadline moves nothing by itself: it only opens a claim for one party.
  • Anyone, ChainZap included, may only confirm the funding (confirmFunding), close an unfunded deal after its deadline (expire), pay out what is already credited to its fixed recipient (withdraw) and credit stray tokens to the buyer (sweepExcess). All of these work directly on the contract, without ChainZap.
  • Payouts go only to the buyer, the seller, the fee recipient fixed in the factory and, after its ruling, the named resolver.

Lifecycle

Step Who Result
Draft your platform, POST /v1/escrow-deals status: DRAFT, the participant link
Consent buyer and seller, each with their wallet the text both sign commits to every term
Agreement automatic once both signed AGREED, then CREATING while the contract is deployed
Funding buyer: an ordinary token transfer of the amount to the escrow; then anyone calls confirmFunding state: FUNDED
Delivery seller: markDelivered with an evidence hash DELIVERED, the inspection period starts
Acceptance buyer: release, or dispute within the inspection period RELEASED or DISPUTED
Silence buyer: claimRefund after the delivery deadline; seller: claimRelease after inspection REFUNDED or RELEASED
Dispute both parties: proposeSettlement; the resolver, if any: resolve within its window RELEASED, REFUNDED or SPLIT

status is the off-chain draft state (DRAFT, AGREED, CREATING, ON_CHAIN, CANCELLED, AGREEMENT_EXPIRED, CREATION_BLOCKED); once on chain, state is the contract's own (AWAITING_FUNDS, FUNDED, DELIVERED, DISPUTED, RELEASED, REFUNDED, SPLIT, EXPIRED). Deadlock is not a contract state: it is a dispute no resolver can decide any more, reported as settlement.deadlocked. The chain is the source of truth: the API reports what it read from solidified blocks and marks contract.out_of_sync when it cannot confirm the latest state.

Funding

Funding is an ordinary transfer of the deal's token to the escrow address, with no allowance and no transferFrom. Once the escrow holds at least the amount, anyone may call confirmFunding before the funding deadline; ChainZap does it as soon as it sees the balance. Until then funding reports what the escrow has received and what remaining amount is missing, as last read from the chain.

  • Less than the amount. The deal stays AWAITING_FUNDS; send the rest before the funding deadline. confirmFunding refuses an escrow that holds less.
  • More than the amount, or a second transfer. After funding, the excess is credited to the buyer (sweepExcess, event escrow_deal.excess_credited) and withdrawn like any payout. It is never locked in the deal and never goes to ChainZap.
  • Too late. After the funding deadline the deal can only be closed (expire, state EXPIRED); anything sent to it is credited to the buyer and can be withdrawn.
  • From another address. Only the escrow's balance counts, so a transfer from any address funds the deal, and every refund or excess goes to the buyer's address in the terms, not to the sender.

The deal page sends exactly the remaining amount to the escrow it read from the factory, and refuses any other recipient, token or value.

Deadlines and claims

State Deadline What it opens Who must act
AWAITING_FUNDS funding deadline the deal closes unfunded (EXPIRED) anyone; ChainZap does
FUNDED delivery period after funding a full refund to the buyer the buyer (claimRefund)
DELIVERED inspection period after delivery the payment to the seller, less the fee the seller (claimRelease)
DISPUTED the resolver's window, with a resolver nothing: deadlock until the parties settle the parties

Deadlines use block time. An action is accepted only before its deadline and a claim only from the deadline on, so the two never overlap. A claim is not automatic: until the party makes it, the other side can still act, for example the buyer can still release after the delivery deadline. timeline.if_nobody_acts in every response says what the current deadline opens: at, outcome (EXPIRED, REFUNDED, RELEASED or DEADLOCK) and claim_by (BUYER, SELLER, ANYONE, or null when nothing opens).

Live deals give the buyer at least 72 hours to inspect (422 inspection_period_below_policy otherwise). Because silence lets the seller claim the payment, ChainZap reminds the buyer by Telegram right after delivery, half way through inspection, 12 hours and 1 hour before it ends; the seller is reminded 24 and 2 hours before the delivery deadline, and each party is told when its claim opens.

Disputes, proposals and deadlock

The buyer may open a dispute (dispute, with an evidence hash) only during inspection. From then on the seller cannot claim the payment, and no timer decides the outcome.

  • Proposals. Each party proposes buyer_amount, what the buyer gets back out of the amount: 0 sends everything to the seller (less the fee), the full amount refunds the buyer, anything between is a split. Either party may change its proposal at any time; the deal settles the moment both match (settlement_reason: MUTUAL_SETTLEMENT, event escrow_deal.settlement_proposed for each proposal). settlement.buyer_proposal and settlement.seller_proposal show the current ones.
  • Resolver. In a deal that named one, the resolver may rule within resolve_period after the dispute (timeline.resolve_deadline): resolve(buyer_amount, ruling_hash), by the signed criteria only. Its fee comes off the top, and the ruling is what the buyer gets out of the rest (settlement_reason: RESOLVER).
  • Deadlock. Without a resolver, or once its window has passed, a dispute without matching proposals is a deadlock (settlement.deadlocked: true). The funds stay locked until both parties propose the same split, the buyer releases or the seller refunds. Neither a timer nor ChainZap can end it.

Money

The service fee is a fixed amount set when the deal is drafted (fee_bps of the amount, by your pilot policy), paid only out of the seller's payout:

  • release or the seller's claim: the seller receives the amount minus the fee;
  • split: the fee is cut in proportion to the seller's part;
  • refund, the buyer's claim or expiry: no fee.

The resolver fee is fixed in the terms and paid only if the resolver decides the dispute. Example: amount 100, resolver fee 5, ruling "buyer 40": the buyer gets 40, the resolver 5 and the seller 55 less the service fee on 55. A settlement by matching proposals, a release or a refund pays the resolver nothing.

balances separates what the contract credited (accrued), what is still to withdraw (claimable) and what has left the contract (withdrawn), each for buyer, seller, fee and resolver. Each party pays the network cost of its own transactions in TRX; ChainZap pays for confirming the funding, closing unfunded deals, crediting excess and pushing payouts.

Create a draft

cURL
curl --max-time 15 https://api.chainzap.io/v1/escrow-deals \
  -H 'X-API-Key: YOUR_SERVER_API_KEY' -H 'Content-Type: application/json' \
  -d '{
    "external_reference": "order-2026-10-0042",
    "buyer": "TAHXBmAAswvxo1fx7NzQeJe234766SHwG5",
    "seller": "TTogA7AhPuChVpzUEePBhPyFi8VsStSAmn",
    "amount": "1250.50",
    "category": "DIGITAL_GOODS",
    "title": "Annual licence key",
    "delivery": {
      "criteria": ["The key activates on the vendor site", "The licence covers 12 months"],
      "method": "Key sent through the order chat",
      "evidence_required": ["Screenshot of the activation page"]
    },
    "timeline": {
      "delivery_period_seconds": 259200,
      "inspection_period_seconds": 259200
    }
  }'

This draft has no resolver. To name one, add "resolver_enabled": true, the resolver ("resolver_id", or your policy's default), its fee in token units and its window after a dispute:

JSON
{
  "resolver_enabled": true,
  "resolver_id": "0192f0c4-6f1e-7a3b-9c2d-4e5f60718293",
  "resolver_fee": "12.50",
  "timeline": {
    "delivery_period_seconds": 259200,
    "inspection_period_seconds": 259200,
    "resolve_period_seconds": 259200
  }
}

A deal with a resolver without these settings answers 422 resolver_settings_required; a deal without one that sends any of them answers 422 resolver_settings_without_resolver. The resolver fee may not exceed the share of the amount your policy allows (422 invalid_resolver_fee).

The same external_reference with the same body returns the same draft (replayed: true); with a different body it answers 409 escrow_reference_conflict. Find a draft later with POST /v1/escrow-deals/lookup. Delivery criteria are two to five objective checks: a dispute is argued against these lines only. Categories are limited to digital deliveries with an objective acceptance test, as enabled by your pilot policy.

POST /v1/escrow-deals/{ref}/versions replaces the terms of a draft (send expected_version); signatures on the previous version stop counting. POST /v1/escrow-deals/{ref}/cancel closes a draft. Neither is possible once both parties signed.

Resolvers

A resolver is optional. A deal that names one names an external resolver your platform registered. A resolver is never ChainZap: its address and every key of its TRON account must not be a ChainZap address or key, nor a key of the deal's buyer or seller.

shell
curl --max-time 15 https://api.chainzap.io/v1/escrow-deals/resolvers \
  -H 'X-API-Key: YOUR_SERVER_API_KEY' -H 'Content-Type: application/json' \
  -d '{
    "address": "TJRyWwFs9wTFGZg3JbrVriFbNfCug5tDeC",
    "name": "Example dispute service",
    "affiliation": "INDEPENDENT",
    "rules_version": "dispute-rules/2026-10",
    "rules_sha256": "5f0c…64 hex characters"
  }'

affiliation is PLATFORM (operated by your platform) or INDEPENDENT; the parties see it, and that your platform registered the resolver. On live the account is read on chain: it must sign rulings with one key (SINGLE_KEY) or two of three keys whose active permission may only call contracts (MULTISIG_2_OF_3); anything else is refused (resolver_scheme_unsupported, resolver_account_not_activated). On the sandbox the read is best effort; a resolver that cannot sign stays registered but eligible: false.

GET /v1/escrow-deals/resolvers lists your resolvers and the shared ones; each shows key_scheme, signatures_required, eligible and ineligible_reason. POST …/resolvers/{id}/verify reads the account again. POST …/resolvers/{id}/disable stops new deals naming it (deals that already name it keep it); …/enable allows them again after a fresh read on live. A registration cannot be edited: a resolver's identity is part of the terms both parties sign, so a different resolver is a new registration. The fee is not part of the registration: each deal sets its own. Up to 20 active resolvers per network (100 registrations in all); another organization's resolver answers 404 resolver_not_found. The developer portal's escrow tab does the same.

Send the parties the deal link from links. On the deal page each signs in with the wallet named in the deal, sees every term, and signs one text with TronLink (signMessageV2). The page recomputes the terms hash, the deal id and the text from the terms it shows and refuses to offer a signature if anything differs. Every transaction it offers later is checked against the same terms: the escrow address is read from the factory's own registry, a transfer goes only to that escrow and only for the remaining amount, and a withdrawal only to the signed-in party.

To collect signatures in your own interface, read GET /v1/escrow-deals/{ref}/consent: message is the exact text, params the contract parameters and terms the document (chainzap.escrow.terms/2) whose canonical JSON hash is terms_hash. Relay a signature with POST /v1/escrow-deals/{ref}/consents (version, role, signature). A party whose account is controlled by other keys (permission multisig) approves on chain instead (approveTerms in actions).

Both signatures are verified twice: by the API, and by the factory, which renders the same text itself before it creates the contract. The text names the resolver or Resolver: none, and says what silence and a dispute mean. These are its lines for a deal with a resolver, then for one without:

text
Resolver: TJRyWwFs9wTFGZg3JbrVriFbNfCug5tDeC
Resolver fee: 12.500000 USDT, paid from the escrow only if the resolver decides a dispute
Resolver period after dispute: 3d 00h 00m
If delivery is not marked in time: the buyer can claim a refund
If the buyer neither accepts nor disputes during inspection: the seller can claim payment
In a dispute: no automatic outcome; it settles when buyer and seller propose the same split, or by the resolver within its period; otherwise the funds stay locked

Resolver: none
If delivery is not marked in time: the buyer can claim a refund
If the buyer neither accepts nor disputes during inspection: the seller can claim payment
In a dispute: no automatic outcome; it settles when buyer and seller propose the same split; otherwise the funds stay locked

Party transactions

GET /v1/escrow-deals/{ref}/actions?role=BUYER lists what that party can sign now. Each action carries the contract, the function, the ABI-encoded parameters, a fee limit and an energy estimate:

Action Who Call
TRANSFER_FUNDS buyer transfer(address,uint256) on the token: the escrow and the remaining amount
CONFIRM_FUNDING anyone confirmFunding() once the escrow holds the amount
MARK_DELIVERED seller markDelivered(bytes32) with the evidence hash
RELEASE, REFUND buyer, seller release(), refund()
CLAIM_REFUND, CLAIM_RELEASE buyer, seller claimRefund() after the delivery deadline, claimRelease() after inspection
DISPUTE buyer dispute(bytes32) during inspection
PROPOSE_SETTLEMENT buyer, seller proposeSettlement(uint96); parameters is empty: encode the buyer's amount yourself, from 0 to the deal amount
EXPIRE, SWEEP_EXCESS anyone expire() after the funding deadline, sweepExcess() when the escrow holds more than it owes
WITHDRAW the recipient withdraw(address) for a credited payout

Actions anyone can call are listed under each party's role while they are open. If your own interface builds these transactions, make the same checks before your users sign: the escrow is escrowOf(deal_id) on the factory named in the signed terms, and the token is the factory's. After you relay a transaction, report it with POST /v1/escrow-deals/{ref}/transactions; it counts only when the chain confirms it, and reporting never changes the deal by itself. A report is accepted only while the deal can have that transaction (409 action_not_available) and with at most five pending per party (429 too_many_pending_transactions); a confirmed transaction that is not that call from that party is dropped.

Telegram

links.telegram_invite is a t.me link to the ChainZap bot for this deal; share it with the parties next to the deal link. The bot names no deal details: it opens the deal page in the browser, where every signature and payment happens, and the deal's status in the Telegram Mini App. The status screen shows only deals a party linked from a private chat after signing in with their wallet; nothing can be signed or sent from Telegram. Web and Telegram show the same deal under the same id.

Evidence

POST /v1/escrow-deals/{ref}/evidence adds a text note and PUT /v1/escrow-deals/{ref}/evidence/files?purpose=DELIVERY&file_name=log.pdf a file (PDF, PNG, JPEG, WebP or plain text, up to 10 MiB). Items you add are labelled as submitted by the platform: they show who sent them, not that a delivery happened. Each item keeps its SHA-256, author and time; a replacement is a new item. When a party marks delivery or opens a dispute, the hash of their evidence set is recorded on chain. File contents are deleted after the retention period; the hashes stay.

GET /v1/escrow-deals/{ref}/dispute returns the criteria, both sides' evidence and the dispute deadlines.

Events and webhooks

Every change becomes an event: escrow_deal.created, terms_updated, consent_received, agreed, cancelled, agreement_expired, creation_blocked, contract_created, funded, delivered, disputed, settlement_proposed, settled, excess_credited, payout_withdrawn and evidence_added. Deadline reminders go to the parties by Telegram, not to the webhook.

Set one endpoint per environment with PUT /v1/escrow-deals/webhook ({"url": "https://..."}); the response shows the signing secret once. The endpoint must be HTTPS on port 443 at a public address; redirects are not followed. Each delivery is a POST with:

Header Meaning
X-ChainZap-Event-Id the event, the same on every retry
X-ChainZap-Delivery-Id this delivery attempt series
X-ChainZap-Timestamp Unix seconds when signed
X-ChainZap-Signature v1= HMAC-SHA256 of {timestamp}.{delivery_id}.{body} with your secret
Python
import hashlib, hmac, time

def verify(secret: str, headers: dict, body: bytes) -> bool:
    timestamp = int(headers["X-ChainZap-Timestamp"])
    if abs(time.time() - timestamp) > 300:
        return False
    message = f"{timestamp}.{headers['X-ChainZap-Delivery-Id']}.".encode() + body
    expected = "v1=" + hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, headers["X-ChainZap-Signature"])

Answer 2xx within 10 seconds. Failed deliveries are retried with growing pauses (30 seconds up to 12 hours, 12 attempts). Delivery is at least once and not in order: deduplicate by X-ChainZap-Event-Id and use sequence to order. GET /v1/escrow-deals/events?after=<sequence> is the full feed to catch up after downtime; POST /v1/escrow-deals/events/{event_id}/redeliver sends one again, POST /v1/escrow-deals/webhook/test sends a signed escrow.ping, POST /v1/escrow-deals/webhook/rotate-secret replaces the secret and DELETE /v1/escrow-deals/webhook stops deliveries.

What changed in protocol v2

New deals use contract and terms version 2. If you integrated with the first pilot version:

  • Draft request. dispute_enabled is now resolver_enabled: every deal can be disputed, only the resolver is optional. With a resolver send resolver_id, resolver_fee and timeline.resolve_period_seconds. timeline.response_period_seconds and timeout_outcome are gone: there is no seller answer window and no outcome by timeout in a dispute.
  • Deal response. New: protocol_version, resolver.fee, timeline.resolve_deadline, settlement (proposals and deadlocked, while disputed), funding (while awaiting funds) and resolver in each of balances.accrued, claimable and withdrawn. timeline.if_nobody_acts is now {at, outcome, claim_by}. Gone: dispute_enabled, timeline.response_period_seconds, timeline.seller_response_deadline and timeline.seller_responded. settlement_reason may be MUTUAL_SETTLEMENT; TIMEOUT_NO_RULING appears only on archived deals.
  • Funding and deadlines. Funding is a direct transfer and confirmFunding instead of an allowance and fund. After a delivery or inspection deadline the buyer or the seller claims the money; nothing settles by timeout.
  • Actions. New: TRANSFER_FUNDS, CONFIRM_FUNDING, CLAIM_REFUND, CLAIM_RELEASE, PROPOSE_SETTLEMENT, EXPIRE and SWEEP_EXCESS. Gone: APPROVE_TOKEN, FUND, RESPOND, PROCESS_TIMEOUT and CREDIT_EXCESS.
  • Events. New: escrow_deal.settlement_proposed and escrow_deal.excess_credited. Gone: escrow_deal.seller_responded.
  • Terms. chainzap.escrow.terms/2 adds rules: "chainzap.escrow.rules/2" and resolver.fee, and drops timeout_outcome and timeline.response_period.
  • Errors. New: resolver_settings_required, resolver_settings_without_resolver, invalid_resolver_fee and invalid_buyer_amount. Gone: invalid_simple_deal, dispute_settings_required, dispute_settings_without_disputes, disputes_disabled and invalid_timeout_outcome.

Deals created before version 2 keep protocol_version: 1. They are archived: the API and the deal page show them read-only from ChainZap's record, and nothing acts on them.

Errors

Code When
403 escrow_not_enabled escrow deals are off for this organization or environment
403 participant_not_allowed a live party is not on your pilot list
422 participant_screening_failed a party did not pass address screening; on live, 503 screening_unavailable when a list cannot be checked
422 deal_amount_above_pilot_cap, 409 pilot_volume_cap_reached the deal exceeds your pilot limits
422 category_not_enabled, 422 resolver_not_available, 422 resolver_not_verified the category or resolver is not available
422 inspection_period_below_policy, 422 resolve_period_below_policy, 422 invalid_period a period is shorter than your policy allows or outside the factory's bounds
422 resolver_settings_required, 422 resolver_settings_without_resolver the resolver settings do not match resolver_enabled
422 invalid_resolver_fee the resolver fee exceeds the share your policy allows, or is set without a resolver
422 resolver_is_chainzap, 422 resolver_is_party, 422 resolver_scheme_unsupported, 422 resolver_account_not_activated the resolver cannot be registered or named
409 resolver_already_registered, 409 resolver_limit_reached, 404 resolver_not_found resolver registration
422 invalid_consent_signature the signature does not match the party and these terms
409 terms_version_conflict, 409 escrow_terms_locked the terms changed, or can no longer change
409 agreement_expired the time to sign has run out
409 action_not_available, 429 too_many_pending_transactions a reported transaction is not possible now, or too many are still pending
404 escrow_not_found no such deal in this organization and environment

All errors use the common format. Escrow requests are limited to 16 KiB of JSON; an evidence file to 10 MiB.

Sandbox

Sandbox keys work on the TRON Nile test network with the test token labelled "ChainZap TEST USD (NO VALUE)". The flow, the pages and the webhooks are the same as live, and the sandbox policy may allow a shorter inspection period to try the flow; no real funds can be involved. Live deals use USDT on TRON mainnet only for pilot organizations, with an allowlist of participants and caps per deal and in total.

GET /v1/usage

Request

cURL
curl --include --max-time 15 'https://api.chainzap.io/v1/usage?days=30' \
  -H 'X-API-Key: cz_live_000000000000.fictional_example_only' \
  -H 'X-Request-ID: merchant-usage-1001'

days is an integer from 1 to 90; the default is 30. This read does not need an idempotency key. Read the X-Request-ID response header. A usage request is rate-limited but does not use checks. Retry only errors marked retryable and respect Retry-After.

Quota for the current period

period.start and period.end delimit the current billing period: the paid period anchored to the confirmed payment, or the UTC calendar month for an organization without a paid plan.

Field Meaning
limit Included checks for the period.
used Completed checks that were charged.
reserved Checks that are still running and hold a reservation.
remaining max(0, limit - used - reserved).

A reservation is released when a request finishes without a charge. balances adds extra checks and the total available; subscription describes the paid period; status: "legacy" with plan: null means your organization's limits were set by agreement (for example a pilot contract) rather than by a self-serve plan, and the limits shown are the ones that apply. See plans and payment. environment is live or sandbox, depending on the key. All API keys of the organization and requests made in the developer dashboard share the same quota.

Statistics for the selected window

recent counts completed logical checks during the requested rolling window of days days. Idempotent HTTP replays are not new check records. It includes charged, error and LIMITED_DATA counts, average latency, networks and the overall-state distribution. It contains no transaction hashes or provider details.

The developer dashboard adds UTC daily buckets, 1/7/30-day counters and p50/p95 latency when at least 20 completed latency samples exist in the last 30 days. Successful-response counts include partial responses; limited coverage is a subset, not an additional total.

The usage chart compares completed requests with charged checks for 7, 30 or 90 days. Hover over the chart, tap it or use the day slider with the arrow keys to inspect exact values. Days without checks show zero; the first and last UTC days may be partial. Live and Sandbox are separate views. The daily table remains available below the charts. The allowance bar shows included checks for the current billing period; extra packages are listed in Plan and billing.

Developer dashboard and plans

After signing in to the developer dashboard, open /dashboard/usage/ for quota counters, daily tables, result distribution and latency statistics. /dashboard/plan/ shows the current plan, monthly quota, requests per minute and the concurrency limit. Networks entitled by the plan and networks currently enabled for checks are displayed separately; an enabled flag is not a live provider health check.

The Playground page accepts automatic network detection or an explicit TRON, Ethereum or TON selection within your available networks. Its checks use the same organization quota and idempotency rules as the server API.

Plans and extra checks packages are bought on the Plan and billing page; see plans and payment. Renewal is manual: there is no automatic charge, invoicing or API wallet. Consumer Mini App subscriptions do not grant B2B API quota. Public documentation does not require sign-in.

Plans, checks and payment (beta)

What you pay for

A check is one API check of a transaction. You pay for a complete, final check only: a response with coverage: complete and a state of CLEAR, REVIEW or HIGH_RISK for a transaction that is confirmed past its finality policy or failed for good. A risky result is a useful result and is charged. A pending transaction, a transaction that was not found, LIMITED_DATA, transport errors, timeouts, quota errors and idempotent replays are not charged on either endpoint; their reservation is released. A successful HTTP response is not the same as a charged check.

Beta plans (pricing policy BETA_PRICING_V0)

A plan gives your organization a number of included checks per billing period.

Plan Per month Included checks Extra check
Sandbox free 50 test checks per UTC month not sold
Starter $99 250 $0.40
Growth $299 1 000 $0.30
Business $999 5 000 $0.20
Scale individual individual individual

Prices are in US dollars. GET /v1/catalog publishes them in integer minor units (cents). These are preliminary beta terms and may change for future billing periods; an order or a billing period that is already paid keeps the terms it was sold under. Request-rate and concurrency limits are preliminary until general availability. Scale is agreed individually and is not sold online.

cURL
curl --max-time 15 https://api.chainzap.io/v1/catalog \
  -H 'X-API-Key: cz_live_000000000000.fictional_example_only'

Billing periods and renewal

A paid billing period starts when the payment is confirmed on the server and lasts one calendar month (31 January runs to 28 or 29 February; the anchor day returns afterwards). Renewal is manual: nothing is charged automatically. Paying early schedules the next billing period to start when the current one ends; paying after a gap starts a new billing period immediately. A different plan applies from the next billing period; the current paid period is never reset. Included checks do not carry over.

Extra checks packages

An extra checks package adds checks on top of the included checks. A package contains 100 to 10 000 checks in steps of 50 and costs the extra-check price of your current plan. Extra checks are valid for 12 months from purchase, are used only after the included checks, and can be used only while a paid plan is active. While the plan is inactive they are frozen, not lost. The price is fixed at purchase and is not recalculated by later catalog changes.

Payment

Purchases are made in the developer dashboard by owner, admin and billing members, never with an API key. Payment is in cryptocurrency. Check the total and choose Pay with crypto: the payment page opens, where you pay the exact total in USDT or another coin offered there. The total is fixed in US dollars, and the page stays payable for the order's validity time, at least 15 minutes. If you leave it, the order on the Plan and billing page takes you back to it or cancels the order. Checks are granted only after the server confirms the payment with the payment provider itself: the wallet's own response, a screenshot or returning to the dashboard page does not grant anything. A payment page cannot be withdrawn, so money sent after you cancel an order or after it expires is still credited when it can apply and reviewed by support otherwise. A duplicate or partial payment is recorded and reviewed by support; it is never granted twice.

Balance in the API

cURL
curl --max-time 15 'https://api.chainzap.io/v1/usage?days=7' \
  -H 'X-API-Key: cz_live_000000000000.fictional_example_only' | jq '.balances, .subscription'

period.remaining keeps its meaning: the included checks left in the current billing period. balances.extra shows extra checks, balances.total_available shows the total that a new check can draw on, and balances.included.reserved shows checks that are still running. subscription reports the billing period, the plan and the manual renewal. The X-Quota-Available header carries the total on check responses. A balance does not override a suspended organization, a network that is not enabled or an inactive plan: 429 quota_exceeded is returned when nothing is available.

Refunds and disputes

Support handles refunds case by case and records them as separate events: the unused remainder of a grant is reversed, checks already used are not clawed back, and the available balance never goes below zero.

Sandbox

Sandbox keys start with cz_test_. They are created in the developer dashboard or by your organization's operator. They call the same endpoints with the same request and response schemas, but never reach data providers, never cost anything and never touch your paid checks. Every sandbox success carries environment: "sandbox", and so does every error the check itself returns (for example provider_unavailable, network_not_enabled, quota and rate limits): branch on that field. A refusal before the check starts (401, 403 tenant_suspended, 503 api_disabled, the per-IP 429 applied before the key is read, 413, 415, a 422 request validation error, invalid_idempotency_key) carries no environment, and an unexpected 500 or 504 may carry none; there the key you sent tells the environment. POST /v1/check and the wallet check also carry details.evidence.source: "sandbox"; POST /v1/transaction/check has no details, and its evidence items name the same source roles as a live answer, so rely on environment. Treat such a result as a fixture, never as an observation. Each fixture explains itself with the same reason_codes the live engine emits, so the branches you write against the sandbox are the branches production takes.

Deterministic outcomes

The first character of the transaction hash selects the outcome, so you can exercise every state on purpose:

First hex character Result
0–3, c, f CLEAR, complete coverage
4–5 REVIEW, complete coverage
6 HIGH_RISK from an elevated AML level, complete coverage
7 HIGH_RISK from a sanctions hit (sanctions_hit: true), complete coverage
8–9 LIMITED_DATA, partial coverage, not charged
a transaction not found, as live: transaction_status: NOT_FOUND, retryable, not charged; /v1/check answers 404 transaction_not_found
b provider outage (503 provider_unavailable), not charged
d failed (reverted) transaction: transaction_status: FAILED, recommended_action: NOT_EXECUTED, the *_match fields are null; final, so charged like a live failed transaction
e pending transaction: REVIEW with TX_PENDING_CONFIRMATIONS, complete coverage, not charged

The sandbox fabricates only what a provider would return. The comparison with your expectations, the status axes, the evidence items and the reasons come from the live engine, so a sandbox answer has the shape and the rules of a live one, contract 0.3 fields included. With the obligation ledger on, sandbox checks, obligations and batches are recorded in a sandbox ledger kept apart from the live one.

cURL
curl --max-time 15 https://api.chainzap.io/v1/check \
  -H 'X-API-Key: cz_test_000000000000.fictional_example_only' \
  -H 'Idempotency-Key: sandbox-demo-1' -H 'Content-Type: application/json' \
  --data '{"tx_hash":"8111111111111111111111111111111111111111111111111111111111111111"}'

Without network, the sandbox answers for TRON when TRON is enabled for your organization, otherwise for the first network you have. As in live discovery, the shape of the hash (0x, base64) never selects the network. Send network explicitly to see whether a network is enabled: it answers 403 network_not_enabled in the sandbox exactly as it does live. Base64 TON hashes use the top four bits of the first byte.

Allowance and isolation

The sandbox spends its allowance on exactly what a live check is charged for: a complete, final result. A pending one, a partial one, an absent transaction and the outage fixture cost nothing, as in production; the failed fixture (d) is final and is charged, as a live failed transaction is. If your organization is on the pilot contract, sandbox keys are refused the same way live keys are (expected_fields_required, token_not_enabled, check_kind_not_enabled). expected_recipient, expected_token and expected_amount are compared against the fixture and a mismatch is never CLEAR, so the branch you test is the branch production takes; a nonzero digit beyond the token's decimals answers 422 invalid_expected_amount, as live. Sandbox checks draw on a separate allowance of 50 test checks per UTC calendar month; GET /v1/usage with a sandbox key reports that allowance with environment: "sandbox". Idempotency keys, quota and usage statistics are separate per environment: the same Idempotency-Key in the sandbox and in the live environment are two different operations. A sandbox key cannot buy anything and does not prove that a live network is enabled for your organization.

Wallet fixtures

For POST /v1/wallet/check the sandbox picks the outcome from a hash of the normalised address, so it cannot be steered by the first character. Use one of the pinned addresses below; each row is stable across releases and checked by the test suite.

Outcome Result
clear CLEAR, complete coverage, charged
review REVIEW from behaviour, complete coverage, charged
high_risk_denylist HIGH_RISK from a denylist hit, complete coverage, charged
high_risk_sanctions HIGH_RISK from a sanctions match, complete coverage, charged
limited_data LIMITED_DATA, partial coverage, not charged
review_partial REVIEW with indirect exposure and partial counterparty screening, charged
provider_unavailable 503 provider_unavailable, not charged
Outcome Ethereum TRON TON
clear 0xc933ee607c865f6fbc6222330dc9166e1cc6d64f TPkD2p19tKyRxAyRFyFdAc5vdPifpmtUfc 0:ad4ab97d14757696f1af317e613adc31b296e683ad1795880c8c906de6daf327
review 0x0aa8246d9ace486edb4cb7600ac6d40c3c8f884b TBBBSYtDqb7szRTcJ9YW4AdrEFTz7a9pvi 0:fa057a9368fb5432944e6c874d1f92e8a1cd893f88e6f18a868c08a3bf7dc885
high_risk_denylist 0x1e799ef4c436f1e576f1cf40fb3655ca7e87cd4a TZBCVU6RLDLhvJoJUPksGjGMdFh7LngTXy 0:0ab976607b5b1f1b384b6fe79521bfc2d11a868b2b2ed38e118cc5f1e3e63990
high_risk_sanctions 0x88c967e52185aef0263776a7efa1521c66a88547 TEs2fpP1TLoCwjAwxmwZz7sZVdwrWrKdRQ 0:bf24aaea2aa3bdd5d48c68ba1cbb7869a50fa2b39b7ed6e2c3c9aec437330461
limited_data 0x9c689365b5e78b2bab2d1b0f4e0734540fb719f1 TFFXvwH4owR9bDNg3g3MG8K1a9mX8PurXm 0:b5c1220a9967b961b34ff54e836ef02f0942c2b5597c559129b3e5fcfc83dfaa
review_partial 0x5629d9db95999852cb0c0b4e9afd057800800cfb TB7fLLjaJqubgudHK5d7mQ754sAds3qsB7 0:ecf8f65ba4afdd1da26b0774b59b688907accdaf5e2ae4ef47ad4a3014fc2c2b
provider_unavailable 0xf60ca8315084bc9b55a2f4026d8856f1170ef8db TAy5XDN27LLL2eoBbQcota2a3YD9iHc3MT 0:a765cd5463fe92dbf3e928978ec5c8e04003ae8589d5116fa5a17c1a0507b259

Every wallet fixture carries environment: "sandbox" and details.evidence.source: "sandbox", explains itself with the same reason codes as the live engine, and spends the sandbox allowance exactly as a live wallet check spends quota: when a state is produced.

Integration examples

These retry examples and response fixtures use the canonical contract 0.3 POST /v1/transaction/check endpoint with an explicit network. For the shorter /v1/check request and its short response, see one hash check. Both endpoints use the same retry and quota rules.

A successful HTTP response must be evaluated on separate axes: verification_final with integrity_status for payment comparison; risk_status, risk_final and recommended_action for risk and action. Pending never means MATCH; final MATCH with insufficient KYT requires MANUAL_REVIEW. Risk retries use risk_retryable and risk_retry_after_seconds, not verification retryable. See status axes.

Python: idempotent retry

This server-side example uses httpx. The key is fictional. In a real integration, load the API key from your server's secret store and persist operation_key and body in your own operation ledger before the first attempt. After a process restart, reuse both.

Python
import random
import time
import httpx

api_key = "cz_live_000000000000.fictional_example_only"
operation_key = "merchant-order-1001-check-1"
body = {
    "network": "tron", "tx_hash": "a" * 64,
    "expected_recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
    "expected_token": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
    "expected_amount": "1250.000001",
}


def check():
    with httpx.Client(timeout=15, follow_redirects=False) as client:
        for attempt in range(4):
            try:
                response = client.post(
                    "https://api.chainzap.io/v1/transaction/check",
                    headers={
                        "X-API-Key": api_key,
                        "Idempotency-Key": operation_key,
                        "X-Request-ID": f"order-1001-attempt-{attempt + 1}",
                    },
                    json=body,
                )
            except httpx.TransportError:
                if attempt == 3:
                    raise RuntimeError("unresolved_transport_outcome") from None
                time.sleep(2 ** attempt + random.uniform(0, 1))
                continue
            print("X-Request-ID:", response.headers.get("X-Request-ID"))
            try:
                result = response.json()
            except ValueError:
                raise RuntimeError("unresolved_gateway_response") from None
            if response.is_success:
                return result
            error = result.get("error", {})
            if error.get("retryable") is not True:
                raise RuntimeError(error.get("code", "unresolved_response"))
            if response.headers.get("Idempotent-Replayed") == "true":
                raise RuntimeError("stored_error_requires_reconciliation")
            try:
                instructed = int(response.headers.get("Retry-After", "0"))
            except ValueError:
                raise RuntimeError("invalid_retry_after") from None
            delay = max(instructed, 2 ** attempt) + random.uniform(0, 1)
            if attempt == 3 or delay > 30:
                raise RuntimeError("schedule_retry_with_same_operation_key")
            time.sleep(delay)

The loop handles retryable 409/429/503/504 responses and network uncertainty without creating another logical operation. Handle the returned business states according to your payment workflow. Do not log body or the entire result.

JavaScript / TypeScript: server request

Use this code on your server, never in a browser with a live key. The operation key and body must be persisted before calling the function, just as in the Python example.

TypeScript
type PublicResult = {
  request_id?: string;
  error?: { code: string; message: string; retryable: boolean };
  [field: string]: unknown;
};

const apiKey = "cz_live_000000000000.fictional_example_only";
const operationKey = "merchant-order-1001-check-1";
const body = {
  network: "tron", tx_hash: "a".repeat(64),
  expected_recipient: "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
  expected_token: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
  expected_amount: "1250.000001",
};
const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

async function check(): Promise<PublicResult> {
  for (let attempt = 0; attempt < 4; attempt++) {
    let response: Response;
    try {
      response = await fetch("https://api.chainzap.io/v1/transaction/check", {
        method: "POST", redirect: "error", signal: AbortSignal.timeout(15000),
        headers: {
          "Content-Type": "application/json", "X-API-Key": apiKey,
          "Idempotency-Key": operationKey,
          "X-Request-ID": `order-1001-attempt-${attempt + 1}`,
        },
        body: JSON.stringify(body),
      });
    } catch {
      if (attempt === 3) throw new Error("unresolved_transport_outcome");
      await wait((2 ** attempt + Math.random()) * 1000);
      continue;
    }
    console.info("X-Request-ID:", response.headers.get("X-Request-ID"));
    const result = await response.json() as PublicResult;
    if (response.ok) return result;
    if (result.error?.retryable !== true) {
      throw new Error(result.error?.code ?? "unresolved_response");
    }
    if (response.headers.get("Idempotent-Replayed") === "true") {
      throw new Error("stored_error_requires_reconciliation");
    }
    const instructed = Number(response.headers.get("Retry-After") ?? "0");
    if (!Number.isFinite(instructed) || instructed < 0) {
      throw new Error("invalid_retry_after");
    }
    const delay = Math.max(instructed, 2 ** attempt) + Math.random();
    if (attempt === 3 || delay > 30) {
      throw new Error("schedule_retry_with_same_operation_key");
    }
    await wait(delay * 1000);
  }
  throw new Error("retry_budget_exhausted");
}

Response scenarios

The following synthetic fixtures illustrate valid response shapes, not live checks. Every sample has an X-Request-ID header equal to its request_id. Inspect null values and coverage as well as the overall state.

Confirmed transaction

View full JSON response
JSON
{
  "request_id": "example-confirmed",
  "tx_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "network": "tron",
  "transaction_type": "TriggerSmartContract",
  "transaction_category": "trc20_transfer",
  "transaction_state": "confirmed",
  "confirmations": 27,
  "required_confirmations": 19,
  "sender": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
  "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
  "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
  "token_symbol": "USDT",
  "decimals": 6,
  "amount": "1250.000001",
  "transfers": [
    {
      "index": 0,
      "asset_type": "trc20",
      "sender": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
      "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
      "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
      "token_id": null,
      "token_symbol": "USDT",
      "decimals": 6,
      "amount": "1250.000001",
      "event_ref": "log:0",
      "execution_status": "success"
    }
  ],
  "transfers_truncated": false,
  "aml_risk_level": "low",
  "schema_version": "0.3",
  "policy_version": "b2b-tx-2026-10.4",
  "sanctions_hit": false,
  "direct_sanctions_match": false,
  "indirect_sanctions_exposure": false,
  "risk_exposure": false,
  "key_exposure": [],
  "token_authenticity": "official",
  "recipient_match": true,
  "token_match": true,
  "amount_match": true,
  "coverage": "complete",
  "workflow_coverage": "complete",
  "source_coverage": {
    "sanctions_sources": {
      "ofac": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": 172800,
        "scope": "configured_address_index",
        "error_code": null
      },
      "eu": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-02T23:49:24Z",
        "data_updated_at": "2026-09-22T18:01:34Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "uk": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-02T23:47:32Z",
        "data_updated_at": "2026-10-02T00:00:00Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "un": {
        "status": "not_applicable",
        "checked_at": null,
        "retrieved_at": null,
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "configured_address_index",
        "error_code": null
      }
    },
    "transaction_sources": {
      "chain_explorer": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      },
      "chain_node": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      }
    },
    "risk_sources": {
      "aml_provider": {
        "status": "checked_freshness_unknown",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_risk_score",
        "error_code": null
      }
    },
    "universal_clearance": false
  },
  "freshness": {
    "checked_at": "2026-10-10T09:00:00Z",
    "transaction_at": "2026-10-10T09:00:00Z",
    "risk_data_at": "2026-10-10T09:00:00Z",
    "token_data_at": "2026-10-10T09:00:00Z",
    "max_source_age_seconds": 0
  },
  "overall_state": "CLEAR",
  "environment": "live",
  "reasons": [
    {
      "code": "TX_FACTS_VERIFIED",
      "category": "transaction",
      "severity": "info"
    },
    {
      "code": "TX_FINALIZED",
      "category": "transaction",
      "severity": "info"
    },
    {
      "code": "AML_LOW",
      "category": "risk",
      "severity": "info"
    },
    {
      "code": "NO_DIRECT_SANCTIONS_MATCH",
      "category": "sanctions",
      "severity": "info"
    },
    {
      "code": "TOKEN_OFFICIAL",
      "category": "token",
      "severity": "info"
    }
  ],
  "decision_id": null,
  "direction": null,
  "workflow_type": null,
  "versions": {
    "decision": "b2b-tx-2026-10.4",
    "rules": {
      "exposure": "b2b-exp-v2",
      "sanctions_freshness": "sf-v1",
      "billing": "final_answers_only",
      "integrity": "int-v3",
      "reuse": "reuse-v1",
      "allocation": "alloc-v1"
    },
    "reason_codes": "rc-1",
    "evidence_schema": "ev-1",
    "contract": "0.3"
  },
  "transaction_status": "CONFIRMED",
  "integrity_status": "MATCH",
  "risk_status": "NO_MATERIAL_SIGNAL",
  "coverage_status": "OK",
  "status_drivers": {
    "transaction": "TX_FINALIZED",
    "integrity": null,
    "risk": null,
    "coverage": null,
    "action": "CUSTOMER_STATE_UNKNOWN"
  },
  "result_final": true,
  "verification_final": true,
  "risk_final": true,
  "risk_retryable": false,
  "risk_retry_after_seconds": null,
  "retryable": false,
  "retry_after_seconds": null,
  "recommended_action": "CONFIRM_INTERNAL_STATE",
  "integrity": {
    "status": "MATCH",
    "selected_event_ref": "log:0",
    "relevant_event_count": 1,
    "comparisons": {
      "recipient": {
        "expected": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
        "actual": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
        "result": "MATCH"
      },
      "sender": {
        "expected": null,
        "actual": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
        "result": "NOT_EVALUATED"
      },
      "token": {
        "expected": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
        "actual": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
        "result": "MATCH"
      },
      "amount": {
        "expected": "1250.000001",
        "actual": "1250.000001",
        "result": "EXACT",
        "tolerance": null,
        "delta": "0",
        "delta_raw": "0"
      },
      "network": {
        "expected": null,
        "actual": "tron",
        "result": "NOT_EVALUATED"
      }
    }
  },
  "risk": {
    "status": "NO_MATERIAL_SIGNAL",
    "driver": null,
    "driver_evidence_ids": []
  },
  "evidence_items": [
    {
      "evidence_id": "ev_431b2fd74ad43ef5",
      "kind": "transaction_facts",
      "source": "chain_explorer",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "block:80000000",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-10T09:00:00Z",
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_63c8c4e421f6ff66",
      "kind": "transaction_facts",
      "source": "chain_node",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "block:80000000",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-10T09:00:00Z",
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_896f4ce46de5246c",
      "kind": "aml_risk",
      "source": "aml_provider",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "transaction_risk_score",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "CHECK_TIME",
      "coverage": "checked_freshness_unknown",
      "freshness": {
        "age_seconds": 0,
        "stale": true,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_7d0537ae6c2054d5",
      "kind": "sanctions_list",
      "source": "ofac",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 172800
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_2be872923c0687c9",
      "kind": "sanctions_list",
      "source": "eu",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-09-22T18:01:34Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_df2204c780968cb5",
      "kind": "sanctions_list",
      "source": "uk",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-02T00:00:00Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_76c33d7fb3a48fb7",
      "kind": "sanctions_list",
      "source": "un",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_applicable",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "not_applicable",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_81b44db3a9445d39",
      "kind": "token_authenticity",
      "source": "token_registry",
      "subject": {
        "type": "token",
        "ref": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
      },
      "source_reference": "official",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "CHECK_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    }
  ],
  "coverage_limitations": {
    "runtime": [],
    "product": [
      "AML_TRANSACTION_LEVEL_ONLY",
      "RISK_STATE_AS_OF_CHECK",
      "SANCTIONS_CONFIGURED_LISTS_ONLY"
    ]
  },
  "obligation": null,
  "reuse": null
}

Awaiting confirmation

View full JSON response
JSON
{
  "request_id": "example-pending",
  "tx_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "network": "tron",
  "transaction_type": "TriggerSmartContract",
  "transaction_category": "trc20_transfer",
  "transaction_state": "pending",
  "confirmations": 3,
  "required_confirmations": 19,
  "sender": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
  "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
  "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
  "token_symbol": "USDT",
  "decimals": 6,
  "amount": "1250.000001",
  "transfers": [
    {
      "index": 0,
      "asset_type": "trc20",
      "sender": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
      "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
      "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
      "token_id": null,
      "token_symbol": "USDT",
      "decimals": 6,
      "amount": "1250.000001",
      "event_ref": "log:0",
      "execution_status": "success"
    }
  ],
  "transfers_truncated": false,
  "aml_risk_level": "low",
  "schema_version": "0.3",
  "policy_version": "b2b-tx-2026-10.4",
  "sanctions_hit": false,
  "direct_sanctions_match": false,
  "indirect_sanctions_exposure": false,
  "risk_exposure": false,
  "key_exposure": [],
  "token_authenticity": "official",
  "recipient_match": null,
  "token_match": null,
  "amount_match": null,
  "coverage": "complete",
  "workflow_coverage": "complete",
  "source_coverage": {
    "sanctions_sources": {
      "ofac": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": 172800,
        "scope": "configured_address_index",
        "error_code": null
      },
      "eu": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-02T23:49:24Z",
        "data_updated_at": "2026-09-22T18:01:34Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "uk": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-02T23:47:32Z",
        "data_updated_at": "2026-10-02T00:00:00Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "un": {
        "status": "not_applicable",
        "checked_at": null,
        "retrieved_at": null,
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "configured_address_index",
        "error_code": null
      }
    },
    "transaction_sources": {
      "chain_explorer": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      },
      "chain_node": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      }
    },
    "risk_sources": {
      "aml_provider": {
        "status": "checked_freshness_unknown",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_risk_score",
        "error_code": null
      }
    },
    "universal_clearance": false
  },
  "freshness": {
    "checked_at": "2026-10-10T09:00:00Z",
    "transaction_at": "2026-10-10T09:00:00Z",
    "risk_data_at": "2026-10-10T09:00:00Z",
    "token_data_at": "2026-10-10T09:00:00Z",
    "max_source_age_seconds": 0
  },
  "overall_state": "REVIEW",
  "environment": "live",
  "reasons": [
    {
      "code": "TX_PENDING_CONFIRMATIONS",
      "category": "transaction",
      "severity": "warning"
    },
    {
      "code": "TX_FACTS_VERIFIED",
      "category": "transaction",
      "severity": "info"
    },
    {
      "code": "AML_LOW",
      "category": "risk",
      "severity": "info"
    },
    {
      "code": "NO_DIRECT_SANCTIONS_MATCH",
      "category": "sanctions",
      "severity": "info"
    },
    {
      "code": "TOKEN_OFFICIAL",
      "category": "token",
      "severity": "info"
    }
  ],
  "decision_id": null,
  "direction": null,
  "workflow_type": null,
  "versions": {
    "decision": "b2b-tx-2026-10.4",
    "rules": {
      "exposure": "b2b-exp-v2",
      "sanctions_freshness": "sf-v1",
      "billing": "final_answers_only",
      "integrity": "int-v3",
      "reuse": "reuse-v1",
      "allocation": "alloc-v1"
    },
    "reason_codes": "rc-1",
    "evidence_schema": "ev-1",
    "contract": "0.3"
  },
  "transaction_status": "PENDING",
  "integrity_status": "NOT_EVALUATED",
  "risk_status": "NO_MATERIAL_SIGNAL",
  "coverage_status": "OK",
  "status_drivers": {
    "transaction": "TX_PENDING_CONFIRMATIONS",
    "integrity": "TX_PENDING_CONFIRMATIONS",
    "risk": null,
    "coverage": null,
    "action": "TX_PENDING_CONFIRMATIONS"
  },
  "result_final": false,
  "verification_final": false,
  "risk_final": false,
  "risk_retryable": true,
  "risk_retry_after_seconds": 48,
  "retryable": true,
  "retry_after_seconds": 48,
  "recommended_action": "RECHECK_LATER",
  "integrity": {
    "status": "NOT_EVALUATED",
    "selected_event_ref": "log:0",
    "relevant_event_count": 1,
    "comparisons": {
      "recipient": {
        "expected": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
        "actual": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
        "result": "NOT_EVALUATED"
      },
      "sender": {
        "expected": null,
        "actual": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
        "result": "NOT_EVALUATED"
      },
      "token": {
        "expected": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
        "actual": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
        "result": "NOT_EVALUATED"
      },
      "amount": {
        "expected": "1250.000001",
        "actual": "1250.000001",
        "result": "NOT_EVALUATED",
        "tolerance": null,
        "delta": null,
        "delta_raw": null
      },
      "network": {
        "expected": null,
        "actual": "tron",
        "result": "NOT_EVALUATED"
      }
    }
  },
  "risk": {
    "status": "NO_MATERIAL_SIGNAL",
    "driver": null,
    "driver_evidence_ids": []
  },
  "evidence_items": [
    {
      "evidence_id": "ev_431b2fd74ad43ef5",
      "kind": "transaction_facts",
      "source": "chain_explorer",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "block:80000000",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-10T09:00:00Z",
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_63c8c4e421f6ff66",
      "kind": "transaction_facts",
      "source": "chain_node",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "block:80000000",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-10T09:00:00Z",
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_896f4ce46de5246c",
      "kind": "aml_risk",
      "source": "aml_provider",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "transaction_risk_score",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "CHECK_TIME",
      "coverage": "checked_freshness_unknown",
      "freshness": {
        "age_seconds": 0,
        "stale": true,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_7d0537ae6c2054d5",
      "kind": "sanctions_list",
      "source": "ofac",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 172800
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_2be872923c0687c9",
      "kind": "sanctions_list",
      "source": "eu",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-09-22T18:01:34Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_df2204c780968cb5",
      "kind": "sanctions_list",
      "source": "uk",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-02T00:00:00Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_76c33d7fb3a48fb7",
      "kind": "sanctions_list",
      "source": "un",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_applicable",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "not_applicable",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_81b44db3a9445d39",
      "kind": "token_authenticity",
      "source": "token_registry",
      "subject": {
        "type": "token",
        "ref": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
      },
      "source_reference": "official",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "CHECK_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    }
  ],
  "coverage_limitations": {
    "runtime": [],
    "product": [
      "AML_TRANSACTION_LEVEL_ONLY",
      "RISK_STATE_AS_OF_CHECK",
      "SANCTIONS_CONFIGURED_LISTS_ONLY"
    ]
  },
  "obligation": null,
  "reuse": null
}

Failed transaction

View full JSON response
JSON
{
  "request_id": "example-failed",
  "tx_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "network": "tron",
  "transaction_type": "TriggerSmartContract",
  "transaction_category": "trc20_transfer",
  "transaction_state": "failed",
  "confirmations": 27,
  "required_confirmations": 19,
  "sender": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
  "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
  "token_contract": null,
  "token_symbol": null,
  "decimals": null,
  "amount": null,
  "transfers": [],
  "transfers_truncated": false,
  "aml_risk_level": "unknown",
  "schema_version": "0.3",
  "policy_version": "b2b-tx-2026-10.4",
  "sanctions_hit": null,
  "direct_sanctions_match": null,
  "indirect_sanctions_exposure": null,
  "risk_exposure": null,
  "key_exposure": [],
  "token_authenticity": "official",
  "recipient_match": null,
  "token_match": null,
  "amount_match": null,
  "coverage": "complete",
  "workflow_coverage": "complete",
  "source_coverage": {
    "sanctions_sources": {
      "ofac": {
        "status": "not_checked",
        "checked_at": null,
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": 172800,
        "scope": "configured_address_index",
        "error_code": null
      },
      "eu": {
        "status": "not_checked",
        "checked_at": null,
        "retrieved_at": "2026-10-02T23:49:24Z",
        "data_updated_at": "2026-09-22T18:01:34Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "uk": {
        "status": "not_checked",
        "checked_at": null,
        "retrieved_at": "2026-10-02T23:47:32Z",
        "data_updated_at": "2026-10-02T00:00:00Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "un": {
        "status": "not_applicable",
        "checked_at": null,
        "retrieved_at": null,
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "configured_address_index",
        "error_code": null
      }
    },
    "transaction_sources": {
      "chain_explorer": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      },
      "chain_node": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      }
    },
    "risk_sources": {
      "aml_provider": {
        "status": "not_applicable",
        "checked_at": null,
        "retrieved_at": null,
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_risk_score",
        "error_code": null
      }
    },
    "universal_clearance": false
  },
  "freshness": {
    "checked_at": "2026-10-10T09:00:00Z",
    "transaction_at": "2026-10-10T09:00:00Z",
    "risk_data_at": null,
    "token_data_at": "2026-10-10T09:00:00Z",
    "max_source_age_seconds": 0
  },
  "overall_state": "REVIEW",
  "environment": "live",
  "reasons": [
    {
      "code": "TX_REVERTED",
      "category": "transaction",
      "severity": "warning"
    },
    {
      "code": "TX_FACTS_VERIFIED",
      "category": "transaction",
      "severity": "info"
    }
  ],
  "decision_id": null,
  "direction": null,
  "workflow_type": null,
  "versions": {
    "decision": "b2b-tx-2026-10.4",
    "rules": {
      "exposure": "b2b-exp-v2",
      "sanctions_freshness": "sf-v1",
      "billing": "final_answers_only",
      "integrity": "int-v3",
      "reuse": "reuse-v1",
      "allocation": "alloc-v1"
    },
    "reason_codes": "rc-1",
    "evidence_schema": "ev-1",
    "contract": "0.3"
  },
  "transaction_status": "FAILED",
  "integrity_status": "NOT_EVALUATED",
  "risk_status": "NOT_EVALUATED",
  "coverage_status": "SKIPPED",
  "status_drivers": {
    "transaction": "TX_REVERTED",
    "integrity": "TX_REVERTED",
    "risk": "TX_REVERTED",
    "coverage": null,
    "action": "TX_REVERTED"
  },
  "result_final": true,
  "verification_final": true,
  "risk_final": true,
  "risk_retryable": false,
  "risk_retry_after_seconds": null,
  "retryable": false,
  "retry_after_seconds": null,
  "recommended_action": "NOT_EXECUTED",
  "integrity": {
    "status": "NOT_EVALUATED",
    "selected_event_ref": null,
    "relevant_event_count": 0,
    "comparisons": {
      "recipient": null,
      "sender": null,
      "token": null,
      "amount": null,
      "network": {
        "expected": null,
        "actual": "tron",
        "result": "NOT_EVALUATED"
      }
    }
  },
  "risk": {
    "status": "NOT_EVALUATED",
    "driver": "TX_REVERTED",
    "driver_evidence_ids": []
  },
  "evidence_items": [
    {
      "evidence_id": "ev_431b2fd74ad43ef5",
      "kind": "transaction_facts",
      "source": "chain_explorer",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "block:80000000",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-10T09:00:00Z",
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_63c8c4e421f6ff66",
      "kind": "transaction_facts",
      "source": "chain_node",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "block:80000000",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-10T09:00:00Z",
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_73e84774c4ced93d",
      "kind": "aml_risk",
      "source": "aml_provider",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "transaction_risk_score",
      "observed_at": null,
      "effective_at": null,
      "temporal_scope": "CHECK_TIME",
      "coverage": "not_applicable",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "not_applicable",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_6b44582b97147dc6",
      "kind": "sanctions_list",
      "source": "ofac",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_checked",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": 172800
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_06483bdcf9ba67c5",
      "kind": "sanctions_list",
      "source": "eu",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": "2026-09-22T18:01:34Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_checked",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_9ea21340c566a061",
      "kind": "sanctions_list",
      "source": "uk",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": "2026-10-02T00:00:00Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_checked",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_76c33d7fb3a48fb7",
      "kind": "sanctions_list",
      "source": "un",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_applicable",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "not_applicable",
      "disagreement": null,
      "evidence_version": "ev-1"
    }
  ],
  "coverage_limitations": {
    "runtime": [],
    "product": [
      "AML_TRANSACTION_LEVEL_ONLY",
      "RISK_STATE_AS_OF_CHECK",
      "SANCTIONS_CONFIGURED_LISTS_ONLY"
    ]
  },
  "obligation": null,
  "reuse": null
}

Transaction not found

View full JSON response
JSON
{
  "request_id": "example-not-found",
  "tx_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "network": "tron",
  "transaction_type": "TriggerSmartContract",
  "transaction_category": "trc20_transfer",
  "transaction_state": "not_found",
  "confirmations": null,
  "required_confirmations": 19,
  "sender": null,
  "recipient": null,
  "token_contract": null,
  "token_symbol": null,
  "decimals": null,
  "amount": null,
  "transfers": [],
  "transfers_truncated": false,
  "aml_risk_level": "unknown",
  "schema_version": "0.3",
  "policy_version": "b2b-tx-2026-10.4",
  "sanctions_hit": null,
  "direct_sanctions_match": null,
  "indirect_sanctions_exposure": null,
  "risk_exposure": null,
  "key_exposure": [],
  "token_authenticity": "official",
  "recipient_match": null,
  "token_match": null,
  "amount_match": null,
  "coverage": "partial",
  "workflow_coverage": "partial",
  "source_coverage": {
    "sanctions_sources": {
      "ofac": {
        "status": "not_checked",
        "checked_at": null,
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": 172800,
        "scope": "configured_address_index",
        "error_code": null
      },
      "eu": {
        "status": "not_checked",
        "checked_at": null,
        "retrieved_at": "2026-10-02T23:49:24Z",
        "data_updated_at": "2026-09-22T18:01:34Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "uk": {
        "status": "not_checked",
        "checked_at": null,
        "retrieved_at": "2026-10-02T23:47:32Z",
        "data_updated_at": "2026-10-02T00:00:00Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "un": {
        "status": "not_applicable",
        "checked_at": null,
        "retrieved_at": null,
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "configured_address_index",
        "error_code": null
      }
    },
    "transaction_sources": {
      "chain_explorer": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      },
      "chain_node": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      }
    },
    "risk_sources": {
      "aml_provider": {
        "status": "not_applicable",
        "checked_at": null,
        "retrieved_at": null,
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_risk_score",
        "error_code": null
      }
    },
    "universal_clearance": false
  },
  "freshness": {
    "checked_at": "2026-10-10T09:00:00Z",
    "transaction_at": null,
    "risk_data_at": null,
    "token_data_at": "2026-10-10T09:00:00Z",
    "max_source_age_seconds": 0
  },
  "overall_state": "LIMITED_DATA",
  "environment": "live",
  "reasons": [
    {
      "code": "TX_NOT_FOUND",
      "category": "transaction",
      "severity": "warning"
    },
    {
      "code": "EXPECTED_DATA_UNAVAILABLE",
      "category": "expected",
      "severity": "warning"
    },
    {
      "code": "TX_FACTS_VERIFIED",
      "category": "transaction",
      "severity": "info"
    }
  ],
  "decision_id": null,
  "direction": null,
  "workflow_type": null,
  "versions": {
    "decision": "b2b-tx-2026-10.4",
    "rules": {
      "exposure": "b2b-exp-v2",
      "sanctions_freshness": "sf-v1",
      "billing": "final_answers_only",
      "integrity": "int-v3",
      "reuse": "reuse-v1",
      "allocation": "alloc-v1"
    },
    "reason_codes": "rc-1",
    "evidence_schema": "ev-1",
    "contract": "0.3"
  },
  "transaction_status": "NOT_FOUND",
  "integrity_status": "NOT_EVALUATED",
  "risk_status": "NOT_EVALUATED",
  "coverage_status": "SKIPPED",
  "status_drivers": {
    "transaction": "TX_NOT_FOUND",
    "integrity": "TX_NOT_FOUND",
    "risk": "TX_NOT_FOUND",
    "coverage": null,
    "action": "TX_NOT_FOUND"
  },
  "result_final": false,
  "verification_final": false,
  "risk_final": false,
  "risk_retryable": true,
  "risk_retry_after_seconds": 15,
  "retryable": true,
  "retry_after_seconds": 15,
  "recommended_action": "RECHECK_LATER",
  "integrity": {
    "status": "NOT_EVALUATED",
    "selected_event_ref": null,
    "relevant_event_count": 0,
    "comparisons": {
      "recipient": null,
      "sender": null,
      "token": null,
      "amount": null,
      "network": {
        "expected": null,
        "actual": "tron",
        "result": "NOT_EVALUATED"
      }
    }
  },
  "risk": {
    "status": "NOT_EVALUATED",
    "driver": "TX_NOT_FOUND",
    "driver_evidence_ids": []
  },
  "evidence_items": [
    {
      "evidence_id": "ev_431b2fd74ad43ef5",
      "kind": "transaction_facts",
      "source": "chain_explorer",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": null,
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_63c8c4e421f6ff66",
      "kind": "transaction_facts",
      "source": "chain_node",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": null,
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_73e84774c4ced93d",
      "kind": "aml_risk",
      "source": "aml_provider",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "transaction_risk_score",
      "observed_at": null,
      "effective_at": null,
      "temporal_scope": "CHECK_TIME",
      "coverage": "not_applicable",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "not_applicable",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_6b44582b97147dc6",
      "kind": "sanctions_list",
      "source": "ofac",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_checked",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": 172800
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_06483bdcf9ba67c5",
      "kind": "sanctions_list",
      "source": "eu",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": "2026-09-22T18:01:34Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_checked",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_9ea21340c566a061",
      "kind": "sanctions_list",
      "source": "uk",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": "2026-10-02T00:00:00Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_checked",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_76c33d7fb3a48fb7",
      "kind": "sanctions_list",
      "source": "un",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_applicable",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "not_applicable",
      "disagreement": null,
      "evidence_version": "ev-1"
    }
  ],
  "coverage_limitations": {
    "runtime": [],
    "product": [
      "AML_TRANSACTION_LEVEL_ONLY",
      "RISK_STATE_AS_OF_CHECK",
      "SANCTIONS_CONFIGURED_LISTS_ONLY"
    ]
  },
  "obligation": null,
  "reuse": null
}

Limited data

View full JSON response
JSON
{
  "request_id": "example-limited-data",
  "tx_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "network": "tron",
  "transaction_type": "TriggerSmartContract",
  "transaction_category": "trc20_transfer",
  "transaction_state": "confirmed",
  "confirmations": 27,
  "required_confirmations": 19,
  "sender": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
  "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
  "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
  "token_symbol": "USDT",
  "decimals": 6,
  "amount": "1250.000001",
  "transfers": [
    {
      "index": 0,
      "asset_type": "trc20",
      "sender": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
      "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
      "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
      "token_id": null,
      "token_symbol": "USDT",
      "decimals": 6,
      "amount": "1250.000001",
      "event_ref": "log:0",
      "execution_status": "success"
    }
  ],
  "transfers_truncated": false,
  "aml_risk_level": "unknown",
  "schema_version": "0.3",
  "policy_version": "b2b-tx-2026-10.4",
  "sanctions_hit": false,
  "direct_sanctions_match": false,
  "indirect_sanctions_exposure": false,
  "risk_exposure": false,
  "key_exposure": [],
  "token_authenticity": "official",
  "recipient_match": true,
  "token_match": true,
  "amount_match": true,
  "coverage": "partial",
  "workflow_coverage": "partial",
  "source_coverage": {
    "sanctions_sources": {
      "ofac": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": 172800,
        "scope": "configured_address_index",
        "error_code": null
      },
      "eu": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-02T23:49:24Z",
        "data_updated_at": "2026-09-22T18:01:34Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "uk": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-02T23:47:32Z",
        "data_updated_at": "2026-10-02T00:00:00Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "un": {
        "status": "not_applicable",
        "checked_at": null,
        "retrieved_at": null,
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "configured_address_index",
        "error_code": null
      }
    },
    "transaction_sources": {
      "chain_explorer": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      },
      "chain_node": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      }
    },
    "risk_sources": {
      "aml_provider": {
        "status": "unavailable",
        "checked_at": null,
        "retrieved_at": null,
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_risk_score",
        "error_code": "timeout"
      }
    },
    "universal_clearance": false
  },
  "freshness": {
    "checked_at": "2026-10-10T09:00:00Z",
    "transaction_at": "2026-10-10T09:00:00Z",
    "risk_data_at": null,
    "token_data_at": "2026-10-10T09:00:00Z",
    "max_source_age_seconds": 0
  },
  "overall_state": "LIMITED_DATA",
  "environment": "live",
  "reasons": [
    {
      "code": "AML_UNAVAILABLE",
      "category": "risk",
      "severity": "warning"
    },
    {
      "code": "TX_FACTS_VERIFIED",
      "category": "transaction",
      "severity": "info"
    },
    {
      "code": "TX_FINALIZED",
      "category": "transaction",
      "severity": "info"
    },
    {
      "code": "NO_DIRECT_SANCTIONS_MATCH",
      "category": "sanctions",
      "severity": "info"
    },
    {
      "code": "TOKEN_OFFICIAL",
      "category": "token",
      "severity": "info"
    }
  ],
  "decision_id": null,
  "direction": null,
  "workflow_type": null,
  "versions": {
    "decision": "b2b-tx-2026-10.4",
    "rules": {
      "exposure": "b2b-exp-v2",
      "sanctions_freshness": "sf-v1",
      "billing": "final_answers_only",
      "integrity": "int-v3",
      "reuse": "reuse-v1",
      "allocation": "alloc-v1"
    },
    "reason_codes": "rc-1",
    "evidence_schema": "ev-1",
    "contract": "0.3"
  },
  "transaction_status": "CONFIRMED",
  "integrity_status": "MATCH",
  "risk_status": "INSUFFICIENT",
  "coverage_status": "UNAVAILABLE",
  "status_drivers": {
    "transaction": "TX_FINALIZED",
    "integrity": null,
    "risk": "AML_UNAVAILABLE",
    "coverage": "AML_UNAVAILABLE",
    "action": "AML_UNAVAILABLE"
  },
  "result_final": true,
  "verification_final": true,
  "risk_final": false,
  "risk_retryable": true,
  "risk_retry_after_seconds": 30,
  "retryable": false,
  "retry_after_seconds": null,
  "recommended_action": "MANUAL_REVIEW",
  "integrity": {
    "status": "MATCH",
    "selected_event_ref": "log:0",
    "relevant_event_count": 1,
    "comparisons": {
      "recipient": {
        "expected": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
        "actual": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
        "result": "MATCH"
      },
      "sender": {
        "expected": null,
        "actual": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
        "result": "NOT_EVALUATED"
      },
      "token": {
        "expected": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
        "actual": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
        "result": "MATCH"
      },
      "amount": {
        "expected": "1250.000001",
        "actual": "1250.000001",
        "result": "EXACT",
        "tolerance": null,
        "delta": "0",
        "delta_raw": "0"
      },
      "network": {
        "expected": null,
        "actual": "tron",
        "result": "NOT_EVALUATED"
      }
    }
  },
  "risk": {
    "status": "INSUFFICIENT",
    "driver": "AML_UNAVAILABLE",
    "driver_evidence_ids": []
  },
  "evidence_items": [
    {
      "evidence_id": "ev_431b2fd74ad43ef5",
      "kind": "transaction_facts",
      "source": "chain_explorer",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "block:80000000",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-10T09:00:00Z",
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_63c8c4e421f6ff66",
      "kind": "transaction_facts",
      "source": "chain_node",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "block:80000000",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-10T09:00:00Z",
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_73e84774c4ced93d",
      "kind": "aml_risk",
      "source": "aml_provider",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "transaction_risk_score",
      "observed_at": null,
      "effective_at": null,
      "temporal_scope": "CHECK_TIME",
      "coverage": "unavailable",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_7d0537ae6c2054d5",
      "kind": "sanctions_list",
      "source": "ofac",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 172800
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_2be872923c0687c9",
      "kind": "sanctions_list",
      "source": "eu",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-09-22T18:01:34Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_df2204c780968cb5",
      "kind": "sanctions_list",
      "source": "uk",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-02T00:00:00Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_76c33d7fb3a48fb7",
      "kind": "sanctions_list",
      "source": "un",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_applicable",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "not_applicable",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_81b44db3a9445d39",
      "kind": "token_authenticity",
      "source": "token_registry",
      "subject": {
        "type": "token",
        "ref": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
      },
      "source_reference": "official",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "CHECK_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    }
  ],
  "coverage_limitations": {
    "runtime": [
      "AML_UNAVAILABLE"
    ],
    "product": [
      "AML_TRANSACTION_LEVEL_ONLY",
      "RISK_STATE_AS_OF_CHECK",
      "SANCTIONS_CONFIGURED_LISTS_ONLY"
    ]
  },
  "obligation": null,
  "reuse": null
}

Sanctions match

View full JSON response
JSON
{
  "request_id": "example-sanctions-hit",
  "tx_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "network": "tron",
  "transaction_type": "TriggerSmartContract",
  "transaction_category": "trc20_transfer",
  "transaction_state": "confirmed",
  "confirmations": 27,
  "required_confirmations": 19,
  "sender": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
  "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
  "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
  "token_symbol": "USDT",
  "decimals": 6,
  "amount": "1250.000001",
  "transfers": [
    {
      "index": 0,
      "asset_type": "trc20",
      "sender": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
      "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
      "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
      "token_id": null,
      "token_symbol": "USDT",
      "decimals": 6,
      "amount": "1250.000001",
      "event_ref": "log:0",
      "execution_status": "success"
    }
  ],
  "transfers_truncated": false,
  "aml_risk_level": "low",
  "schema_version": "0.3",
  "policy_version": "b2b-tx-2026-10.4",
  "sanctions_hit": true,
  "direct_sanctions_match": true,
  "indirect_sanctions_exposure": false,
  "risk_exposure": false,
  "key_exposure": [],
  "token_authenticity": "official",
  "recipient_match": true,
  "token_match": true,
  "amount_match": true,
  "coverage": "complete",
  "workflow_coverage": "complete",
  "source_coverage": {
    "sanctions_sources": {
      "ofac": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": 172800,
        "scope": "configured_address_index",
        "error_code": null
      },
      "eu": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-02T23:49:24Z",
        "data_updated_at": "2026-09-22T18:01:34Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "uk": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-02T23:47:32Z",
        "data_updated_at": "2026-10-02T00:00:00Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "un": {
        "status": "not_applicable",
        "checked_at": null,
        "retrieved_at": null,
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "configured_address_index",
        "error_code": null
      }
    },
    "transaction_sources": {
      "chain_explorer": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      },
      "chain_node": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      }
    },
    "risk_sources": {
      "aml_provider": {
        "status": "checked_freshness_unknown",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_risk_score",
        "error_code": null
      }
    },
    "universal_clearance": false
  },
  "freshness": {
    "checked_at": "2026-10-10T09:00:00Z",
    "transaction_at": "2026-10-10T09:00:00Z",
    "risk_data_at": "2026-10-10T09:00:00Z",
    "token_data_at": "2026-10-10T09:00:00Z",
    "max_source_age_seconds": 0
  },
  "overall_state": "HIGH_RISK",
  "environment": "live",
  "reasons": [
    {
      "code": "DIRECT_SANCTIONS_MATCH",
      "category": "sanctions",
      "severity": "critical"
    },
    {
      "code": "TX_FACTS_VERIFIED",
      "category": "transaction",
      "severity": "info"
    },
    {
      "code": "TX_FINALIZED",
      "category": "transaction",
      "severity": "info"
    },
    {
      "code": "AML_LOW",
      "category": "risk",
      "severity": "info"
    },
    {
      "code": "TOKEN_OFFICIAL",
      "category": "token",
      "severity": "info"
    }
  ],
  "decision_id": null,
  "direction": null,
  "workflow_type": null,
  "versions": {
    "decision": "b2b-tx-2026-10.4",
    "rules": {
      "exposure": "b2b-exp-v2",
      "sanctions_freshness": "sf-v1",
      "billing": "final_answers_only",
      "integrity": "int-v3",
      "reuse": "reuse-v1",
      "allocation": "alloc-v1"
    },
    "reason_codes": "rc-1",
    "evidence_schema": "ev-1",
    "contract": "0.3"
  },
  "transaction_status": "CONFIRMED",
  "integrity_status": "MATCH",
  "risk_status": "HIGH_RISK",
  "coverage_status": "OK",
  "status_drivers": {
    "transaction": "TX_FINALIZED",
    "integrity": null,
    "risk": "DIRECT_SANCTIONS_MATCH",
    "coverage": null,
    "action": "DIRECT_SANCTIONS_MATCH"
  },
  "result_final": true,
  "verification_final": true,
  "risk_final": true,
  "risk_retryable": false,
  "risk_retry_after_seconds": null,
  "retryable": false,
  "retry_after_seconds": null,
  "recommended_action": "HOLD_AND_ESCALATE",
  "integrity": {
    "status": "MATCH",
    "selected_event_ref": "log:0",
    "relevant_event_count": 1,
    "comparisons": {
      "recipient": {
        "expected": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
        "actual": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
        "result": "MATCH"
      },
      "sender": {
        "expected": null,
        "actual": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
        "result": "NOT_EVALUATED"
      },
      "token": {
        "expected": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
        "actual": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
        "result": "MATCH"
      },
      "amount": {
        "expected": "1250.000001",
        "actual": "1250.000001",
        "result": "EXACT",
        "tolerance": null,
        "delta": "0",
        "delta_raw": "0"
      },
      "network": {
        "expected": null,
        "actual": "tron",
        "result": "NOT_EVALUATED"
      }
    }
  },
  "risk": {
    "status": "HIGH_RISK",
    "driver": "DIRECT_SANCTIONS_MATCH",
    "driver_evidence_ids": []
  },
  "evidence_items": [
    {
      "evidence_id": "ev_431b2fd74ad43ef5",
      "kind": "transaction_facts",
      "source": "chain_explorer",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "block:80000000",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-10T09:00:00Z",
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_63c8c4e421f6ff66",
      "kind": "transaction_facts",
      "source": "chain_node",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "block:80000000",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-10T09:00:00Z",
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_896f4ce46de5246c",
      "kind": "aml_risk",
      "source": "aml_provider",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "transaction_risk_score",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "CHECK_TIME",
      "coverage": "checked_freshness_unknown",
      "freshness": {
        "age_seconds": 0,
        "stale": true,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_7d0537ae6c2054d5",
      "kind": "sanctions_list",
      "source": "ofac",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 172800
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_2be872923c0687c9",
      "kind": "sanctions_list",
      "source": "eu",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-09-22T18:01:34Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_df2204c780968cb5",
      "kind": "sanctions_list",
      "source": "uk",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-02T00:00:00Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_76c33d7fb3a48fb7",
      "kind": "sanctions_list",
      "source": "un",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_applicable",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "not_applicable",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_81b44db3a9445d39",
      "kind": "token_authenticity",
      "source": "token_registry",
      "subject": {
        "type": "token",
        "ref": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
      },
      "source_reference": "official",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "CHECK_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    }
  ],
  "coverage_limitations": {
    "runtime": [],
    "product": [
      "AML_TRANSACTION_LEVEL_ONLY",
      "RISK_STATE_AS_OF_CHECK",
      "SANCTIONS_CONFIGURED_LISTS_ONLY"
    ]
  },
  "obligation": null,
  "reuse": null
}

Payment details do not match

View full JSON response
JSON
{
  "request_id": "example-expected-mismatch",
  "tx_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "network": "tron",
  "transaction_type": "TriggerSmartContract",
  "transaction_category": "trc20_transfer",
  "transaction_state": "confirmed",
  "confirmations": 27,
  "required_confirmations": 19,
  "sender": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
  "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
  "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
  "token_symbol": "USDT",
  "decimals": 6,
  "amount": "1250.000001",
  "transfers": [
    {
      "index": 0,
      "asset_type": "trc20",
      "sender": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
      "recipient": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
      "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
      "token_id": null,
      "token_symbol": "USDT",
      "decimals": 6,
      "amount": "1250.000001",
      "event_ref": "log:0",
      "execution_status": "success"
    }
  ],
  "transfers_truncated": false,
  "aml_risk_level": "low",
  "schema_version": "0.3",
  "policy_version": "b2b-tx-2026-10.4",
  "sanctions_hit": false,
  "direct_sanctions_match": false,
  "indirect_sanctions_exposure": false,
  "risk_exposure": false,
  "key_exposure": [],
  "token_authenticity": "official",
  "recipient_match": true,
  "token_match": true,
  "amount_match": false,
  "coverage": "complete",
  "workflow_coverage": "complete",
  "source_coverage": {
    "sanctions_sources": {
      "ofac": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": 172800,
        "scope": "configured_address_index",
        "error_code": null
      },
      "eu": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-02T23:49:24Z",
        "data_updated_at": "2026-09-22T18:01:34Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "uk": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-02T23:47:32Z",
        "data_updated_at": "2026-10-02T00:00:00Z",
        "stale_after_seconds": 3024000,
        "scope": "configured_address_index",
        "error_code": null
      },
      "un": {
        "status": "not_applicable",
        "checked_at": null,
        "retrieved_at": null,
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "configured_address_index",
        "error_code": null
      }
    },
    "transaction_sources": {
      "chain_explorer": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      },
      "chain_node": {
        "status": "checked",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_facts",
        "error_code": null
      }
    },
    "risk_sources": {
      "aml_provider": {
        "status": "checked_freshness_unknown",
        "checked_at": "2026-10-10T09:00:00Z",
        "retrieved_at": "2026-10-10T09:00:00Z",
        "data_updated_at": null,
        "stale_after_seconds": null,
        "scope": "transaction_risk_score",
        "error_code": null
      }
    },
    "universal_clearance": false
  },
  "freshness": {
    "checked_at": "2026-10-10T09:00:00Z",
    "transaction_at": "2026-10-10T09:00:00Z",
    "risk_data_at": "2026-10-10T09:00:00Z",
    "token_data_at": "2026-10-10T09:00:00Z",
    "max_source_age_seconds": 0
  },
  "overall_state": "REVIEW",
  "environment": "live",
  "reasons": [
    {
      "code": "EXPECTED_AMOUNT_MISMATCH",
      "category": "expected",
      "severity": "warning"
    },
    {
      "code": "TX_FACTS_VERIFIED",
      "category": "transaction",
      "severity": "info"
    },
    {
      "code": "TX_FINALIZED",
      "category": "transaction",
      "severity": "info"
    },
    {
      "code": "AML_LOW",
      "category": "risk",
      "severity": "info"
    },
    {
      "code": "NO_DIRECT_SANCTIONS_MATCH",
      "category": "sanctions",
      "severity": "info"
    }
  ],
  "decision_id": null,
  "direction": null,
  "workflow_type": null,
  "versions": {
    "decision": "b2b-tx-2026-10.4",
    "rules": {
      "exposure": "b2b-exp-v2",
      "sanctions_freshness": "sf-v1",
      "billing": "final_answers_only",
      "integrity": "int-v3",
      "reuse": "reuse-v1",
      "allocation": "alloc-v1"
    },
    "reason_codes": "rc-1",
    "evidence_schema": "ev-1",
    "contract": "0.3"
  },
  "transaction_status": "CONFIRMED",
  "integrity_status": "MISMATCH",
  "risk_status": "NO_MATERIAL_SIGNAL",
  "coverage_status": "OK",
  "status_drivers": {
    "transaction": "TX_FINALIZED",
    "integrity": "EXPECTED_AMOUNT_MISMATCH",
    "risk": null,
    "coverage": null,
    "action": "EXPECTED_AMOUNT_MISMATCH"
  },
  "result_final": true,
  "verification_final": true,
  "risk_final": true,
  "risk_retryable": false,
  "risk_retry_after_seconds": null,
  "retryable": false,
  "retry_after_seconds": null,
  "recommended_action": "MANUAL_REVIEW",
  "integrity": {
    "status": "MISMATCH",
    "selected_event_ref": "log:0",
    "relevant_event_count": 1,
    "comparisons": {
      "recipient": {
        "expected": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
        "actual": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
        "result": "MATCH"
      },
      "sender": {
        "expected": null,
        "actual": "TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3",
        "result": "NOT_EVALUATED"
      },
      "token": {
        "expected": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
        "actual": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
        "result": "MATCH"
      },
      "amount": {
        "expected": "1251.000001",
        "actual": "1250.000001",
        "result": "UNDERPAID",
        "tolerance": null,
        "delta": "-1",
        "delta_raw": "-1000000"
      },
      "network": {
        "expected": null,
        "actual": "tron",
        "result": "NOT_EVALUATED"
      }
    }
  },
  "risk": {
    "status": "NO_MATERIAL_SIGNAL",
    "driver": null,
    "driver_evidence_ids": []
  },
  "evidence_items": [
    {
      "evidence_id": "ev_431b2fd74ad43ef5",
      "kind": "transaction_facts",
      "source": "chain_explorer",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "block:80000000",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-10T09:00:00Z",
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_63c8c4e421f6ff66",
      "kind": "transaction_facts",
      "source": "chain_node",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "block:80000000",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-10T09:00:00Z",
      "temporal_scope": "TRANSACTION_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_896f4ce46de5246c",
      "kind": "aml_risk",
      "source": "aml_provider",
      "subject": {
        "type": "transaction",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "transaction_risk_score",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "CHECK_TIME",
      "coverage": "checked_freshness_unknown",
      "freshness": {
        "age_seconds": 0,
        "stale": true,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_7d0537ae6c2054d5",
      "kind": "sanctions_list",
      "source": "ofac",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 172800
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_2be872923c0687c9",
      "kind": "sanctions_list",
      "source": "eu",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-09-22T18:01:34Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_df2204c780968cb5",
      "kind": "sanctions_list",
      "source": "uk",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": "2026-10-02T00:00:00Z",
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": 3024000
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_76c33d7fb3a48fb7",
      "kind": "sanctions_list",
      "source": "un",
      "subject": {
        "type": "transaction_addresses",
        "ref": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "source_reference": "configured_address_index",
      "observed_at": null,
      "effective_at": null,
      "temporal_scope": "LIST_PUBLICATION",
      "coverage": "not_applicable",
      "freshness": {
        "age_seconds": null,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "not_applicable",
      "disagreement": null,
      "evidence_version": "ev-1"
    },
    {
      "evidence_id": "ev_81b44db3a9445d39",
      "kind": "token_authenticity",
      "source": "token_registry",
      "subject": {
        "type": "token",
        "ref": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
      },
      "source_reference": "official",
      "observed_at": "2026-10-10T09:00:00Z",
      "effective_at": null,
      "temporal_scope": "CHECK_TIME",
      "coverage": "checked",
      "freshness": {
        "age_seconds": 0,
        "stale": false,
        "stale_after_seconds": null
      },
      "dependency": "required",
      "disagreement": null,
      "evidence_version": "ev-1"
    }
  ],
  "coverage_limitations": {
    "runtime": [],
    "product": [
      "AML_TRANSACTION_LEVEL_ONLY",
      "RISK_STATE_AS_OF_CHECK",
      "SANCTIONS_CONFIGURED_LISTS_ONLY"
    ]
  },
  "obligation": null,
  "reuse": null
}

Zero-Friction v0.1

POST /v1/check adds optional network context and a short response. A request with an explicit network never switches chains. NETWORK_REQUIRED and NETWORK_AMBIGUOUS require a corrected body and a new idempotency key; a replay keeps the original network and result. Short cURL, Node.js, Python and PHP examples, address normalization and the asset matrix are in the linked guide.

Wallet check

POST /v1/wallet/check uses the same retry loop with a 35-second client deadline and a wallet body; the guide carries the cURL, Python and TypeScript snippets.

Transaction states

transaction_state describes what the chain reports about the transaction. It does not replace the risk assessment or the coverage check.

transaction_state is the 0.2 field, in lowercase, and keeps unknown for facts that cannot establish a state. Contract 0.3 answers the same question in transaction_status (CONFIRMED, PENDING, FAILED, NOT_FOUND, INSUFFICIENT, where INSUFFICIENT covers unknown, sources that disagree on the transaction itself (the hash they returned, whether it exists, its block, its execution status or its finality) and an absence not every source confirmed; a disagreement only about its content (transfers, sender, recipient or transaction type) keeps transaction_status and shows as coverage_status: DISAGREEMENT with verification_final: false), which also applies the finality threshold: a confirmed transaction below it is PENDING. Prefer transaction_status; see status axes.

State Meaning Integration behavior
confirmed Chain facts report confirmation. Also require sufficient confirmations and examine the overall state and coverage.
pending The transaction is visible but finality is incomplete. Do not record final settlement. The answer is not charged. Schedule a separately identified later observation with a new idempotency key, with backoff: for TRON, finality takes about a minute after broadcast. Free answers count toward the daily ceiling of unbilled checks (see limits).
failed Execution failed or reverted. Do not treat the expected transfer as received.
not_found The queried transaction was not found in the available evidence. Check the network and hash; absence is not proof of a completed payment.
unknown The available transaction facts cannot establish a state. Treat as incomplete evidence.

TRON confirmations

TRON v1 uses a 19-confirmation finality policy together with finalized chain evidence. Compare confirmations with required_confirmations. A transaction state alone does not establish risk coverage. Response examples illustrate these states.

Ethereum and TON finality

Ethereum requires a canonical block at or below the finalized head from the configured sources; the latest head or any block before the finalized head is insufficient. TON requires provider-attested masterchain finality for every transaction and, separately, a complete message trace. The value 1 in required_confirmations for these adapters does not replace either finality policy. See network details.

Overall states

overall_state combines the transaction facts, the risk, sanctions and token checks and the expected payment comparison into one state.

State Meaning
CLEAR Applicable checks completed without an identified issue and finality requirements were met. This is not a guarantee against future risk.
REVIEW The result contains a condition requiring review, such as pending finality, failed execution, elevated risk or an expected-payment mismatch.
HIGH_RISK A high or critical finding exists, such as a sanctions match, high AML risk or a fake token.
LIMITED_DATA Evidence is incomplete, unsupported or contradictory. Never treat this as CLEAR.

Contract 0.3 also answers the four underlying questions separately, says whether the answer is final and recommends an action: see status axes and recommended action. overall_state keeps the rules below; ledger and client-context findings (another obligation already paid by this transaction, a duplicate execution, an already credited deposit) can only raise it to REVIEW.

On contract 0.3, decide on the axes, not on this state. overall_state is a one-word summary kept for 0.2 integrations, and missing data outranks a mismatch in it: a payment that is definitively wrong (verification_final: true, integrity_status: MISMATCH) still reads LIMITED_DATA when the risk screen could not run. Use verification_final and integrity_status for the payment, and risk_status and recommended_action for what to do with it.

Act on the state

The state is a check result, not a payment instruction: your policy decides. The design-partner pilot agrees this mapping:

Answer Recommended action
CLEAR, coverage: complete, every requested *_match is true Eligible for crediting under your own policy.
REVIEW Manual review. TX_PENDING_CONFIRMATIONS: wait and check again with a new key. EXPECTED_*_MISMATCH: the payment is not the one you expected; these codes never appear while transaction_status is PENDING.
HIGH_RISK Hold the funds and escalate to compliance.
LIMITED_DATA No automatic credit. With verification_final: true and integrity_status: MISMATCH the payment is definitively wrong: review it, a retry will not change it. Otherwise retry later with a new key; after a bounded number of attempts, review manually. While the sources disagree about the transaction itself, the answer is not final and can still carry EXPECTED_*_MISMATCH (see statuses).
429, 5xx, 409 idempotency_in_progress Retry with the same idempotency key after Retry-After.
404 transaction_not_found (/v1/check) Check the hash; look again later with a new key.
422 NETWORK_REQUIRED, 422 NETWORK_AMBIGUOUS Resend with network and a new key.
Other 4xx Integration error: fix the request, do not retry.

Read the state together with coverage

Coverage and state are independent. A known sanctions match, fake token or high AML finding takes priority as HIGH_RISK even when facts are incomplete or contradictory. Without such a finding, missing required evidence gives LIMITED_DATA. /v1/check reports the same states in lowercase in state and provides all reason objects in details.reasons.

Contract 0.3 answers four separate questions about a transaction instead of folding them into one state. Each answer has its own status and the reason code that set it. overall_state is still returned, computed by the 0.2 rules, so an integration built on it keeps working.

Four axes

Field Values Question
transaction_status CONFIRMED, PENDING, FAILED, NOT_FOUND, INSUFFICIENT Did the transaction execute, and is that final?
integrity_status MATCH, MISMATCH, AMBIGUOUS, NOT_EVALUATED, INSUFFICIENT Does it pay what you expected?
risk_status NO_MATERIAL_SIGNAL, REVIEW, HIGH_RISK, INSUFFICIENT, NOT_EVALUATED Is a material risk or sanctions signal attached to it?
coverage_status OK, DEGRADED, SKIPPED, UNAVAILABLE, DISAGREEMENT Did the sources this answer needs respond, and agree?

status_drivers names the reason code behind each axis and behind recommended_action (transaction, integrity, risk, coverage, action); null means nothing needed explaining. When recommended_action is MANUAL_REVIEW, status_drivers.action names one finding: TX_REVERTED on a FAILED transaction, otherwise the most severe finding. Among findings of equal severity, what the chain shows about the transaction itself comes first, then the risk screens (AML, sanctions, token), then the payment comparison and the remaining findings. So when a final payment mismatch is checked while the AML source is unavailable, status_drivers.action usually shows AML_UNAVAILABLE, not the mismatch: read status_drivers.integrity and reasons for the payment comparison, and each axis's own driver for its cause.

Transaction

  • CONFIRMED: executed and past the network's finality threshold.
  • PENDING: in the mempool or a block, but below the finality threshold. Never final.
  • FAILED: executed and reverted. When no transfer in it executed, nothing was paid; on TON a failed trace can still contain a leg that did execute, so read integrity_status and recommended_action.
  • NOT_FOUND: every source confirms that the transaction is absent. It may still appear.
  • INSUFFICIENT: the sources could not establish the facts, or disagree on whether the transaction exists, on its status or on its finality.

Integrity

  • MATCH: after chain finality, exactly one executed transfer pays the expected recipient the expected token and amount, within amount_tolerance when you send one. Legs inside one transaction that add up to the amount also match (SPLIT_WITHIN_TX).
  • MISMATCH: no executed transfer fits. integrity.comparisons shows which field differs and, for the amount, UNDERPAID or OVERPAID with the delta.
  • AMBIGUOUS: several executed transfers fit equally well; nothing is allocated.
  • NOT_EVALUATED: the transaction is pending, you sent nothing to compare, or the transaction failed and nothing in it executed (a reverted token call usually leaves no transfer rows at all).
  • INSUFFICIENT: you sent expectations but the transfer data needed to compare them is missing, the sources disagree about the transfers, the obligation you named has no instruction on record (INSTRUCTION_UNAVAILABLE), or the transaction failed while a transfer in it executed (a TON trace), so whether it paid is not established by the chain alone.

A movement that did not execute never matches: a reverted transfer to the right address is NOT_EVALUATED, and its recipient_match, token_match and amount_match are null.

Risk

HIGH_RISK beats REVIEW, which beats INSUFFICIENT. NO_MATERIAL_SIGNAL means the applicable screens ran and found nothing material; it is not a clearance. NOT_EVALUATED means risk does not apply (a failed or absent transaction). The risk block repeats the status with its driver and driver_evidence_ids, the evidence items the driver rests on. Every provider exposure row in key_exposure carries the grade the exposure policy gave it.

Coverage

DISAGREEMENT beats UNAVAILABLE (a required group of sources did not answer), which beats DEGRADED (one chain source only, a stale list, an asset without AML support), then SKIPPED (risk sources not called because the transaction failed or is absent) and OK.

Verification and risk finality

verification_final describes the payment facts and comparison. result_final is its alias; retryable is its opposite, and retry_after_seconds is the suggested wait for another verification observation. A final chain transaction with an exact expected match returns verification_final: true and integrity_status: MATCH even when KYT is unavailable and risk_status: INSUFFICIENT.

risk_final is true once checking again is not expected to fill a gap in the risk assessment: the transaction is final, its sources agree, and no AML, sanctions or token check had a temporary gap (an outage or a stale list). While the transaction is pending or not found, risk_final is false whatever risk_status says, and risk_retry_after_seconds equals retry_after_seconds: risk is assessed again once the transaction is final. risk_retryable is its opposite; risk_retry_after_seconds suggests when to check risk again. A permanent coverage limit can be final while remaining INSUFFICIENT: finality does not mean clearance. KYT describes risk at check time and can change later even after a final assessment.

Pending transactions return integrity_status: NOT_EVALUATED, verification_final: false, null match flags, NOT_EVALUATED for every requested comparison and no EXPECTED_*_MISMATCH reason: terms that differ are reported only once the transaction is final. When the sources disagree about the transaction itself (transaction_status: INSUFFICIENT, coverage_status: DISAGREEMENT), the answer is not final either, but it still compares the transfers the sources report and can carry EXPECTED_*_MISMATCH: act on verification_final: false and check again. Observed transfers remain evidence, not a final payment match. Verification retries wait 5–120 seconds for pending finality, 15 seconds for an absent transaction, or 30 seconds for a temporary chain-source gap. A temporary KYT gap suggests a separate risk retry after 30 seconds. When the AML source reports error_code spend_cap (your organization's daily cap on paid sources is reached, or spend cannot be accounted for the moment) or daily_cap (the AML source's service-wide daily ceiling is reached), a reached cap resets at 00:00 UTC, and a new check before then usually returns the same gap; see limits. On a transaction that is not found, risk_retryable is true although risk_status is NOT_EVALUATED: risk is assessed once the transaction appears.

Missing KYT does not reopen final verification and never permits PROCEED: MATCH with risk_status: INSUFFICIENT recommends MANUAL_REVIEW. Only final answers with complete coverage are charged; a final verification with partial KYT coverage remains free. For a new observation, use a new Idempotency-Key; replaying the same key returns the stored answer under its original policy.

recommended_action is what the answer supports, not an instruction. Your policy decides, and you record what you did on the decision.

Value When
HOLD_AND_ESCALATE risk_status: HIGH_RISK.
RECHECK_LATER Verification is not final: pending, absent, a temporary chain-source gap or disagreement. A check sent with direction: "OUTGOING" whose transaction is not found adds ATTEMPT_MAY_STILL_LAND.
NOT_EXECUTED The transaction failed and no transfer in it executed. A failed transaction in which some leg executed is MANUAL_REVIEW, never a go-ahead.
MANUAL_REVIEW A reviewable finding: risk_status: INSUFFICIENT, REVIEW risk, MISMATCH, AMBIGUOUS, INSUFFICIENT integrity, reuse by another obligation, a duplicate execution or an already credited deposit.
CONFIRM_INTERNAL_STATE The chain is in order, but ChainZap cannot see your side: no obligation was named, the payment is not known to be outgoing, nothing was compared, or customer_context does not say NOT_CREDITED. Check your own records before acting.
PROCEED A clean match against a recorded outgoing obligation with a clean ledger, or a payment you declare NOT_CREDITED.

An incoming payment is never PROCEED unless you send customer_context.credit_status: "NOT_CREDITED"; CREDITED raises the answer to REVIEW with ALREADY_CREDITED, and an allocation_status of UNKNOWN keeps it at CONFIRM_INTERNAL_STATE. When a check omits direction, it is taken from the obligation's recorded direction, then from workflow_type: PAYOUT and REFUND are outgoing, DEPOSIT and INVOICE incoming. Without customer_context.credit_status: "NOT_CREDITED", a payment whose direction is still unknown is never PROCEED.

Evidence and limitations

evidence_items lists what the answer rests on: chain sources, the AML provider, each sanctions list and the token check. Each item has evidence_id, kind, a neutral source, subject, source_reference (such as the block), observed_at (when ChainZap read it), effective_at (the time the fact refers to, such as the block time or the list date), coverage, freshness, dependency (required, or not_applicable when the source does not apply to this transaction), evidence_version and, when sources disagree, disagreement.fields. AML items carry temporal_scope: "CHECK_TIME": the provider reports the risk state as of the check, not as of the payment.

coverage_limitations separates what limited this answer (runtime) from what the product never covers for this network or asset (product, for example AML_TRANSACTION_LEVEL_ONLY or SANCTIONS_CONFIGURED_LISTS_ONLY). A product limitation does not make an answer retryable.

Versions

versions names everything that shaped the answer: decision (equal to policy_version), rules (exposure, sanctions freshness, billing, integrity, reuse and allocation rules), reason_codes, evidence_schema and contract. Store it with the decision if you need to explain the answer later.

Data completeness

Complete and partial

coverage: complete means the applicable evidence checks met their requirements for this response. It does not mean the transaction is free of risk; a complete check can still report HIGH_RISK. coverage: partial means one or more required sources, asset mappings, token checks or transaction facts are missing, unsupported or inconsistent.

workflow_coverage reports completion of this check; coverage is its compatibility alias. complete never means universal compliance clearance. source_coverage.sanctions_sources reports OFAC/EU/UK/UN separately: checked, checked_stale, checked_freshness_unknown, not_checked, not_applicable or unavailable. In source_coverage.risk_sources, checked_freshness_unknown is the normal status of a completed AML screen: the AML source does not date its data, so its evidence item also reads freshness.stale: true. This does not make the check partial.

Sanctions list freshness

Each sanctions list is judged by its own freshness window (policy sf-v1, part of policy_version). These are internal policies, not provider guarantees:

List Fresh while Basis
OFAC 48 hours The last successful sync with the mirror of the SDN list. retrieved_at reports it; the SDN publication date is not available, so data_updated_at is null.
EU, UK 35 days The last rebuild from the official publication, done monthly. data_updated_at is the publication date, retrieved_at the rebuild.
UN not_applicable The UN Security Council list publishes no crypto addresses.

A list that is stale, whose freshness cannot be established, or that is unavailable cannot back a clean answer: the check reports SANCTIONS_DATA_STALE or SANCTIONS_UNAVAILABLE, coverage: partial and LIMITED_DATA, and is not charged. A known high-risk finding still wins. stale_after_seconds on each source states its window.

direct_sanctions_match means an exact address match in the configured indexes. false means no match in those indexes, subject to their scope and freshness. indirect_sanctions_exposure means the transaction risk provider reported sanctions exposure of any size, including counterparty exposure that the provider labels “direct”; it is not proof that a transaction party is listed. risk_exposure reports other exposure categories of any size or a hacking event. The deprecated sanctions_hit is an alias of direct_sanctions_match only. If the AML source is unavailable, exposure flags are null; completed local matching can still be false.

How exposure moves the state

The two exposure flags say that the provider reported something; whether it moves the state depends on how close and how large it is (policy b2b-exp-v2). The reported rows stay in key_exposure (up to 50, after any local list match), most significant first, with their hops, share and the grade the policy gave each one (high, review, minor). percent is null when the share is unknown or its source does not allow showing it.

Provider row Hops and share Effect
Sanctioned entity 1 hop or closer and at least 5% HIGH_RISK, INDIRECT_SANCTIONS_EXPOSURE
Sanctioned entity 1 hop or closer below 5%, at least 1% at any hop, or share unknown REVIEW, SANCTIONS_EXPOSURE_REVIEW
Sanctioned entity 2 hops or more and below 1% no change, SANCTIONS_EXPOSURE_MINOR
Other category up to 2 hops, with at least 1% or an unknown share; any hacking event REVIEW, RISK_EXPOSURE
Other category further away or smaller no change, RISK_EXPOSURE_MINOR
Unrecognised or empty category as for other categories REVIEW, UNRECOGNIZED_PROVIDER_LABEL; or no change, UNRECOGNIZED_PROVIDER_LABEL_MINOR

A category ChainZap does not recognise is never ignored: it is graded like any other risk category and named, so a close or large unfamiliar finding goes to review, and a distant, small one is still reported (UNRECOGNIZED_PROVIDER_LABEL_MINOR). The recognised categories are sanctioned_entity, illicit_activity, mixer, risk_exchange, gambling and bridge.

The thresholds were calibrated on a sample of ordinary USDT transfers on TRON: under the earlier rule, which escalated any indirect sanctions trace, most of them were HIGH_RISK; under this one risk_status: HIGH_RISK rests on an exact list match, a material share one hop away or a high AML level, and risk.driver names which. An exact local sanctions match of a transaction party is always HIGH_RISK. Apply your own stricter rule to key_exposure if your compliance policy requires one.

Each source reports checked_at (this evaluation), retrieved_at (when a response or remote index was fetched) and data_updated_at (the underlying list date, when known). A recent fetch does not establish a recent underlying publication. The AML source does not provide that publication date; it remains null. The legacy freshness.max_source_age_seconds covers fetched observations only, not sanctions-list age. source_coverage.universal_clearance is always false.

  • Risk unknown: aml_risk_level="unknown".
  • Token unverified: token_authenticity="unknown"; its ticker does not prove authenticity.

LIMITED_DATA is a normal business response with HTTP 200, not necessarily a transport error.

Freshness

Read freshness.checked_at, the evidence timestamps and max_source_age_seconds. A source timestamp of null means the time is unknown. An idempotent replay returns the original result and the original request ID; it is not a fresh observation. A new observation is a new logical operation, created after you have resolved the previous response.

Billing

Only complete, final checks are charged: CLEAR, REVIEW and HIGH_RISK with coverage: complete for a transaction confirmed past its finality policy or failed for good each use one check. Pending and not-found answers, partial coverage (LIMITED_DATA), transport errors, timeouts and idempotent replays do not use a check; their reservation is released. The Playground page uses the same rule and the same organization quota. Sandbox keys never use paid checks. Details are in plans and payment.

Network and asset boundaries

See the Ethereum/TON asset matrix and finality policies. Complete coverage is scoped to the supported transfer type and the selected asset; it does not cover untraced ETH internal value or a token's net balance effect. Unknown token decimals never become a default value.

TON contract value that cannot be classified produces TON_CONTRACT_VALUE_NOT_CLASSIFIED and partial coverage, even if another transfer in the same trace is recognized. It cannot produce CLEAR; a known high-risk finding still takes priority as HIGH_RISK. Check details.limitations, details.finalized and details.trace_complete in the short response.

A TON jetton credit can be confirmed even when the recipient wallet cannot execute a notification because it has too little gas. TON_NOTIFICATION_NOT_EXECUTED records this separately: the jetton transfer and the wallet owner must still be verified by both sources. It does not excuse a failed or bounced token transfer.

source_coverage.transaction_sources reports each configured on-chain fact source separately under a stable neutral name: chain_explorer and chain_node on TRON and TON, chain_node and chain_node_secondary on Ethereum. source_coverage.risk_sources.aml_provider is the AML assessment. These observations retain their original retrieval time through the facts cache. A successful response with an unknown AML risk level is a partial aml_provider observation (risk_level_unknown). Missing local sanctions coverage does not turn a successful AML assessment into AML_UNAVAILABLE; the workflow remains partial with SANCTIONS_UNAVAILABLE. Network availability still depends on deployment configuration and tenant access.

Wallet checks

A wallet check reports its own coverage word in details.data_coverage: FULL when every applicable module was fully screened, PARTIAL when at least one was screened only in part, INSUFFICIENT when a gate module (sanctions_aml or address_security) was not screened at all or the engine could not decide. The top-level coverage is complete only for FULL. Each module row carries its own source_coverage, and details.source_coverage lists every source with the same status vocabulary as above. Unlike the transaction check, a wallet check is charged whenever a state is produced, including with partial coverage; only limited_data is free. See wallet check.

Coverage status and evidence items

Contract 0.3 adds coverage_status (OK, DEGRADED, SKIPPED, UNAVAILABLE, DISAGREEMENT), the reason behind it in status_drivers.coverage, one evidence_items entry per source the answer rests on, and coverage_limitations split into what limited this check and what the product never covers. See status axes. coverage and workflow_coverage keep their meaning.

Reason codes

Each reason has code, category and severity (info, warning, high, critical). /v1/transaction/check returns 2 to 5 prioritized reasons; the short list always keeps the reason that decided the state, every EXPECTED_* reason, every payment comparison and client-context finding, and every obligation ledger finding above info, and fills the rest by severity. The full catalogue is versioned (versions.reason_codes); status_drivers names the reason behind each status axis. /v1/check returns the full list in details.reasons and the corresponding strings in reason_codes. Tolerate future codes. Never treat an unknown code as a clean result, and never infer from an omitted reason that the related check was performed.

Code Meaning
TX_FACTS_VERIFIED The transaction sources answered and agree, including when they agree that the transaction is absent. It describes the facts, not a found or matching payment.
TX_FACTS_PARTIAL Required transaction facts are incomplete.
TX_FACTS_DISAGREE Transaction evidence is contradictory.
TX_FINALIZED Transaction finality requirements are met.
TX_PENDING_CONFIRMATIONS Finality or confirmations are insufficient.
TX_REVERTED Transaction execution failed.
TX_NOT_FOUND The transaction was not found.
TRANSFERS_TRUNCATED Transfer evidence was truncated.
AMBIGUOUS_TRANSFER Several transfers fit the supplied recipient and token, and none, or more than one, can be selected as the payment.
AML_UNSUPPORTED_ASSET The applicable asset lacks supported AML coverage.
AML_UNAVAILABLE Applicable AML evidence is missing.
AML_LOW Available AML assessment reports low risk.
AML_ELEVATED Available AML assessment reports moderate risk.
AML_HIGH Available AML assessment reports high or severe risk.
DIRECT_SANCTIONS_MATCH Exact address match in the configured sanctions indexes.
NO_DIRECT_SANCTIONS_MATCH No exact address match in the checked indexes; read source coverage and freshness.
INDIRECT_SANCTIONS_EXPOSURE Material provider-reported sanctions exposure (1 hop or closer, at least 5%); not a direct listing match. Gives HIGH_RISK.
SANCTIONS_EXPOSURE_REVIEW Provider-reported sanctions exposure that needs review (1 hop or closer below 5%, at least 1% at any hop, or share unknown). Gives REVIEW.
SANCTIONS_EXPOSURE_MINOR Distant and small provider-reported sanctions exposure; reported, does not change the state.
RISK_EXPOSURE Significant other risk exposure reported by the provider (up to 2 hops and at least 1%, or share unknown), or a hacking event. Gives REVIEW.
RISK_EXPOSURE_MINOR Distant or small other risk exposure; reported, does not change the state.
SANCTIONS_UNAVAILABLE Sanctions screening is unavailable; this is not a clear result.
SANCTIONS_DATA_STALE A sanctions list is past its freshness window or its freshness cannot be established; coverage is partial.
TOKEN_AUTH_UNAVAILABLE Token authentication evidence is unavailable.
TOKEN_OFFICIAL Token contract is recognized as official.
TOKEN_FAKE Token is identified as fake.
TOKEN_UNKNOWN Token authenticity is unknown.
EXPECTED_RECIPIENT_MISMATCH The selected transfer does not match the expected recipient.
EXPECTED_TOKEN_MISMATCH The selected transfer does not match the expected contract.
EXPECTED_AMOUNT_MISMATCH The selected transfer does not match the expected raw amount.
EXPECTED_DATA_UNAVAILABLE A requested comparison could not be completed because transfer data is missing. Its match field is null.
EXPECTED_SENDER_MISMATCH The selected transfer does not come from expected_sender.
EXPECTED_NETWORK_MISMATCH The recorded instruction names another network than the one checked.
AMOUNT_WITHIN_TOLERANCE The amount differs from the expected one by no more than amount_tolerance; it matches.
ADDITIONAL_RELEVANT_TRANSFERS Besides the matching transfer, the transaction pays the expected recipient the expected token again; it may pay twice. Gives REVIEW.
SPLIT_WITHIN_TX Several transfers in the transaction to the expected recipient add up to the expected amount. Gives REVIEW.
ATTEMPT_MAY_STILL_LAND A check sent with direction: "OUTGOING" found no transaction yet; the attempt behind it may still reach the chain. Do not pay it again before it is final.
UNRECOGNIZED_PROVIDER_LABEL The AML provider reported a category ChainZap does not recognise, close and large enough to look at. Gives REVIEW.
UNRECOGNIZED_PROVIDER_LABEL_MINOR An unrecognised provider category that is distant or small; reported, does not change the state.

Obligation ledger and client context

These reasons appear when a check names an obligation or carries customer_context. They never change coverage or what is charged.

Code Meaning
INSTRUCTION_UNAVAILABLE The obligation named has no instruction on record for the version asked; nothing was compared. integrity_status: INSUFFICIENT, gives REVIEW.
INSTRUCTION_HISTORY_INCOMPLETE The first instruction recorded for the obligation is not version 1; earlier versions are unknown.
INSTRUCTION_NOT_EFFECTIVE The payment is in a block before the instruction's instruction_effective_at. Gives REVIEW.
INSTRUCTION_CONFLICT Another request changed the obligation's instruction while this one was being checked. Gives REVIEW.
TX_REUSED_ACROSS_OBLIGATIONS The payment event already settles a different obligation. Gives REVIEW; nothing is allocated.
DUPLICATE_EXECUTION Two or more transactions together paid the obligation more than it was owed. Gives REVIEW.
EVENT_CONTENT_CHANGED A payment event recorded earlier is now reported with different content; the record is not overwritten. Gives REVIEW.
ALREADY_CREDITED You declared the payment already credited, or applied in your books, even in part (customer_context). Gives REVIEW.
CUSTOMER_STATE_UNKNOWN Shown as status_drivers.action when ChainZap cannot tell whether acting on a clean answer is safe for your books: CONFIRM_INTERNAL_STATE.

Product limitations

Listed in coverage_limitations.product, never in reasons: what the product does not cover for this network or asset, whatever the check. They do not make an answer retryable.

Code Meaning
AML_TRANSACTION_LEVEL_ONLY AML screening covers the transaction's addresses as the provider sees them, not the full history of every counterparty.
RISK_STATE_AS_OF_CHECK Risk evidence is the provider's state at the time of the check, not at the time of the payment.
SANCTIONS_CONFIGURED_LISTS_ONLY Sanctions matching covers the configured lists only (OFAC, EU, UK).
MEMO_NOT_EVALUATED A memo or comment on the transfer, such as a TON comment, is not compared.

Ethereum / TON / Zero-Friction

/v1/check exposes all reason objects in details.reasons and string codes in reason_codes. The canonical /v1/transaction/check keeps its bounded list.

Code Meaning
ETH_REORG_DETECTED Canonical Ethereum block does not match transaction inclusion.
ETH_NATIVE_INTERNAL_TRANSFERS_UNAVAILABLE Native internal ETH value is outside the receipt-log scope.
TOKEN_MINT_OR_BURN A mint or burn is not an ordinary deposit.
TOKEN_BALANCE_EFFECT_NOT_VERIFIED Logs do not prove net token balance changes.
ZERO_VALUE_TRANSFER Transfer amount is zero.
TON_BOUNCED A message was bounced.
TON_TRACE_INCOMPLETE Required message descendants or complete trace evidence are missing.
TON_EXECUTION_FAILED A transaction in the transfer trace failed.
TON_FINALITY_UNVERIFIED Masterchain finality is not verified.
TON_CONTRACT_VALUE_NOT_CLASSIFIED Contract-message TON value is not classified as a plain transfer. Coverage is partial even if another transfer was verified.
JETTON_IDENTITY_UNVERIFIED Jetton wallet/master/owner identity is unverified.
JETTON_WALLET_MISMATCH Jetton wallet, master or owner contradicts the transfer message.
NO_SUPPORTED_TRANSFER No supported completed transfer was established.
MULTI_ASSET_AML_PARTIAL Multiple assets are present; one asset rail was screened for AML.
TON_TRACE_LIMIT_EXCEEDED The bounded trace size or depth was exceeded; transfer evidence is incomplete.
TON_NOTIFICATION_NOT_EXECUTED A jetton transfer notification reached a recipient that did not execute it; the transfer itself completed.

Wallet reason codes

POST /v1/wallet/check returns up to ten reasons in details.reasons, ordered by severity, and their codes in reason_codes. A finding is stated only when the engine's own significance policy rates it as one, so a reason here always corresponds to something the report would show. The severity in the table is the default; a confirmed critical provider flag or a red explorer tag raises it.

Code Category / severity Meaning
DIRECT_SANCTIONS_MATCH sanctions / critical Exact address match in the configured sanctions indexes, or a direct listing reported by the AML provider.
INDIRECT_SANCTIONS_EXPOSURE sanctions / high Exposure to sanctioned funds reported by the AML provider; not a direct listing.
SANCTIONS_EXPOSURE_MINOR sanctions / warning A small share of exposure to sanctioned funds reported by the AML provider. The transaction check emits the same code at info.
SANCTIONS_UNAVAILABLE sanctions / warning No sanctions index was screened; this is not a clear result.
NO_DIRECT_SANCTIONS_MATCH sanctions / info No exact match in the screened indexes; read source coverage and freshness.
AML_HIGH risk / high The AML provider reports high or severe risk.
AML_ELEVATED risk / warning The AML provider reports moderate risk.
AML_LOW risk / info The AML provider reports low risk.
AML_EXPOSURE_UNAVAILABLE risk / warning The AML provider was called but did not answer; exposure is unknown.
AML_EXPOSURE_SKIPPED risk / info The AML provider was not called because a local sanctions or denylist hit already decides the result.
HACKING_EVENT_LINK risk / high The AML provider links the address to a known hacking event.
DENYLIST_MATCH address / critical The address is on a public denylist of scam, phishing or drainer addresses.
ISSUER_BLOCKED address / critical A stablecoin issuer has frozen the address.
PROVIDER_SECURITY_FLAG address / high A security provider flags the address; the severity follows the confirmed flags.
EXPLORER_RISK_TAG address / high A block explorer or account label marks the address as risky.
SCAM_EVENT_ACTIVITY address / high Transfers with addresses flagged for scam activity were observed.
ADDRESS_SECURITY_UNAVAILABLE address / warning Address security sources were not fully screened.
NO_ADDRESS_SECURITY_FLAGS address / info The screened address security sources report nothing.
TOKEN_IMPERSONATION token / high A held or created token impersonates an official asset.
TOKEN_HOLDINGS_UNVERIFIED token / warning Held tokens could not all be screened for authenticity.
TOKEN_HOLDINGS_VERIFIED token / info Held tokens were screened and none impersonates an official asset.
RISKY_COUNTERPARTY_DIRECT counterparties / high A direct counterparty is a known risky service or address.
RISKY_COUNTERPARTY_INDIRECT counterparties / warning A risky service or address sits one hop further away.
COUNTERPARTY_SCREENING_PARTIAL counterparties / warning Not every counterparty could be attributed; coverage is partial.
NO_COUNTERPARTY_FLAGS counterparties / info Screened counterparties raised no flags.
NEW_WALLET behaviour / warning The wallet is very new.
BEHAVIOUR_PATTERN_FLAGGED behaviour / warning A transaction pattern worth a look was observed, for example bursts, rapid cycling or relay behaviour.
LOW_ACTIVITY behaviour / info Very few transactions; behaviour signals carry little weight.
CONTRACT_ADDRESS behaviour / info The address is a contract, not a wallet.
NO_BEHAVIOUR_FLAGS behaviour / info No notable behaviour pattern was observed.
WALLET_DATA_LIMITED coverage / warning Not enough data to decide; the state is limited_data.
SOURCE_UNAVAILABLE coverage / warning A required source did not answer.
NETWORK_ASSUMED_ETH coverage / warning A 0x address was screened on Ethereum only; it may also exist on other EVM chains.

Decisions, actions and outcomes

Every transaction check is a decision with its own decision_id. What ChainZap recommended, what you did, how you resolved it and how it turned out are separate records: a recommendation is not an action, and a held payment is not a prevented loss until you confirm it was one.

Read a decision

GET /v1/decisions/{decision_id} returns the decision as it was made: the four status axes, overall_state, recommended_action, reuse_status, the selected payment event, reason_codes, versions and the evidence_items it rested on, plus every event recorded on it since. A later check of the same transaction is a new decision; it never rewrites an earlier one, so an answer you acted on can always be explained with the evidence it had at the time. Evidence items are kept for as long as the providers' terms allow and at most the retention period of raw values.

An idempotent replay of the original request also returns the original answer, byte for byte.

Record what followed

Endpoint Body Meaning
POST /v1/decisions/{decision_id}/actions action, optional action_at What you did: CREDITED, PAID, HELD, REJECTED, REFUNDED, ESCALATED, RETRIED, NO_ACTION, OTHER.
POST /v1/decisions/{decision_id}/resolution resolution, optional resolved_at How the case was closed: ACCEPTED, ACCEPTED_AFTER_REVIEW, REJECTED, REFUNDED, CORRECTED, WRITTEN_OFF, OTHER.
POST /v1/decisions/{decision_id}/outcome outcome_status, loss_prevented, optional outcome_at How it turned out: CONFIRMED, DISPUTED, UNRESOLVED or UNKNOWN, and whether a loss was prevented: CONFIRMED, NOT_CONFIRMED or UNKNOWN (the default).

Each call appends an event (sending the very same event again is ignored); nothing is overwritten, and several actions on one decision are normal (HELD, then ESCALATED, then REFUNDED). Every event may carry an owner: an opaque identifier of the person or system that acted, up to 64 characters of A-Z a-z 0-9 . _ : -. Never send a name or an email address; 422 invalid_owner refuses anything else.

Exposure is not prevented loss

A HIGH_RISK answer means a material risk signal was present. It does not mean a loss was avoided, and ChainZap never counts it as one. Only an outcome you record with loss_prevented: "CONFIRMED" is a prevented loss. Report on outcomes, not on recommendations.

verification_final and risk_final preserve the separate finality axes as of the decision. They are null on historical decisions made before policy b2b-tx-2026-10.3; read those decisions under their original versions.decision policy.

Idempotency

Idempotency-Key ties every attempt to send a request to one logical check. It is an operation key, not an API key.

Persist a key per logical operation

Idempotency-Key accepts 1 to 128 printable ASCII characters. Keys are scoped to your organization and the normalized request body. Store the key and the body before the first attempt and reuse both on every retry, including retries from another server or after API key rotation. Never include credentials or personal information in a key.

Situation API response
The operation is complete The original response, byte for byte, with Idempotent-Replayed: true and the original X-Request-ID.
The operation is still running 409 idempotency_in_progress with Retry-After; retryable.
Same key, different body 409 idempotency_conflict; not retryable. Fix the link between the key and the operation.

Retention and errors

The default retention is 24 hours; confirm any contracted retention change with support. After retention expires, the key can represent a new charged execution. Bound automatic retries to the retention window and keep your own payment-operation ledger.

What is stored and replayed: every 200 answer, including a pending or LIMITED_DATA one, and the 404 and 422 answers given after the check was accepted, for example transaction_not_found, invalid_expected_amount, NETWORK_REQUIRED, NETWORK_AMBIGUOUS and an address that fails validation for the network discovery resolved. retryable=true describes the failure; it does not erase a stored response, so 404 transaction_not_found replays for the whole retention period under the same key.

What is never stored: 429 and every 5xx. Retrying with the same key runs the check again, which is exactly what those errors ask for. Refusals made before a check is accepted are not stored either: authentication, 403, 413, request-schema 422 and contract 422 such as expected_fields_required.

Stop automatic retrying when Idempotent-Replayed: true accompanies a stored answer. Once you have received and reconciled it, create a new logical check with a new key if your workflow needs a later observation, for example of a pending or not-yet-indexed transaction after Retry-After. Never use a new key merely because the original network outcome is unknown.

Endpoint and network are part of the operation

Both check endpoints share your organization's key namespace. Reusing a key on another endpoint or with a changed body returns 409 idempotency_conflict, even if the hash is unchanged. A resolved network and the original result are retained on replay; the API checks that access to that network is still allowed.

For NETWORK_REQUIRED or NETWORK_AMBIGUOUS, first reconcile the received error, then supply network in a new operation with a new key. A replay does not run network discovery again.

Rate limits, quota and concurrency

Organization limits

Limits come from your current versioned plan or from a custom entitlement assignment. Inspect /v1/usage and the Plan and billing page in the developer dashboard. Published beta prices and included checks are in plans and payment; request-rate and concurrency limits are preliminary until general availability.

Response headers Meaning
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset Your organization's request capacity, what is left of it and when it resets.
X-Quota-Limit, X-Quota-Remaining, X-Quota-Reset The included checks of the current billing period, what is left of them and when the period ends. X-Quota-Available adds extra checks.

Reset headers use Unix seconds. A paid billing period is anchored to the confirmed payment; an organization without a paid plan keeps a UTC calendar month. Periods follow UTC regardless of your browser's timezone. All API keys and requests made in the developer dashboard share the same quota. When a check draws on included checks, X-Quota-Remaining already counts it: its quota is reserved while it runs and returned if the answer is not charged, so a free answer shows one less than /v1/usage reports afterwards. An idempotent replay does not touch quota and carries no X-Quota-* headers.

Backpressure

Rate exhaustion returns 429 rate_limit_exceeded. Too many in-flight checks returns 429 concurrency_limit_exceeded. A full quota returns 429 quota_exceeded. Honor Retry-After, which may be long for a monthly quota. Replays still pass through access, rate and concurrency controls, but do not charge again.

Checks that reach the data sources and return no complete, final result (pending, not found, partial) are never charged, and a day of them is capped per organization (500 by default). Poll a pending transaction with backoff rather than in a tight loop. Past that ceiling the API answers 429 rate_limit_exceeded until the ceiling resets at the end of the UTC day; each answer's Retry-After is at most an hour. A healthy integration never reaches it.

Use a bounded request queue and per-organization concurrency on your server. Plan changes affect future entitlements and do not rewrite historical usage.

Batches

A batch file holds up to 200 rows and 256 KiB; an organization runs at most two batches at a time (checked when a batch starts). A running batch uses at most half of your organization's request rate, so your own checks keep their share, and it pauses rather than fails when the quota or the daily ceiling of unbilled checks runs out. Each distinct transaction in a batch is charged once.

Spend protection

Every organization has a cap on what ChainZap spends on paid data sources for its checks per UTC day; support can raise or lower it. Past the cap a check still answers, from cached data where it exists. A risk screen that would need a new paid call is skipped: on a transaction check the AML source in source_coverage.risk_sources reports error_code: "spend_cap" ("daily_cap" when the AML source's service-wide daily ceiling is reached instead), risk_status is INSUFFICIENT, and the answer is not charged; a wallet check lists aml_provider_unavailable. The cap resets at 00:00 UTC; a new check before then usually returns the same gap, whatever risk_retry_after_seconds suggests. spend_cap is also reported while spend cannot be accounted (an unknown tariff or an unavailable counter); that gap clears when accounting recovers, not at 00:00 UTC. Payment verification is not affected: a final match stays final.

Before each uncached paid attempt, ChainZap atomically reserves its full configured cost, including in-flight calls. A call that would exceed the remaining cap is skipped. Unknown tariffs or unavailable accounting block new paid calls when a cap is configured. Definitely unsent requests return their reservation; a lost response conservatively keeps it. Cached evidence remains usable. This limit affects risk coverage, not final payment verification.

Ledger reads

Reading obligations, decisions and batches (a lookup, a status poll, a page of rows, an export) does not spend the request rate of your checks: reads have their own bucket per organization, four times your per-minute rate and at least 120 a minute, so following a batch never takes capacity from your checks. Past it a read answers 429 rate_limit_exceeded with Retry-After; on a read, the X-RateLimit-* headers describe this bucket.

Automatic network discovery

POST /v1/check and POST /v1/transaction/check share the same organization limits. A request without network reserves quota before looking up transaction facts across the available networks. Discovery is bounded and does not run a risk check for every network. Only the completed check with complete coverage is charged; a discovery error releases the reservation.

Retries and timeouts

Client deadline

Use a client deadline longer than the 12-second server hard deadline, for example 15 seconds per attempt. A wallet check has a 30-second server budget; use about 35 seconds for it. The latency target is p95 at most 5 seconds; it is a release gate, not an unconditional guarantee. Network time is outside the server budget.

Bounded retries

For a received error, retry only when error.retryable=true. Honor Retry-After, use exponential backoff with jitter, limit the number of attempts and cap the overall workflow duration. If the instructed wait exceeds your interactive budget, schedule a retry rather than shorten the wait.

Lost connection

For a network exception, retry the same stored body and Idempotency-Key. Preserve both across restarts. Stop on an idempotent replay of a stored error and reconcile the result. 409 idempotency_conflict requires an integration fix; 409 idempotency_in_progress, 429, 503 and 504 may be retried under these rules.

Record only the request ID and the public error code in ordinary logs. Do not log request bodies, API keys, wallet values or provider payloads. The executable examples follow these rules.

A new observation

A pending transaction can later finalize, but replaying its stored response does not fetch a newer state. Once the current result is received and reconciled, schedule a new logical check with a new key if you need a later observation. For an unresolved network error, follow network discovery; NETWORK_REQUIRED and NETWORK_AMBIGUOUS require explicit network context, not automatic retries.

Verification and risk finality

verification_final describes the payment facts and comparison. result_final is its alias; retryable is its opposite, and retry_after_seconds is the suggested wait for another verification observation. A final chain transaction with an exact expected match returns verification_final: true and integrity_status: MATCH even when KYT is unavailable and risk_status: INSUFFICIENT.

risk_final is true once checking again is not expected to fill a gap in the risk assessment: the transaction is final, its sources agree, and no AML, sanctions or token check had a temporary gap (an outage or a stale list). While the transaction is pending or not found, risk_final is false whatever risk_status says, and risk_retry_after_seconds equals retry_after_seconds: risk is assessed again once the transaction is final. risk_retryable is its opposite; risk_retry_after_seconds suggests when to check risk again. A permanent coverage limit can be final while remaining INSUFFICIENT: finality does not mean clearance. KYT describes risk at check time and can change later even after a final assessment.

Pending transactions return integrity_status: NOT_EVALUATED, verification_final: false, null match flags, NOT_EVALUATED for every requested comparison and no EXPECTED_*_MISMATCH reason: terms that differ are reported only once the transaction is final. When the sources disagree about the transaction itself (transaction_status: INSUFFICIENT, coverage_status: DISAGREEMENT), the answer is not final either, but it still compares the transfers the sources report and can carry EXPECTED_*_MISMATCH: act on verification_final: false and check again. Observed transfers remain evidence, not a final payment match. Verification retries wait 5–120 seconds for pending finality, 15 seconds for an absent transaction, or 30 seconds for a temporary chain-source gap. A temporary KYT gap suggests a separate risk retry after 30 seconds. When the AML source reports error_code spend_cap (your organization's daily cap on paid sources is reached, or spend cannot be accounted for the moment) or daily_cap (the AML source's service-wide daily ceiling is reached), a reached cap resets at 00:00 UTC, and a new check before then usually returns the same gap; see limits. On a transaction that is not found, risk_retryable is true although risk_status is NOT_EVALUATED: risk is assessed once the transaction appears.

Missing KYT does not reopen final verification and never permits PROCEED: MATCH with risk_status: INSUFFICIENT recommends MANUAL_REVIEW. Only final answers with complete coverage are charged; a final verification with partial KYT coverage remains free. For a new observation, use a new Idempotency-Key; replaying the same key returns the stored answer under its original policy.

Error reference

Public envelope

JSON
{"request_id":"merchant-order-1001-attempt-1","error":{"code":"provider_unavailable","message":"Transaction data is temporarily unavailable","retryable":true}}

The response header X-Request-ID identifies the same response. Branch on the stable code and retryable; do not parse the human-readable message. Gateway failures may not have this envelope: treat an invalid or missing envelope as an unresolved transport outcome and preserve the original idempotency key.

HTTP Codes Action
401 invalid_api_key Correct the server credentials; do not retry blindly.
403 tenant_suspended, network_not_enabled, check_kind_not_enabled Resolve access with your organization; the pilot contract covers TRON transaction checks only.
403 forbidden The request did not come through Cloudflare: it was sent straight to a server address. Call https://api.chainzap.io. This edge answer carries its request_id in the body only. When api.chainzap.io itself is pinned to a server address, the server refuses the request with a plain 403 page and no JSON.
403 network_disabled Adapter activation is pending; contact support.
403 ledger_not_enabled, batch_not_enabled The obligation ledger or batches are not switched on; send the check without ledger fields.
404 obligation_not_found, decision_not_found, payment_event_not_found, allocation_not_found, batch_not_found The record does not exist in this environment. A payment event exists only after a check that named an obligation recorded it with complete coverage.
404 transaction_not_found /v1/check only. Every checked network confirmed the transaction is absent. Check the hash and indexing time; bound retries.
409 idempotency_conflict Fix the link between the key and the body (or, for a batch upload, the file); not retryable.
409 idempotency_in_progress Wait for Retry-After and retry the same operation.
409 instruction_conflict, instruction_version_required The obligation's instruction is recorded with other terms. Send a new instruction_version for changed terms; recorded versions never change.
409 allocation_exceeds_event, payment_event_not_settled, allocation_token_mismatch The shares exceed the payment event, the event did not execute or is not final, or it is in another token than the obligation.
409 attempt_conflict The attempt is recorded with another channel, provider reference, amount or transaction, by POST /v1/obligations/attempts or by a check that names its attempt_reference with another tx_hash; record a retry or a provider switch as a new attempt_reference.
409 batch_not_executable, insufficient_quota, nothing_to_retry The batch is not in a state for this action, the quota cannot cover it (send allow_partial), or no row can be retried.
413 payload_too_large The body exceeds 4 KiB (256 KiB for a batch upload); send only the documented fields.
413 batch_too_large The batch file has more than 200 rows; split it.
415 unsupported_media_type Send the body as Content-Type: application/json, or text/csv for a batch upload.
422 malformed_tx_hash, invalid_address, invalid_network, invalid_tron_address, invalid_eth_address, invalid_ton_address, invalid_expected_amount, invalid_idempotency_key, invalid_query_parameter, validation_error Correct the invalid request.
422 invalid_amount_tolerance, invalid_reference, invalid_owner, instruction_terms_required Correct the request: a tolerance finer than the token's decimals, a reference outside 1 to 128 printable ASCII characters, an owner that is not an opaque identifier, or an obligation without any payment terms.
422 invalid_csv, invalid_csv_header, confirmation_required Fix the batch file, or confirm the estimate with confirm: true.
422 expected_fields_required, token_not_enabled Your organization's contract requires all three expected_* fields and the token it covers. Fix the request; not retryable.
422 NETWORK_REQUIRED, NETWORK_AMBIGUOUS Insufficient input, not success: the checked networks left the answer open. Specify the network and use a new key. See network discovery.
429 rate_limit_exceeded, quota_exceeded, concurrency_limit_exceeded Honor Retry-After; reduce concurrency or wait for the quota reset.
429 batch_limit_reached Two batches are already running for your organization; wait for one to finish.
503 provider_unavailable, rate_limiter_unavailable, api_disabled, api_not_configured Retry only when retryable and within your retry budget; keep the same key.
504 request_timeout Retry the same key and body after the instructed delay. It also answers when another request held the same payment or obligation in the ledger at that moment: nothing was stored or charged.
500 internal_error Use the bounded retry policy and contact support with X-Request-ID.

Check Idempotent-Replayed: a stored error does not refresh itself on replay. See idempotency. Never assume that an HTTP error means no request was accepted.

Security recommendations

Protect credentials

Call the API from your backend. Store API keys in a secret manager, limit access to the integration service and rotate a key after suspected exposure. Never paste a key into a support ticket, URL, issue tracker, analytics event, browser console or frontend bundle.

Keep your identity provider protected with its available strong authentication policies. Accept invitations only from a trusted organization administrator. Sign out on shared devices; use Organization settings in the developer dashboard to sign out everywhere.

Handle data deliberately

Validate incoming payment requests on your server. Bind checks to your own payment-operation IDs and reconcile ambiguous outcomes. Use exact token contracts and decimal strings, and handle null and unknown states explicitly. Escape organization names, key labels and response values before displaying them in your own UI.

Retain only the data your workflow needs. Restrict access to response details. Send support the response X-Request-ID and the public error code; omit keys, balances and raw payloads.

API reference

Download OpenAPI. Field names and types below come directly from the public API contract. A required field may still accept null when the type allows it. Read the guides above for each field’s meaning.

POST /v1/transaction/check

Check one transaction. Send X-API-Key and a stored Idempotency-Key. Whether network is required depends on the endpoint.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.
500 Internal error. Keep the X-Request-ID for support.

POST /v1/check

Check one transaction. Send X-API-Key and a stored Idempotency-Key. Whether network is required depends on the endpoint.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

GET /v1/usage

Read your organization’s limits and usage. The optional days parameter accepts 1-90 and defaults to 30.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
422 Correct the request fields.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

GET /v1/catalog

Read the published beta pricing catalog: plans, included checks and extra-check terms.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.

POST /v1/wallet/check

Check one wallet address. Send X-API-Key and a stored Idempotency-Key. The network is optional and is read from the address format.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.
500 Internal error. Keep the X-Request-ID for support.

POST /v1/obligations

Record an obligation and its instruction. A changed recipient, amount, token or network is a new instruction_version.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
504 The request exceeded the server time limit.

POST /v1/obligations/lookup

Read an obligation back with its instruction history, attempts, allocations and decisions. The reference travels in the body, never in the URL.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
504 The request exceeded the server time limit.

POST /v1/obligations/attempts

Record one execution attempt of an obligation, or a new status of it. A retry or a provider switch is a new attempt_reference.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
504 The request exceeded the server time limit.

POST /v1/allocations

Declare how one payment event settles one or more obligations, for example an aggregate payout.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
504 The request exceeded the server time limit.

POST /v1/allocations/{allocation_id}/reversal

Reverse an allocation. It stays on record with the time and reason.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
504 The request exceeded the server time limit.

GET /v1/decisions/{decision_id}

Read ChainZap's decision as it was made, with its evidence and what you recorded after it.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
504 The request exceeded the server time limit.

POST /v1/decisions/{decision_id}/actions

Record what you did with a decision, how you resolved it, or how it turned out. Recorded next to the decision, never into it.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
504 The request exceeded the server time limit.

POST /v1/decisions/{decision_id}/resolution

Record what you did with a decision, how you resolved it, or how it turned out. Recorded next to the decision, never into it.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
504 The request exceeded the server time limit.

POST /v1/decisions/{decision_id}/outcome

Record what you did with a decision, how you resolved it, or how it turned out. Recorded next to the decision, never into it.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
504 The request exceeded the server time limit.

POST /v1/batches

Upload a CSV of up to 200 payments. Every row is validated, compared with the others and with earlier checks, and the cost is estimated before anything runs.

HTTP Response
201 Created: the batch was accepted and checked before it runs; start it with execute.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.

GET /v1/batches

Upload a CSV of up to 200 payments. Every row is validated, compared with the others and with earlier checks, and the cost is estimated before anything runs.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.

GET /v1/batches/{batch_id}

Read a batch's status, progress, preflight, estimate and summary, or its rows with their results.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.

GET /v1/batches/{batch_id}/rows

Read a batch's status, progress, preflight, estimate and summary, or its rows with their results.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.

GET /v1/batches/{batch_id}/export.csv

Export a batch as CSV for spreadsheets

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.

POST /v1/batches/{batch_id}/execute

Start a preflighted batch after confirming its estimate, retry only rows whose answer can still change, or cancel it.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.

POST /v1/batches/{batch_id}/retry

Start a preflighted batch after confirming its estimate, retry only rows whose answer can still change, or cancel it.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.

POST /v1/batches/{batch_id}/cancel

Start a preflighted batch after confirming its estimate, retry only rows whose answer can still change, or cancel it.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.

POST /v1/escrow-deals

Create an escrow deal draft, list the organization's deals, or find one by your reference.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

GET /v1/escrow-deals

Create an escrow deal draft, list the organization's deals, or find one by your reference.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/lookup

Create an escrow deal draft, list the organization's deals, or find one by your reference.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

GET /v1/escrow-deals/events

Read the event feed after a sequence number, or send one event to the webhook again.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/events/{event_id}/redeliver

Read the event feed after a sequence number, or send one event to the webhook again.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

GET /v1/escrow-deals/webhook

Set, read or disable the signed webhook, rotate its secret, or send a test event.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

PUT /v1/escrow-deals/webhook

Set, read or disable the signed webhook, rotate its secret, or send a test event.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

DELETE /v1/escrow-deals/webhook

Set, read or disable the signed webhook, rotate its secret, or send a test event.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/webhook/rotate-secret

Set, read or disable the signed webhook, rotate its secret, or send a test event.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/webhook/test

Set, read or disable the signed webhook, rotate its secret, or send a test event.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

GET /v1/escrow-deals/resolvers

Register your external resolvers, list them, read their accounts on-chain again, or disable them. A resolver is optional, and ChainZap is never one.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/resolvers

Register your external resolvers, list them, read their accounts on-chain again, or disable them. A resolver is optional, and ChainZap is never one.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

GET /v1/escrow-deals/resolvers/{resolver_id}

Register your external resolvers, list them, read their accounts on-chain again, or disable them. A resolver is optional, and ChainZap is never one.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/resolvers/{resolver_id}/verify

Register your external resolvers, list them, read their accounts on-chain again, or disable them. A resolver is optional, and ChainZap is never one.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/resolvers/{resolver_id}/disable

Register your external resolvers, list them, read their accounts on-chain again, or disable them. A resolver is optional, and ChainZap is never one.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/resolvers/{resolver_id}/enable

Register your external resolvers, list them, read their accounts on-chain again, or disable them. A resolver is optional, and ChainZap is never one.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

GET /v1/escrow-deals/{ref}

Read one escrow deal, replace the terms of a draft, or cancel a draft.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/{ref}/versions

Read one escrow deal, replace the terms of a draft, or cancel a draft.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/{ref}/cancel

Read one escrow deal, replace the terms of a draft, or cancel a draft.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

Read the exact text each party signs, or relay a party's signature.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/{ref}/consents

Read the exact text each party signs, or relay a party's signature.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

GET /v1/escrow-deals/{ref}/actions

List the contract calls a party can sign now, or report a transaction you relayed.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/{ref}/transactions

List the contract calls a party can sign now, or report a transaction you relayed.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

GET /v1/escrow-deals/{ref}/evidence

Add or read evidence, or read the dispute with both sides' evidence and deadlines.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

POST /v1/escrow-deals/{ref}/evidence

Add or read evidence, or read the dispute with both sides' evidence and deadlines.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

PUT /v1/escrow-deals/{ref}/evidence/files

Add or read evidence, or read the dispute with both sides' evidence and deadlines.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

GET /v1/escrow-deals/{ref}/evidence/{evidence_id}/content

Add or read evidence, or read the dispute with both sides' evidence and deadlines.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

GET /v1/escrow-deals/{ref}/dispute

Add or read evidence, or read the dispute with both sides' evidence and deadlines.

HTTP Response
200 Success: the body follows this endpoint's schema. For a check, read the status axes and coverage.
401 API key is invalid or has been revoked.
403 Your organization or network does not have access.
404 Not found in your organization and environment. On /v1/check: the transaction was not found in the selected network or, without a network, in any network checked.
409 Conflict: the request is still running, its idempotency key was reused with a different body, or it conflicts with recorded state. Read error.code.
413 The request body is larger than this endpoint accepts.
415 This content type is not accepted here: JSON, or CSV for a batch upload.
422 Correct the request fields.
429 A rate, concurrency, monthly quota, daily unbilled-checks or active-batch limit was reached. Read error.code and Retry-After.
500 Internal error. Keep the X-Request-ID for support.
503 API access or a required service is unavailable.
504 The request exceeded the server time limit.

ActionRequest

Field Type / values Required
owner string / null No
action "CREDITED", "PAID", "HELD", "REJECTED", "REFUNDED", "ESCALATED", "RETRIED", "NO_ACTION", "OTHER" Yes
action_at string / null No

ActionView

Field Type / values Required
action string Yes
contract string Yes
function string Yes
parameters string Yes
data string Yes
fee_limit_sun integer Yes
call_value integer No
estimated_energy integer / null Yes
estimated_trx string / null Yes
note string / null No

ActionsResponse

Field Type / values Required
role "BUYER", "SELLER" Yes
address string Yes
state string / null Yes
status string Yes
actions ActionView[] Yes

AllocationRequest

Field Type / values Required
network "tron", "eth", "ton" Yes
tx_hash string Yes
event_ref string Yes
allocations AllocationShare[] Yes
owner string / null No

AllocationResponse

Field Type / values Required
allocation_id string Yes
external_reference string / null Yes
event EventResponse Yes
allocated_amount string Yes
status string Yes
source string Yes
instruction_version integer / null Yes
decision_id string / null Yes
created_at string Yes
reversed_at string / null Yes
reversal_reason string / null Yes

AllocationResultResponse

Field Type / values Required
allocations AllocationResponse[] Yes
obligations ObligationResponse[] Yes

AllocationShare

Field Type / values Required
external_reference string Yes
amount string Yes

AmountComparisonResponse

Field Type / values Required
expected string / null Yes
actual string / null Yes
result "EXACT", "WITHIN_TOLERANCE", "UNDERPAID", "OVERPAID", "NOT_EVALUATED", "INSUFFICIENT" Yes
tolerance string / null Yes
delta string / null Yes
delta_raw string / null Yes

AmountView

Field Type / values Required
value string Yes
minor string Yes
token string Yes
symbol string No
decimals integer Yes

AttemptRequest

Field Type / values Required
external_reference string Yes
attempt_reference string Yes
status "CREATED", "SUBMITTED", "BROADCAST", "TIMEOUT", "FAILED", "CANCELLED", "PROVIDER_CONFIRMED", "UNKNOWN" No
channel string / null No
provider_reference string / null No
amount string / null No
network "tron", "eth", "ton" / null No
tx_hash string / null No
instruction_version integer / null No
attempt_created_at string / null No

AttemptResponse

Field Type / values Required
attempt_reference string / null Yes
status "CREATED", "SUBMITTED", "BROADCAST", "TIMEOUT", "FAILED", "CANCELLED", "PROVIDER_CONFIRMED", "UNKNOWN" Yes
channel string / null Yes
provider_reference string / null Yes
amount string / null Yes
network string / null Yes
tx_hash string / null Yes
instruction_version integer / null Yes
attempt_created_at string Yes
updated_at string Yes
status_history object[] Yes
execution_confirmed boolean Yes
redacted boolean Yes

BalancesView

Field Type / values Required
accrued PartyMoney Yes
claimable PartyMoney Yes
withdrawn PartyMoney Yes
held_by_contract Money Yes

BatchListResponse

Field Type / values Required
batches BatchResponse[] Yes

BatchResponse

Field Type / values Required
batch_id string Yes
environment "live", "sandbox" Yes
status "PREFLIGHTED", "QUEUED", "RUNNING", "PAUSED", "CANCELLING", "COMPLETED", "CANCELLED", "FAILED" Yes
pause_reason string / null Yes
row_count integer Yes
progress object Yes
defaults object Yes
preflight object Yes
estimate object Yes
summary object / null Yes
created_at string Yes
started_at string / null Yes
completed_at string / null Yes

BatchRowResponse

Field Type / values Required
row_no integer Yes
kind "check", "obligation" Yes
state "PENDING", "CLAIMED", "DONE", "FAILED", "RETRYABLE", "SKIPPED_DUPLICATE", "SKIPPED_INVALID", "CANCELLED" Yes
attempt integer Yes
external_reference string / null Yes
network string / null Yes
tx_hash string / null Yes
errors string[] Yes
warnings string[] Yes
duplicate_of integer / null Yes
error_code string / null Yes
result object / null Yes
redacted boolean Yes

BatchRowsResponse

Field Type / values Required
batch_id string Yes
rows BatchRowResponse[] Yes
next_after integer / null Yes

CatalogItemResponse

Field Type / values Required
plan_code string Yes
plan_version integer Yes
name string Yes
environment "live", "sandbox" Yes
commercial_status "published", "custom" Yes
currency string Yes
monthly_price_minor integer / null Yes
included_checks integer / null Yes
overage_unit_price_minor integer / null Yes
package CatalogPackageTerms / null Yes

CatalogPackageTerms

Field Type / values Required
min_quantity integer Yes
max_quantity integer Yes
step integer Yes
expires_days integer / null Yes

CatalogResponse

Field Type / values Required
pricing_policy string Yes
revision integer Yes
currency string Yes
consumption_unit "transaction_check" No
renewal "manual" No
items CatalogItemResponse[] Yes
computed_at string Yes

Category

Field Type / values Required

CheckDetails

Field Type / values Required
request_id string Yes
tx_hash string Yes
network "tron", "eth", "ton" Yes
transaction_type string Yes
transaction_category "eth_transfer", "erc20_transfer", "ton_transfer", "jetton_transfer", "trx_transfer", "trc10_transfer", "trc20_transfer", "smart_contract_call", "smart_contract_deploy", "staking", "voting", "governance", "resource_operation", "account_operation", "exchange", "market", "permission_update", "other", "unknown" Yes
transaction_state "confirmed", "pending", "failed", "not_found", "unknown" Yes
confirmations integer / null Yes
required_confirmations integer Yes
sender string / null Yes
recipient string / null Yes
token_contract string / null Yes
token_symbol string / null Yes
decimals integer / null Yes
amount string / null Yes
transfers DetailedTransferResponse[] Yes
transfers_truncated boolean Yes
aml_risk_level "low", "moderate", "high", "severe", "unknown" Yes
schema_version "0.3" Yes
policy_version string Yes
sanctions_hit boolean / null Yes
direct_sanctions_match boolean / null Yes
indirect_sanctions_exposure boolean / null Yes
risk_exposure boolean / null Yes
key_exposure ExposureResponse[] Yes
token_authenticity "official", "fake", "unknown" Yes
recipient_match boolean / null Yes
token_match boolean / null Yes
amount_match boolean / null Yes
coverage "complete", "partial" Yes
workflow_coverage "complete", "partial" Yes
source_coverage SourceCoverageResponse Yes
freshness FreshnessResponse Yes
overall_state "CLEAR", "REVIEW", "HIGH_RISK", "LIMITED_DATA" Yes
environment "live", "sandbox" No
reasons ReasonResponse[] Yes
decision_id string / null Yes
direction "INCOMING", "OUTGOING" / null Yes
workflow_type "PAYOUT", "DEPOSIT", "INVOICE", "REFUND", "SETTLEMENT", "OTHER" / null Yes
versions VersionsResponse Yes
transaction_status "CONFIRMED", "PENDING", "FAILED", "NOT_FOUND", "INSUFFICIENT" Yes
integrity_status "MATCH", "MISMATCH", "AMBIGUOUS", "NOT_EVALUATED", "INSUFFICIENT" Yes
risk_status "HIGH_RISK", "REVIEW", "NO_MATERIAL_SIGNAL", "NOT_EVALUATED", "INSUFFICIENT" Yes
coverage_status "OK", "DEGRADED", "UNAVAILABLE", "DISAGREEMENT", "SKIPPED" Yes
status_drivers StatusDriversResponse Yes
result_final boolean Yes
verification_final boolean Yes
risk_final boolean Yes
risk_retryable boolean Yes
risk_retry_after_seconds integer / null Yes
retryable boolean Yes
retry_after_seconds integer / null Yes
recommended_action "PROCEED", "CONFIRM_INTERNAL_STATE", "MANUAL_REVIEW", "RECHECK_LATER", "NOT_EXECUTED", "HOLD_AND_ESCALATE" Yes
integrity IntegrityResponse Yes
risk RiskSummaryResponse Yes
evidence_items EvidenceItemResponse[] Yes
coverage_limitations CoverageLimitationsResponse Yes
obligation ObligationSummaryResponse / null No
reuse ReuseResponse / null No
selected_transfer_index integer / null Yes
finalized boolean Yes
finality_policy string Yes
trace_complete boolean / null Yes
evidence object Yes
limitations string[] Yes
checked_networks "tron", "eth", "ton"[] Yes

ConsentPackage

Field Type / values Required
version integer Yes
deal_id string Yes
message string Yes
params object Yes
terms object Yes
terms_canonical string Yes
terms_hash string Yes
agreement_expires_at string Yes
factory string Yes

ConsentRequest

Field Type / values Required
version integer Yes
role "BUYER", "SELLER" Yes
signature string Yes

ConsentStatus

Field Type / values Required
signed boolean Yes
method "SIGNATURE", "ONCHAIN_APPROVAL" / null Yes
at string / null Yes

ConsentsView

Field Type / values Required
buyer ConsentStatus Yes
seller ConsentStatus Yes

ContractView

Field Type / values Required
factory string Yes
escrow string / null Yes
deal_id string / null Yes
ruling_hash string / null Yes
observed_at string / null Yes
out_of_sync boolean Yes

CoverageLimitationsResponse

Field Type / values Required
runtime ReasonCode[] Yes
product ReasonCode[] Yes

CreateDealRequest

Field Type / values Required
buyer string Yes
seller string Yes
amount string Yes
category Category Yes
title string Yes
description string No
delivery DeliveryInput Yes
timeline TimelineInput Yes
resolver_enabled boolean No
resolver_id string / null No
resolver_fee string / null No
external_reference string Yes
obligation_reference string / null No

CustomerContext

Field Type / values Required
credit_status "NOT_CREDITED", "CREDITED", "PARTIALLY_CREDITED", "UNKNOWN" / null No
allocation_status "NOT_ALLOCATED", "PARTIALLY_ALLOCATED", "ALLOCATED", "UNKNOWN" / null No

DealListResponse

Field Type / values Required
deals DealResponse[] Yes
next_before string / null Yes

DealResponse

Field Type / values Required
id string Yes
external_reference string Yes
obligation_reference string / null Yes
environment "live", "sandbox" Yes
network string Yes
status string Yes
protocol_version 1, 2 Yes
state string / null Yes
settlement_reason string / null Yes
category string Yes
title string / null Yes
pricing_class string Yes
parties Parties Yes
resolver_enabled boolean Yes
resolver ResolverView / null Yes
amount AmountView Yes
fee FeeView Yes
terms TermsView Yes
timeline TimelineView Yes
settlement SettlementView / null Yes
funding FundingView / null Yes
contract ContractView Yes
balances BalancesView Yes
links Links Yes
created_at string / null Yes
updated_at string / null Yes
transactions TransactionView[] No
replayed boolean No

DecisionEventResponse

Field Type / values Required
kind "ACTION", "RESOLUTION", "OUTCOME" Yes
action string / null Yes
resolution string / null Yes
outcome_status string / null Yes
loss_prevented string / null Yes
owner string / null Yes
occurred_at string Yes
recorded_at string Yes

DecisionResponse

Field Type / values Required
decision_id string Yes
decided_at string Yes
channel string Yes
transaction_status string Yes
integrity_status string Yes
risk_status string Yes
coverage_status string Yes
overall_state string Yes
recommended_action string Yes
reuse_status string / null Yes
environment "live", "sandbox" Yes
network string Yes
external_reference string / null Yes
instruction_version integer / null Yes
selected_event_ref string / null Yes
result_final boolean Yes
verification_final boolean / null Yes
risk_final boolean / null Yes
retryable boolean Yes
status_drivers object Yes
reason_codes string[] Yes
versions object Yes
evidence_items object[] / null Yes
events DecisionEventResponse[] Yes

DecisionSummaryResponse

Field Type / values Required
decision_id string Yes
decided_at string Yes
channel string Yes
transaction_status string Yes
integrity_status string Yes
risk_status string Yes
coverage_status string Yes
overall_state string Yes
recommended_action string Yes
reuse_status string / null Yes

DeliveryInput

Field Type / values Required
criteria string[] Yes
method string Yes
evidence_required string[] No

DetailedTransferResponse

Field Type / values Required
index integer Yes
asset_type string Yes
sender string / null Yes
recipient string / null Yes
token_contract string / null Yes
token_id string / null Yes
token_symbol string / null Yes
decimals integer / null Yes
amount string / null Yes
event_ref string / null No
execution_status "success", "failed", "unknown" No
raw_amount string / null Yes

DisputeResponse

Field Type / values Required
state string / null Yes
disputed_at string / null Yes
resolve_deadline string / null Yes
resolver_enabled boolean Yes
deadlocked boolean Yes
buyer_proposal Money / null Yes
seller_proposal Money / null Yes
criteria string[] Yes
evidence EvidenceView[] Yes
ruling RulingView / null Yes

ErrorDetail

Field Type / values Required
code string Yes
message string Yes
retryable boolean Yes

ErrorResponse

Field Type / values Required
request_id string Yes
error ErrorDetail Yes

EventFeedResponse

Field Type / values Required
events EventView[] Yes
next_after integer Yes

EventResponse

Field Type / values Required
network string Yes
tx_hash string / null Yes
event_ref string Yes
asset_type string Yes
token_contract string / null Yes
sender string / null Yes
recipient string / null Yes
amount string / null Yes
execution_status string Yes
finalized boolean Yes
block_number integer / null Yes
block_time string / null Yes

EventView

Field Type / values Required
seq integer Yes
event_id string Yes
type string Yes
occurred_at string Yes
deal object Yes
data object Yes

EvidenceDisagreementResponse

Field Type / values Required
fields string[] Yes

EvidenceFreshnessResponse

Field Type / values Required
age_seconds integer / null Yes
stale boolean Yes
stale_after_seconds integer / null Yes

EvidenceItemResponse

Field Type / values Required
evidence_id string Yes
kind "transaction_facts", "aml_risk", "sanctions_list", "token_authenticity" Yes
source string Yes
subject EvidenceSubjectResponse Yes
source_reference string / null Yes
observed_at string / null Yes
effective_at string / null Yes
temporal_scope "TRANSACTION_TIME", "CHECK_TIME", "LIST_PUBLICATION" Yes
coverage "checked", "checked_stale", "checked_freshness_unknown", "not_checked", "not_applicable", "unavailable", "partial", "disagreement" Yes
freshness EvidenceFreshnessResponse Yes
dependency "required", "optional", "not_applicable" Yes
disagreement EvidenceDisagreementResponse / null Yes
evidence_version string Yes

EvidenceListResponse

Field Type / values Required
evidence EvidenceView[] Yes

EvidenceSubjectResponse

Field Type / values Required
type "transaction", "transaction_addresses", "token" Yes
ref string / null Yes

EvidenceView

Field Type / values Required
id string Yes
author_role string Yes
purpose string Yes
kind string Yes
terms_version integer Yes
file_name string / null Yes
media_type string / null Yes
size_bytes integer / null Yes
sha256 string / null Yes
note string / null Yes
supersedes string / null Yes
uploaded boolean Yes
content_deleted boolean Yes
created_at string Yes

ExecuteRequest

Field Type / values Required
confirm boolean Yes
allow_partial boolean No

ExposureResponse

Field Type / values Required
category string Yes
exposure_type string Yes
hops integer Yes
percent string / null Yes
source string / null No
grade "high", "review", "minor", "none" / null No

FeeView

Field Type / values Required
value string Yes
minor string Yes
rule "seller-share-v1" Yes

FieldComparisonResponse

Field Type / values Required
expected string / null Yes
actual string / null Yes
result "MATCH", "MISMATCH", "NOT_EVALUATED", "INSUFFICIENT" Yes

FreshnessResponse

Field Type / values Required
checked_at string Yes
transaction_at string / null Yes
risk_data_at string / null Yes
token_data_at string / null Yes
max_source_age_seconds integer Yes

FundingView

Field Type / values Required
received Money Yes
remaining Money Yes

InstructionResponse

Field Type / values Required
version integer Yes
network string Yes
recipient string / null Yes
sender string / null Yes
token_contract string / null Yes
amount string / null Yes
amount_tolerance string / null Yes
effective_at string / null Yes
approval_reference string / null Yes
source string Yes
history_complete boolean Yes
created_at string Yes
redacted boolean Yes

IntegrityComparisonsResponse

Field Type / values Required
recipient FieldComparisonResponse / null Yes
sender FieldComparisonResponse / null Yes
token FieldComparisonResponse / null Yes
amount AmountComparisonResponse / null Yes
network FieldComparisonResponse Yes

IntegrityResponse

Field Type / values Required
status "MATCH", "MISMATCH", "AMBIGUOUS", "NOT_EVALUATED", "INSUFFICIENT" Yes
selected_event_ref string / null Yes
relevant_event_count integer Yes
comparisons IntegrityComparisonsResponse Yes
Field Type / values Required
buyer string Yes
seller string Yes
telegram_invite string / null Yes

LookupRequest

Field Type / values Required
external_reference string Yes

ModuleStatusResponse

Field Type / values Required
key "sanctions_aml", "wallet_behaviour", "token_authenticity", "contract_controls", "address_security", "counterparties_services", "flows_connections" Yes
status "RISK_DETECTED", "NO_MATCHES_FOUND", "PARTIALLY_CHECKED", "INSUFFICIENT_DATA", "NOT_SUPPORTED", "NOT_CHECKED" Yes
source_coverage "complete", "partial", "none", "not_applicable" Yes

Money

Field Type / values Required
value string Yes
minor string Yes

NetworkErrorDetail

Field Type / values Required
code string Yes
message string Yes
retryable boolean Yes
required_field "network" Yes
candidates "tron", "eth", "ton"[] Yes
checked_networks "tron", "eth", "ton"[] Yes

NetworkInputErrorResponse

Field Type / values Required
request_id string Yes
network null No
state "insufficient_input" No
error NetworkErrorDetail Yes

NewVersionRequest

Field Type / values Required
buyer string Yes
seller string Yes
amount string Yes
category Category Yes
title string Yes
description string No
delivery DeliveryInput Yes
timeline TimelineInput Yes
resolver_enabled boolean No
resolver_id string / null No
resolver_fee string / null No
expected_version integer Yes

NobodyActs

Field Type / values Required
at string / null Yes
outcome "EXPIRED", "REFUNDED", "RELEASED", "DEADLOCK", "AGREEMENT_EXPIRED" Yes
claim_by "BUYER", "SELLER", "ANYONE" / null Yes

NoteRequest

Field Type / values Required
purpose "DELIVERY", "DISPUTE", "RESPONSE", "OTHER" Yes
note string Yes
supersedes string / null No

ObligationLookupRequest

Field Type / values Required
external_reference string Yes

ObligationRequest

Field Type / values Required
network "tron", "eth", "ton" Yes
expected_recipient string / null No
expected_token string / null No
expected_amount string / null No
expected_sender string / null No
amount_tolerance string / null No
external_reference string Yes
direction "INCOMING", "OUTGOING" / null No
workflow_type "PAYOUT", "DEPOSIT", "INVOICE", "REFUND", "SETTLEMENT", "OTHER" / null No
instruction_version integer / null No
instruction_effective_at string / null No
approval_reference string / null No

ObligationResponse

Field Type / values Required
external_reference string / null Yes
environment "live", "sandbox" Yes
workflow_type string / null Yes
direction string / null Yes
status string Yes
current_instruction_version integer / null Yes
expected_amount string / null Yes
allocated_amount string Yes
residual_amount string / null Yes
pending_events integer Yes
instructions InstructionResponse[] Yes
attempts AttemptResponse[] Yes
allocations AllocationResponse[] Yes
decisions DecisionSummaryResponse[] Yes
created_at string Yes
redacted boolean Yes

ObligationSummaryResponse

Field Type / values Required
external_reference string / null Yes
workflow_type string / null Yes
direction string / null Yes
instruction_version integer / null Yes
status "OPEN", "PARTIALLY_ALLOCATED", "ALLOCATED", "OVER_ALLOCATED", "CANCELLED" Yes
expected_amount string / null Yes
allocated_amount string Yes
residual_amount string / null Yes
attempts integer Yes
events integer Yes
pending_events integer Yes
redacted boolean No

OutcomeRequest

Field Type / values Required
owner string / null No
outcome_status "CONFIRMED", "DISPUTED", "UNRESOLVED", "UNKNOWN" Yes
loss_prevented "CONFIRMED", "NOT_CONFIRMED", "UNKNOWN" No
outcome_at string / null No

Parties

Field Type / values Required
buyer string Yes
seller string Yes

PartyMoney

Field Type / values Required
buyer Money Yes
seller Money Yes
fee Money Yes
resolver Money Yes

ReasonCode

Field Type / values Required

ReasonResponse

Field Type / values Required
code ReasonCode Yes
category "transaction", "risk", "sanctions", "token", "expected", "integrity", "ledger", "context" Yes
severity "info", "warning", "high", "critical" Yes

RedeliveryResponse

Field Type / values Required
delivery_id string Yes
status string Yes

ResolutionRequest

Field Type / values Required
owner string / null No
resolution "ACCEPTED", "ACCEPTED_AFTER_REVIEW", "REJECTED", "REFUNDED", "CORRECTED", "WRITTEN_OFF", "OTHER" Yes
resolved_at string / null No

ResolverListResponse

Field Type / values Required
resolvers ResolverResponse[] Yes

ResolverRegistration

Field Type / values Required
address string Yes
name string Yes
affiliation "PLATFORM", "INDEPENDENT" Yes
rules_version string Yes
rules_sha256 string Yes

ResolverResponse

Field Type / values Required
id string Yes
network string Yes
address string Yes
name string Yes
affiliation "PLATFORM", "INDEPENDENT" Yes
rules_version string Yes
rules_sha256 string Yes
status "ACTIVE", "DISABLED" Yes
owned boolean Yes
key_scheme "SINGLE_KEY", "MULTISIG_2_OF_3", "UNSUPPORTED" / null Yes
signatures_required integer / null Yes
checked_at string / null Yes
eligible boolean Yes
ineligible_reason string / null Yes

ResolverView

Field Type / values Required
id string Yes
name string Yes
address string Yes
affiliation "PLATFORM", "INDEPENDENT" Yes
rules_version string Yes
rules_sha256 string Yes
fee Money Yes
key_scheme "SINGLE_KEY", "MULTISIG_2_OF_3", "UNSUPPORTED" / null Yes
signatures_required integer / null Yes
verified boolean Yes
registered_by "PLATFORM", "OPERATOR" Yes

ReuseResponse

Field Type / values Required
status "FIRST_SEEN", "REPEAT_CHECK", "SAME_OBLIGATION", "DECLARED_SHARED", "OTHER_OBLIGATION" Yes
other_obligations integer Yes
prior_decisions integer Yes
first_seen_at string / null Yes

ReversalRequest

Field Type / values Required
reason "CUSTOMER_CORRECTION", "DISPUTED", "DUPLICATE_RECORD", "OTHER" Yes
owner string / null No

RiskSummaryResponse

Field Type / values Required
status "HIGH_RISK", "REVIEW", "NO_MATERIAL_SIGNAL", "NOT_EVALUATED", "INSUFFICIENT" Yes
driver ReasonCode / null Yes
driver_evidence_ids string[] Yes

RulingView

Field Type / values Required
buyer_amount Money / null Yes
rationale string Yes
ruling_hash string Yes
status string Yes
txid string / null Yes

SettlementView

Field Type / values Required
buyer_proposal Money / null Yes
seller_proposal Money / null Yes
deadlocked boolean Yes

SimpleCheckRequest

Field Type / values Required
network "tron", "eth", "ton" / null No
expected_recipient string / null No
expected_token string / null No
expected_amount string / null No
expected_sender string / null No
amount_tolerance string / null No
tx_hash string Yes

SimpleCheckResponse

Field Type / values Required
request_id string Yes
network "tron", "eth", "ton" Yes
state "clear", "review", "high_risk", "limited_data" Yes
risk_level "low", "moderate", "high", "severe", "unknown" Yes
coverage "complete", "partial" Yes
reason_codes string[] Yes
environment "live", "sandbox" No
details CheckDetails Yes

SourceCoverageResponse

Field Type / values Required
sanctions_sources object Yes
transaction_sources object Yes
risk_sources object Yes
universal_clearance false No

SourceObservationResponse

Field Type / values Required
status "checked", "checked_stale", "checked_freshness_unknown", "not_checked", "not_applicable", "unavailable", "partial", "disagreement" Yes
checked_at string / null Yes
retrieved_at string / null Yes
data_updated_at string / null Yes
stale_after_seconds integer / null Yes
scope string Yes
error_code string / null Yes

StatusDriversResponse

Field Type / values Required
transaction ReasonCode / null Yes
integrity ReasonCode / null Yes
risk ReasonCode / null Yes
coverage ReasonCode / null Yes
action ReasonCode / null Yes

TermsView

Field Type / values Required
version integer Yes
agreed_version integer / null Yes
terms_hash string / null Yes
deal_id string / null Yes
agreement_expires_at string / null Yes
consents ConsentsView Yes

TimelineInput

Field Type / values Required
agreement_window_seconds integer / null No
funding_window_seconds integer / null No
delivery_period_seconds integer Yes
inspection_period_seconds integer Yes
resolve_period_seconds integer / null No

TimelineView

Field Type / values Required
funding_deadline string / null Yes
delivery_period_seconds integer / null Yes
inspection_period_seconds integer / null Yes
resolve_period_seconds integer / null Yes
funded_at string / null Yes
delivered_at string / null Yes
disputed_at string / null Yes
settled_at string / null Yes
current_deadline string / null Yes
resolve_deadline string / null Yes
if_nobody_acts NobodyActs / null Yes

TransactionAccepted

Field Type / values Required
txid string Yes
status string Yes

TransactionCheckRequest

Field Type / values Required
network "tron", "eth", "ton" Yes
expected_recipient string / null No
expected_token string / null No
expected_amount string / null No
expected_sender string / null No
amount_tolerance string / null No
tx_hash string Yes
direction "INCOMING", "OUTGOING" / null No
workflow_type "PAYOUT", "DEPOSIT", "INVOICE", "REFUND", "SETTLEMENT", "OTHER" / null No
external_reference string / null No
instruction_version integer / null No
instruction_effective_at string / null No
approval_reference string / null No
attempt_reference string / null No
customer_context CustomerContext / null No

TransactionCheckResponse

Field Type / values Required
request_id string Yes
tx_hash string Yes
network "tron", "eth", "ton" Yes
transaction_type string Yes
transaction_category "eth_transfer", "erc20_transfer", "ton_transfer", "jetton_transfer", "trx_transfer", "trc10_transfer", "trc20_transfer", "smart_contract_call", "smart_contract_deploy", "staking", "voting", "governance", "resource_operation", "account_operation", "exchange", "market", "permission_update", "other", "unknown" Yes
transaction_state "confirmed", "pending", "failed", "not_found", "unknown" Yes
confirmations integer / null Yes
required_confirmations integer Yes
sender string / null Yes
recipient string / null Yes
token_contract string / null Yes
token_symbol string / null Yes
decimals integer / null Yes
amount string / null Yes
transfers TransferResponse[] Yes
transfers_truncated boolean Yes
aml_risk_level "low", "moderate", "high", "severe", "unknown" Yes
schema_version "0.3" Yes
policy_version string Yes
sanctions_hit boolean / null Yes
direct_sanctions_match boolean / null Yes
indirect_sanctions_exposure boolean / null Yes
risk_exposure boolean / null Yes
key_exposure ExposureResponse[] Yes
token_authenticity "official", "fake", "unknown" Yes
recipient_match boolean / null Yes
token_match boolean / null Yes
amount_match boolean / null Yes
coverage "complete", "partial" Yes
workflow_coverage "complete", "partial" Yes
source_coverage SourceCoverageResponse Yes
freshness FreshnessResponse Yes
overall_state "CLEAR", "REVIEW", "HIGH_RISK", "LIMITED_DATA" Yes
environment "live", "sandbox" No
reasons ReasonResponse[] Yes
decision_id string / null Yes
direction "INCOMING", "OUTGOING" / null Yes
workflow_type "PAYOUT", "DEPOSIT", "INVOICE", "REFUND", "SETTLEMENT", "OTHER" / null Yes
versions VersionsResponse Yes
transaction_status "CONFIRMED", "PENDING", "FAILED", "NOT_FOUND", "INSUFFICIENT" Yes
integrity_status "MATCH", "MISMATCH", "AMBIGUOUS", "NOT_EVALUATED", "INSUFFICIENT" Yes
risk_status "HIGH_RISK", "REVIEW", "NO_MATERIAL_SIGNAL", "NOT_EVALUATED", "INSUFFICIENT" Yes
coverage_status "OK", "DEGRADED", "UNAVAILABLE", "DISAGREEMENT", "SKIPPED" Yes
status_drivers StatusDriversResponse Yes
result_final boolean Yes
verification_final boolean Yes
risk_final boolean Yes
risk_retryable boolean Yes
risk_retry_after_seconds integer / null Yes
retryable boolean Yes
retry_after_seconds integer / null Yes
recommended_action "PROCEED", "CONFIRM_INTERNAL_STATE", "MANUAL_REVIEW", "RECHECK_LATER", "NOT_EXECUTED", "HOLD_AND_ESCALATE" Yes
integrity IntegrityResponse Yes
risk RiskSummaryResponse Yes
evidence_items EvidenceItemResponse[] Yes
coverage_limitations CoverageLimitationsResponse Yes
obligation ObligationSummaryResponse / null No
reuse ReuseResponse / null No

TransactionReport

Field Type / values Required
role "BUYER", "SELLER" Yes
action "APPROVE_TERMS", "TRANSFER_FUNDS", "CONFIRM_FUNDING", "MARK_DELIVERED", "RELEASE", "REFUND", "CLAIM_REFUND", "CLAIM_RELEASE", "DISPUTE", "PROPOSE_SETTLEMENT", "WITHDRAW", "EXPIRE", "SWEEP_EXCESS" Yes
txid string Yes

TransactionView

Field Type / values Required
purpose string Yes
txid string Yes
status string Yes
signer_role string Yes
block_number integer / null Yes
confirmed_at string / null Yes

TransferResponse

Field Type / values Required
index integer Yes
asset_type string Yes
sender string / null Yes
recipient string / null Yes
token_contract string / null Yes
token_id string / null Yes
token_symbol string / null Yes
decimals integer / null Yes
amount string / null Yes
event_ref string / null No
execution_status "success", "failed", "unknown" No

UsageBalanceResponse

Field Type / values Required
granted integer Yes
used integer Yes
reserved integer Yes
available integer Yes

UsageBalancesResponse

Field Type / values Required
included UsageBalanceResponse Yes
extra UsageExtraBalanceResponse Yes
total_available integer Yes
computed_at string Yes

UsageExtraBalanceResponse

Field Type / values Required
granted integer Yes
used integer Yes
reserved integer Yes
available integer Yes
expires_at string / null Yes

UsageLimitsResponse

Field Type / values Required
rate_limit_per_minute integer Yes
max_concurrent_requests integer Yes
enabled_networks string[] Yes

UsagePeriodResponse

Field Type / values Required
start string Yes
end string Yes
used integer Yes
reserved integer Yes
limit integer Yes
remaining integer Yes

UsagePlanResponse

Field Type / values Required
code string Yes
version integer Yes
name string Yes

UsageRecentResponse

Field Type / values Required
days integer Yes
requests integer Yes
billable integer Yes
limited_data integer Yes
errors integer Yes
average_latency_ms integer / null Yes
by_network object Yes
by_overall_state object Yes
by_kind object No

UsageResponse

Field Type / values Required
tenant_id string Yes
environment "live", "sandbox" No
plan UsagePlanResponse / null Yes
limits UsageLimitsResponse Yes
period UsagePeriodResponse Yes
balances UsageBalancesResponse Yes
subscription UsageSubscriptionResponse Yes
recent UsageRecentResponse Yes

UsageSubscriptionResponse

Field Type / values Required
status "legacy", "active", "scheduled", "lapsed", "none" Yes
plan_code string / null Yes
plan_version integer / null Yes
period_start string / null Yes
period_end string / null Yes
next_period_start string / null No
renewal "manual" No
cancel_at_period_end boolean No

VersionsResponse

Field Type / values Required
decision string Yes
rules object Yes
reason_codes string Yes
evidence_schema string Yes
contract string Yes

WalletAddressSecurityResponse

Field Type / values Required
denylisted boolean / null Yes
denylist_category string / null Yes
provider_flagged boolean / null Yes
provider_flags string[] Yes
issuer_blocked boolean / null Yes
issuer_blocked_symbols string[] Yes

WalletBehaviourResponse

Field Type / values Required
is_contract boolean / null Yes
contract_verified boolean / null Yes
transaction_count integer / null Yes
transaction_count_limited boolean Yes
wallet_age_days integer / null Yes
first_activity_at string / null Yes
last_activity_at string / null Yes
counterparties_count integer / null Yes

WalletCheckDetails

Field Type / values Required
schema_version "0.1" No
overall_state "CLEAR", "REVIEW", "HIGH_RISK", "LIMITED_DATA" Yes
data_coverage "FULL", "PARTIAL", "INSUFFICIENT" Yes
coverage_scope "supported_deep_sources" No
modules ModuleStatusResponse[] Yes
sanctions WalletSanctionsResponse Yes
address_security WalletAddressSecurityResponse Yes
behaviour WalletBehaviourResponse Yes
counterparties WalletCounterpartiesResponse Yes
token_authenticity WalletTokenAuthenticityResponse Yes
freshness WalletFreshnessResponse Yes
source_coverage object Yes
universal_clearance false No
reasons WalletReasonResponse[] Yes
limitations string[] Yes
evidence WalletEvidenceResponse Yes

WalletCheckRequest

Field Type / values Required
address string Yes
network "tron", "eth", "ton" / null No

WalletCheckResponse

Field Type / values Required
request_id string Yes
address string Yes
network "tron", "eth", "ton" Yes
state "clear", "review", "high_risk", "limited_data" Yes
coverage "complete", "partial" Yes
reason_codes string[] Yes
environment "live", "sandbox" No
details WalletCheckDetails Yes

WalletCounterpartiesResponse

Field Type / values Required
screened integer Yes
unverified integer Yes
flagged integer Yes

WalletEvidenceResponse

Field Type / values Required
live boolean Yes
source "live", "sandbox" Yes

WalletFreshnessResponse

Field Type / values Required
checked_at string Yes
analyzed_at string / null Yes
served_from_analysis_cache boolean Yes
oldest_source_age_seconds integer / null Yes

WalletReasonResponse

Field Type / values Required
code string Yes
category "sanctions", "risk", "address", "token", "counterparties", "behaviour", "coverage" Yes
severity "info", "warning", "high", "critical" Yes

WalletSanctionsResponse

Field Type / values Required
direct_match boolean / null Yes
authorities string[] Yes
attributed boolean Yes
indirect_exposure boolean / null Yes
exposure_source "aml_provider" / null Yes

WalletTokenAuthenticityResponse

Field Type / values Required
total integer Yes
official integer Yes
fake integer Yes
suspicious integer Yes
unknown integer Yes

WebhookRequest

Field Type / values Required
url string Yes

WebhookResponse

Field Type / values Required
url string / null Yes
status string / null Yes
secret string / null No
consecutive_failures integer No

Changelog and versioning

2026-10-15: corrections, same policy

The contract stays 0.3 and policy b2b-tx-2026-10.4. Stored replies replay unchanged.

  • TRON transfer lists cut short. When both sources list more transfers than ChainZap reads (100), the two lists are no longer compared row by row: each source keeps its own first rows. A list cut on one side only is still a disagreement. The gap stays TRANSFERS_TRUNCATED, so such a check can become final as LIMITED_DATA instead of asking for a recheck indefinitely.
  • Provider rights. A source with no rights on record, or one whose rights keep it internal, shows neither its own evidence reference nor its own exposure fields. No current answer changes.
  • Examples. List sources in the examples read as in a live answer, including un and a checked list's freshness; the reference example carries the reuse object a recorded check has.
  • Docs. Which sandbox errors carry environment; which disagreements make transaction_status INSUFFICIENT; CONFIRM_INTERNAL_STATE closes a payout only with a MATCH, and a payout reaches PROCEED only with the obligation's external_reference in the check (or customer_context.credit_status: "NOT_CREDITED"); ATTEMPT_MAY_STILL_LAND needs direction: "OUTGOING" in the request; spend_cap is also reported while spend cannot be accounted; comparison codes are withheld while transaction_status is PENDING, and a check whose sources disagree about the transaction itself is not final but can still carry them.

2026-10-14: reference corrections, same policy

The contract stays 0.3 and policy b2b-tx-2026-10.4; no decision rule changed.

  • Error codes. The OpenAPI description of POST /v1/transaction/check now lists every error code the check can return: ledger_not_enabled (403), instruction_conflict, instruction_version_required and attempt_conflict (409), invalid_amount_tolerance and invalid_reference (422). The codes and their meaning are unchanged; see errors.
  • Examples. The response example in the reference lists both chain sources and the AML source with the statuses a live answer reports, one evidence item per source, and the reasons a live answer gives. Every example shows timestamps as the API returns them (…Z).
  • Docs. risk_final is false while a transaction is pending or not found, whatever risk_status says. On MANUAL_REVIEW, status_drivers.action names one finding: TX_REVERTED on a failed transaction, otherwise the most severe, with a risk-data gap or risk signal before the payment comparison at equal severity. Read status_drivers.integrity for the payment comparison. On the AML source, checked_freshness_unknown is the normal status of a completed screen.

2026-10-14: policy b2b-tx-2026-10.4

Integrity rule int-v3. The contract stays 0.3; stored replies replay unchanged under the policy they were answered with.

  • Pending is not compared. While a transaction is pending (transaction_status: PENDING), no EXPECTED_*_MISMATCH reason is reported, as integrity is already NOT_EVALUATED with null match flags. Terms that differ are reported once the transaction is final.
  • Do not pay again. ATTEMPT_MAY_STILL_LAND (a check sent with direction: "OUTGOING" whose transaction is not found) is always kept in the short reasons list, whatever else is found.
  • Docs. Every organization has a daily spend cap on paid sources, and spend_cap / daily_cap reset at 00:00 UTC; the daily ceiling of unbilled checks is 500 by default; quota headers count the check being answered and replays carry none; the sandbox marker on /v1/transaction/check is environment; the failed sandbox fixture is charged; on 0.3 decide on the axes, not on overall_state; how a payout reaches PROCEED; expected_token everywhere; HTTP status descriptions in the reference are endpoint-neutral and list 413 and 415.

2026-10-14: corrections, same contract

The contract stays 0.3 and policy b2b-tx-2026-10.3; no decision rule changed. Stored replies replay unchanged.

  • Wallet check. address_security.provider_flagged says whether the address-security provider flagged the address (null when it did not answer). The provider's own labels are no longer returned: the deprecated provider_flags is ["PROVIDER_SECURITY_FLAG"] or empty. When the provider's rights do not let ChainZap show the flag, it is null with the limitation address_security_provider_withheld. State and reasons do not change.
  • TRON time. transaction_at, evidence effective_at and, for payments the ledger records from now on, the payment time are the block's time, never the timestamp the sender wrote into the transaction. Payments already in the ledger keep the time first recorded. A transaction not yet in a block has transaction_at: null.
  • TRON sources. Two sources that list the same transfers in a different order no longer read as a disagreement.
  • Ledger. A reversal waits for a check of the same payment and is never undone by it; under contention it answers 504 request_timeout with Retry-After, nothing stored. Ledger operations now list that 504.
  • Contract. /v1/openapi.json is now identical to the published contract, and GET /v1/batches/{batch_id}/export.csv is described as CSV only.
  • Batches. The guide now says that a row whose only gap is risk is DONE with risk_retryable: true.
  • Provider rights. An exposure row from a source without rights on record shows none of that source's own fields.

2026-10-10: contract 0.3 finality correction

Policy b2b-tx-2026-10.3 separates payment verification from risk/KYT. verification_final and its alias result_final describe verification; risk_final, risk_retryable and risk_retry_after_seconds describe the risk assessment. A final expected match remains MATCH when KYT is unavailable, with risk_status: INSUFFICIENT and MANUAL_REVIEW. Pending now returns NOT_EVALUATED in integrity and every requested comparison, with null match flags. retryable only requests a new verification observation. Update clients that used it as a combined verification/KYT flag.

The contract stays 0.3. Stored replies and decisions retain their original policy and meaning; a new observation with a new key uses the corrected policy. Historical decision records have null separate finality fields. The quickstart uses the canonical endpoint; all response examples are rendered by the same engine as live checks.

2026-10-05: contract 0.3

POST /v1/transaction/check answers carry schema_version: "0.3" under policy b2b-tx-2026-10.2. Every 0.2 field keeps its meaning; everything below is added.

  • Status axes. transaction_status, integrity_status, risk_status and coverage_status with status_drivers, result_final, retryable, retry_after_seconds and recommended_action. See status axes.
  • Structured comparison. integrity with per-field comparisons, the selected event_ref and the number of relevant transfers; transfers[].event_ref and execution_status; expected_sender and amount_tolerance in the request. Several transfers to the payee add ADDITIONAL_RELEVANT_TRANSFERS or SPLIT_WITHIN_TX. See matching.
  • Evidence and versions. evidence_items, coverage_limitations (runtime, product), risk with its driver and evidence, a grade on every key_exposure row, and versions.
  • Obligation ledger. external_reference, instruction_version, instruction_effective_at, approval_reference, attempt_reference, direction, workflow_type and customer_context (credit_status, allocation_status) in the request, obligation and reuse in the answer, and the /v1/obligations (with POST /v1/obligations/lookup, so no URL carries a reference), /v1/allocations and /v1/decisions endpoints. See obligations and decisions.
  • Batches. /v1/batches checks a CSV file of up to 200 payments with a free preflight and an estimate, and the developer dashboard has a Batches page. See batches.
  • Exposure policy b2b-exp-v2. A provider category ChainZap does not recognise is graded like other risk categories and named (UNRECOGNIZED_PROVIDER_LABEL) instead of being ignored.
  • Decision id. decision_id identifies the answer; request_id stays yours.
  • Fixes. A reverted transfer no longer reads as a match: its *_match fields are null, and a failed transaction in which nothing executed is NOT_EVALUATED with NOT_EXECUTED. The short reason list keeps the comparison and client-context reasons and never exceeds five. Ethereum source disagreements on a transaction's existence or status leave it INSUFFICIENT.
  • Sandbox. Nibble d is now a failed transaction; c and f stay CLEAR.
  • Same-day refinements. A failed transaction in which a transfer executed is integrity_status: INSUFFICIENT, never MATCH. A check without direction takes it from the obligation or from workflow_type, and a payment of unknown direction is never PROCEED. Several transfers to the payee that cannot be told apart always name AMBIGUOUS_TRANSFER. A check that names an attempt_reference recorded with another transaction is refused with 409 attempt_conflict. Ledger reads have their own rate bucket. An exposure row whose share its source does not allow showing has percent: null.

POST /v1/check returns the same assessment under details. Idempotency keys used before this change keep returning their stored 0.2 answers.

2026-10-03: contract 0.2

POST /v1/transaction/check answers carry schema_version: "0.2" and policy_version, the decision policy that produced the answer. What changed in that policy:

  • Provider-reported exposure moves the state by hops and share instead of by presence: sanctions exposure gives HIGH_RISK only at 1 hop or closer and at least 5%, REVIEW when it needs a look, and SANCTIONS_EXPOSURE_MINOR otherwise; other categories give REVIEW only up to 2 hops with at least 1% or an unknown share, or on a hacking event (RISK_EXPOSURE_MINOR otherwise). Every row stays in key_exposure. See coverage.
  • A sanctions list past its freshness window gives SANCTIONS_DATA_STALE and LIMITED_DATA; the UN list is not_applicable.
  • Only final answers are charged: a pending transaction is free.
  • The short reason list always keeps the reason that decided the state and every EXPECTED_* reason.
  • An expected_amount with zeros beyond the token's decimals is accepted.
  • An oversized body answers 413 payload_too_large instead of 422.
  • Pilot organizations must send all three expected_* fields (422 expected_fields_required) for official USDT on TRON (422 token_not_enabled); wallet checks are outside the pilot (403 check_kind_not_enabled).
  • Sandbox nibble e is now a pending transaction, and the sandbox no longer claims to pick a network from the hash shape.

Idempotency keys used before this change return 409 idempotency_conflict.

2026-10-02: payment through OxaPay

Plans and extra checks are paid in cryptocurrency through OxaPay: the order links to an OxaPay payment page for the exact US dollar total. Crypto Pay and direct TON transfers are no longer offered; orders placed with them keep their history. The checkout no longer asks for an asset. See plans and payment.

2026-09-23: neutral source names

Every data source in a check response is now named by its role, never by the vendor behind it. source_coverage.risk_sources carries aml_provider; transaction_sources keys are chain_explorer, chain_node and chain_node_secondary; the wallet check's source_coverage uses aml_provider and address_security_provider, sanctions.exposure_source is aml_provider, and the AML limitations are aml_provider_unavailable and aml_provider_skipped_dominant_local_hit. Status vocabularies, states and reason codes are unchanged. Integrations that keyed on earlier vendor-specific source names must read sources by role; those names are not returned.

2026-09-21: wallet checks

POST /v1/wallet/check screens one wallet address under the same key, quota and idempotency rules as the transaction check. The network is read from the address format. The response is a state, a coverage level and reason codes with per-module facts; no numeric score is published. A wallet check is billed when a state is produced; limited_data is free. GET /v1/usage adds recent.by_kind. The sandbox gains pinned wallet fixtures and the Playground gains a wallet mode. See wallet check.

2026-09-10: self-serve sign-in

Anyone can sign in to the developer dashboard with a Google account; an organization is created automatically and the first member becomes its owner. An invitation code is optional and only joins an existing organization. TRON and TON mainnet checks are live for new organizations; Ethereum checks are activated by support. Plans and extra checks are bought on the Plan and billing page.

2026-09-08: beta pricing, purchases and sandbox

GET /v1/catalog publishes the BETA_PRICING_V0 plans. GET /v1/usage adds balances, subscription and environment; period.remaining keeps its meaning. Check responses add environment and the X-Quota-Available header. Plans and extra checks packages are bought in the developer dashboard with Crypto Pay or a direct TON transfer, with manual renewal; see plans and payment. Sandbox keys (cz_test_) return deterministic test results without provider calls; see sandbox. Owner, admin and billing members can buy in the dashboard.

2026-09-07: three networks and a simpler check

POST /v1/check accepts a transaction hash with optional network context. It adds conservative network discovery, a short assessment and full evidence in details. TRON, Ethereum mainnet and TON mainnet have separate facts and finality policies. The original check endpoint and the usage endpoint remain available under the same organization limits and idempotency rules.

Unclassified TON contract value now requires partial coverage, including when other transfers are recognized or facts come from cache. Known high risks remain visible.

Ethereum checks are activated per organization by support after live provider acceptance; check service status for the current adapter state.

v1.0.0

TRON transaction checks and organization usage reporting. The developer dashboard provides API key management, plan details and a Playground page for testing requests within your organization.

Ethereum and TON are accepted by the schema; their adapters require separate activation and live acceptance. No wallet API, webhooks, monitoring or payment execution is part of this version.

Contract evolution

The public endpoint prefix is /v1. This documentation is versioned at /docs/v1/. The downloadable OpenAPI is generated from the same request and response models as the API and checked against a committed snapshot in CI.

Integrations should tolerate additional response fields while validating the fields they consume. Do not assume that an unknown reason code means a clean result. Breaking changes require a versioned migration guide; documentation links for the existing major version remain stable.

Service status and support

Access status

The public status indicator reports whether business access is enabled. It does not run a live provider check and is not an uptime guarantee. An available network describes supported functionality; an individual organization may have narrower entitlements.

A 503 api_disabled response means business checks have not been enabled or have been deliberately paused. A 503 provider_unavailable response means the required transaction data could not be retrieved for that request. Preserve the request ID and follow the retry guidance.

Contact

Contact the ChainZap team for Ethereum activation, Scale plan terms, incident questions and support. Include X-Request-ID, the public error code and the UTC time. Never include API keys, identity tokens or raw transaction or provider payloads.

Networks: TRON mainnet is live for every organization and is the scope of the design-partner pilot (USDT TRC-20). TON mainnet is a preview: it answers, but a TON check of an executed transaction currently returns LIMITED_DATA (not charged) because no configured TON sanctions list holds addresses, and it is outside the pilot. Ethereum is implemented and activated per organization by support. /status.json reports configured adapter activation separately from global API access, without live provider probes.

API access

Loading…

Overview