Build against proof.
Evaluate a proposed agent payment, observe what actually settled, and get a signed record either side can verify independently — across HTTP, MCP, and a TypeScript SDK. No accounts, no API keys.
1 · Agent payments
Overview
OnchainDiligence is a pay-per-call HTTP API. A client calls an endpoint, receives a 402 Payment Required challenge, pays a few cents of stablecoin, and retries the request with proof of payment attached. The server verifies payment, runs the check against live data or an on-chain observation, and returns a signed result.
Each response carries an Ed25519 signature over the data and the signer’s timestamp assertion. Store the response with independently trusted key records to verify it later without contacting this service. A separately verified anchor is required for an external time bound.
- Sanctions, name, and company data come from public sources documented under Evidence Providers below — they are inputs this infrastructure signs and delivers, not the product itself.
- Action and provider evidence (allowance, swap, bridge, staking, and commerce-executor settlement) independently observe what an agent's payment actually did on-chain, separate from any provider's own claim.
- The fee covers infrastructure only — not the underlying data, which is free and public where sourced externally.
Base URL
All API requests go to the api subdomain over HTTPS:
https://api.onchaindiligence.com
The bare domain onchaindiligence.com serves this site, not the API — point integrations at api.onchaindiligence.com.
Quickstart
See a live 402 challenge right now — no wallet, no key, just curl:
# returns 402 Payment Required with the MPP challenge curl -i https://api.onchaindiligence.com/screen/0x7f268357A8c2552623316e2562D90e642bB538E5
To run the check and get a signed result, the mppx CLI handles the entire challenge-pay-retry flow for you. Set the private key of a Tempo wallet holding pathUSD, then call any endpoint:
# key of a wallet holding a little pathUSD on Tempo export MPPX_PRIVATE_KEY=0xYOUR_KEY npx mppx https://api.onchaindiligence.com/screen/0x7f268357A8c2552623316e2562D90e642bB538E5
The CLI receives the 402, builds and submits the payment on Tempo, and prints the signed JSON result. To call the API from your own code instead, see how payment works below.
Command line
The @onchaindiligence/cli package is the fastest way to try the API. Some commands are free and need no key — run them straight from npx. --trust/--fetch-keys and offline-by-default verification require @onchaindiligence/cli@0.4.0 or later — check with npx @onchaindiligence/cli --version if a command below reports an unknown flag.
# check the API + upstream data sources npx @onchaindiligence/cli health # genuinely offline against caller-trusted key records npx @onchaindiligence/cli verify result.json --trust keys.json # is an attestation anchored on Tempo? npx @onchaindiligence/cli anchored <signature>
verify exits 0 for VALID, 3 for INVALID, 4 for UNVERIFIABLE, and 2 for usage errors. Online discovery is available only with explicit --fetch-keys.
The paid checks settle a real per-call payment, so they read a funded payer key from the PAYER_KEY environment variable:
# a viem private key funded on the payment rail export PAYER_KEY=0xYOUR_KEY npx @onchaindiligence/cli screen 0x7f26…38E5 npx @onchaindiligence/cli screen-name "Vladimir Putin" npx @onchaindiligence/cli company 00000006 npx @onchaindiligence/cli us-company AAPL npx @onchaindiligence/cli diligence 0x7f26… 00000006
Add --json for raw output you can pipe into jq. Install globally with npm i -g @onchaindiligence/cli to get the onchaindiligence and ocd commands. Source: github.com/Qazza1/onchaindiligence-cli.
Sandbox / test mode
Build and CI-test your full integration — including a positive sanctions hit — without paying or touching production. Sandbox screening is free, takes no payment, and never calls the live oracle.
It lives on its own path, /sandbox/screen/:address, and only accepts the documented test-vector addresses below. A real address is deliberately refused, so you can never screen a real counterparty against test data by mistake.
# always SANCTIONED — test your block/deny path GET /sandbox/screen/0x00000000000000000000000000000000000000ba # always CLEAN — test your allow path GET /sandbox/screen/0x00000000000000000000000000000000c1ea0000 GET /sandbox/screen/0x0000000000000000000000000000000000000001 # list the vectors any time GET /sandbox
A sandbox response mirrors the shape of a real one so you can code against the same structure — but it is always flagged and never signed. The attestation block carries signed: false and sandbox: true with an explicit note, and the body carries "source": "sandbox". A sandbox result can never be mistaken for a real determination or pass verification.
{
"data": {
"address": "0x…ba",
"sanctioned": true,
"source": "sandbox"
},
"attestation": { "signed": false, "sandbox": true },
"sandbox": true
}
Because it's free and deterministic, it drops straight into CI — assert that your code blocks the sanctioned vector and allows the clean one, on every build:
# the sanctioned vector must be blocked by your code curl -s .../sandbox/screen/0x…ba | jq -e '.data.sanctioned == true' # the clean vector must pass curl -s .../sandbox/screen/0x…c1ea0000 | jq -e '.data.sanctioned == false'
When you're ready for real screening, switch the path from /sandbox/screen/:address to the production /screen/:address — same response shape, now paid and signed.
How payment works
Payment rides on the HTTP 402 standard over the Machine Payments Protocol (MPP). The exchange is three steps:
1 — Call with no payment
The server responds 402 Payment Required with a WWW-Authenticate: Payment header. The challenge encodes the amount, the stablecoin contract, the Tempo chain id, and the recipient.
HTTP/1.1 402 Payment Required WWW-Authenticate: Payment id="...", method="tempo", intent="charge", request="<base64 challenge>" Content-Type: application/problem+json
2 — Pay and retry
The client pays the requested stablecoin amount to the recipient on Tempo, then retries the same request with the payment proof attached in the Authorization header. The mppx CLI and SDK do this automatically.
3 — Get a signed result
The server verifies the payment, runs the check, and returns 200 OK with the result and an attestation. If the upstream data source is unreachable, the server returns 503 before requesting payment — you are never charged for a check that cannot complete.
| Field | Value |
|---|---|
| Network | Tempo mainnet · chain id 4217 |
| Currency | pathUSD · 0x20c0…0000 (predeployed) |
| Settlement | On-chain, per call |
2 · Action evidence
Overview
Beyond a simple payment, an agent's on-chain action can be a token approval, a swap, a cross-chain bridge, or a stake. Each of these has a dedicated preflight route on the MCP server (Base mainnet, x402): it validates the proposed action against your policy and returns a signed PREFLIGHT artifact before anything executes. A matching evidence check independently observes the on-chain settlement afterward and reports whether it matches what was preflighted — the same before/after distinction as payments.
These are MCP-surface tools, not routes on the api.onchaindiligence.com HTTP API documented above. See MCP server for the connection details.
ERC-20 allowance
Validates a proposed ERC20_ALLOWANCE action (network, token, spender, amount) against a caller policy before the agent submits an on-chain approve. Schema: onchaindiligence.erc20-allowance-action.v1.
Swap
Validates a proposed swap (input/output asset, amounts, router, recipient) on Base against policy before execution. Schema: onchaindiligence.swap-action.v1.
Bridge (Circle CCTP)
Validates a proposed Circle CCTP V2 bridge from Base to Ethereum mainnet (eip155:8453 → eip155:1) — source/destination network, asset, and amount bounds. Schema: onchaindiligence.bridge-action.v1.
Lido staking
Validates a proposed Lido stETH submit (stake) on Ethereum mainnet against policy before execution. Schema: onchaindiligence.staking-action.v1.
3 · Provider evidence
Executors & providers
The @onchaindiligence/sdk/commerce package orchestrates an agent's merchant payment lifecycle across six payment rails. OCD evaluates policy and independently observes settlement; each executor's own authorization stays fully independent from OCD's decision — an OCD ALLOW never overrides a provider's own denial, and a provider approval never implies OCD ALLOW.
| Provider | What it is |
|---|---|
| x402 | The base production executor for a local signer: Base mainnet, USDC, x402 v2 exact settlement. |
| PayBox | Independent, non-custodial agent payment vault (paybox.sh). Submits a caller-reported provider claim to OCD after a terminal status — a checked claim, never independent settlement evidence. |
| Turnkey | Turnkey Server Wallets. Turnkey's own signed transaction:status webhook, verified server-side by OCD, is the primary evidence path. |
| Crossmint | Crossmint Agent Wallets. Crossmint's own signed wallet-transfer webhook, verified server-side, is the primary evidence path. |
| CDP | Coinbase Developer Platform Server Wallet v2 EOA accounts. No wallet webhook exists, so this executor itself submits the caller-reported provider claim, same as PayBox. |
| Circle | Circle Developer-Controlled Wallets. Circle's own signed transactions.outbound webhook, verified server-side, is the primary evidence path. |
Provider claim vs. independent settlement. Turnkey, Crossmint, and Circle each deliver a provider-signed webhook that OCD verifies server-side — that is independently-sourced evidence. PayBox and CDP have no such webhook, so their executor instead submits a caller-reported claim to POST /operations/:operationId/provider-evidence — recorded and shown as a checked claim, never elevated to independent settlement evidence. The distinction always travels with the record.
See the SDK coverage summary and the full executor documentation for integration details.
4 · Receipts & verification
Signed receipts
A receipt records what was decided (decision), what executed (execution), and what independently settled on-chain (settlement) — each reported separately, never merged into one summary judgment. Every check on this record resolves to one of three states:
| State | Meaning |
|---|---|
| VALID | The signature verifies against a known, correctly-scoped key and the content is unmodified. |
| INVALID | The signature does not verify, or the content has been tampered with. |
| UNVERIFIABLE | Verification could not be completed — e.g. the key is unknown or the trust registry is unreachable. Never treated as a pass or a failure. |
Explore a real signed receipt at /r/OCD-RCP-NB51-QG4S-VCAN-Y57F or in the Explorer.
Attestation
Every paid response includes a versioned attestation object. Version 2 signs RFC 8785 canonical JSON containing the exact data, timestamp, key ID, issuer, purpose, and schema version.
| Field | Meaning |
|---|---|
| signed | Whether the response is cryptographically signed. |
| schema_version | onchaindiligence.attestation.v2. |
| issuer / purpose | Domain separation for the signer and claim type. |
| key_id | Identifier of the signing key. e.g. ed25519-D8wfc7civVNG05Ds |
| algorithm | Always ed25519. |
| signature | base64url signature over the signing input. |
| issued_at | ISO-8601 timestamp the response was signed. |
The exact key and its active, retired, revoked, or compromised status are published by key ID:
curl https://api.onchaindiligence.com/.well-known/attestation-keys
Offline verification
Verification has two modes, and the choice is always explicit — never a silent default.
| Mode | What happens |
|---|---|
| Online | OCD retrieves current key material for you, as a convenience — verifyAttestationOnline(envelope) in the SDK, or ocd verify --fetch-keys in the CLI. A network call is made at verification time. |
| Offline | You supply key material you already obtained and trust, and no network call is made — verifyAttestationOffline(envelope, trustMaterial) in the SDK, or ocd verify --trust keys.json in the CLI. The result does not depend on OCD's server being reachable, or honest, at the moment you check. |
The trust snapshot is the live key registry, unmodified
There is no separate offline key format to learn or convert. The key registry response at /.well-known/attestation-keys is already shaped exactly like the SDK's TrustedAttestationKeySet input ({ keys: [...], issuer, registry_version }) — save that JSON once while you have a connection, and pass the file straight to --trust later with zero transformation.
--trust/verifyAttestationOffline is a deliberate statement that you trust those exact records. A key embedded inside the artifact you're verifying, or fetched from an arbitrary URL, must never be copied into your trust file automatically — that would make verification trust whatever the artifact claims about itself.A real copy-paste flow
# obtain/save trusted public keys while online curl -s https://api.onchaindiligence.com/.well-known/attestation-keys -o keys.json # disconnect / disable network here if you want to prove it to yourself # verify a saved result completely offline npx @onchaindiligence/cli verify artifact.json --trust keys.json
Run against a free, real, signed fixture (GET /sandbox/attestation-sample — no counterparty screened, no payment) with the real published @onchaindiligence/cli@0.4.0:
✓ VALID Attestation is valid under the supplied trust policy.
key: ed25519-P2jIwhCn-Af6pTz4 code: valid
verify exits 0 for VALID, 3 for INVALID, 4 for UNVERIFIABLE, and 2 if you omit both --trust and --fetch-keys — there is no silent fallback path.
What VALID, INVALID, and UNVERIFIABLE establish
| State | Meaning |
|---|---|
| VALID | The exact bytes were signed by a key present in your trust material, with a status and validity window that covers the signing time. |
| INVALID | The signature does not match, the content was altered after signing, or the signing key's own lifecycle rules (revoked, compromised, outside its valid window) were violated. |
| UNVERIFIABLE | No basis to decide either way — the key is unknown to your trust material, the online registry was unreachable (--fetch-keys only), or the input is malformed. Never treated as a pass or a failure. |
Signed evidence bundles
A bundle is a portable, signed package: a manifest, a set of existing evidence artifacts — receipts, attestations, allowance/swap/bridge/staking evidence — and a reconciliation summary, sealed together and verifiable completely offline. A bundle does not replace those artifacts or re-issue them; it packages ones that already exist so they travel as one file instead of several.
Bundle integrity and artifact verification are reported separately
Verifying a bundle answers two different questions, and the CLI and SDK keep them visibly distinct rather than collapsing them into one pass/fail:
| Result | What it answers |
|---|---|
bundle_integrity | Was the manifest sealed intact — nothing added, removed, or altered since signing? |
artifact_verifications[] | Does each individual artifact inside the bundle independently verify, on its own terms? |
Both use the same VALID / INVALID / UNVERIFIABLE states defined above. A bundle can be genuinely VALID at the manifest level while one artifact inside it is INVALID or UNVERIFIABLE — that distinction is the point, not an edge case to collapse away. An artifact of a family the verifier doesn't recognize is reported UNVERIFIABLE, never silently accepted as valid.
Reconciliation: agreements, contradictions, insufficient evidence
Where a bundle contains more than one artifact bearing on the same fact, its reconciliation summary separates three outcomes: agreements, contradictions, and insufficient_evidence. Evidence that is simply missing or unresolved is insufficient_evidence, not a contradiction — the two are never conflated.
SDK and CLI
import { verifyBundleOffline } from '@onchaindiligence/sdk' const result = await verifyBundleOffline(bundle, trustMaterial) // result.bundle_integrity -> 'VALID' | 'INVALID' | 'UNVERIFIABLE' // result.artifact_verifications -> per-artifact results, same tri-state
ocd verify bundle.json --trust keys.json
Bundle verification is offline-only by design — there is no --fetch-keys mode for a bundle, only --trust. Example output, produced by the published @onchaindiligence/cli@0.4.0 against a locally-assembled bundle:
✓ VALID bundle integrity: VALID
artifact sha256:PE__9LR3…: VALID
artifact sha256:TUYpIKRQ…: VALID
artifact sha256:cq76zr7N…: VALID
artifact sha256:qWvhdY8M…: VALID
reconciliation: none
limitation: Bundle validity proves cryptographic integrity under the verifier contract only.
verify uses the same exit codes for a bundle as for a single attestation: 0 for VALID, 3 for INVALID, 4 for UNVERIFIABLE, 2 for a usage error.
VALID bundle proves the manifest and its artifacts were sealed exactly as verified, under the verifier contract. It does not prove execution success, settlement, delivery, regulatory compliance, safety, proper authorization, or objective truth — those remain separate, independently-reported facts, exactly as for a single attestation above. A provider-authenticated artifact inside a bundle proves a provider claim, the same checked claim described under Executors & providers, never independent settlement evidence.artifact_verifications result.Legacy key limitation
The key registry publishes an honest, machine-readable trust_warnings list and a strict_offline_verification_ready flag rather than asserting stronger guarantees than the historical record supports. As of this writing, one retired key has no recorded valid_from activation boundary — it was active before this registry began tracking exact activation times, and that boundary has not been invented to make it look known:
{
"strict_offline_verification_ready": false,
"trust_warnings": [
"attestation key ed25519-D8wfc7civVNG05Ds has no valid_from activation boundary"
]
}
Anything signed with that key still verifies correctly — the signature, content, and its known valid_until upper bound all still apply. What's missing is a hard lower bound on when it started being trustworthy, so strict time-windowed verification of very old records signed with it cannot be fully bounded. Every key issued since — including the current active key — records a real, exact valid_from, so this specific gap does not recur going forward. Check trust_warnings on your own saved snapshot rather than assuming it is empty.
Legacy version 1 records
The manual snippet below is retained specifically for legacy version 1 attestations, which predate the domain-separated v2 signing input:
import crypto from 'node:crypto' const input = JSON.stringify({ data: envelope.data, issued_at: envelope.attestation.issued_at, key_id: envelope.attestation.key_id, }) const ok = crypto.verify( null, Buffer.from(input), publicKeyPem, // from /.well-known/attestation-key Buffer.from(envelope.attestation.signature, 'base64url'), ) // ok === true -> signature matches; issued_at is the signer's assertion
5 · MCP
MCP server
The checks and evidence types on this page are also exposed as a paid Model Context Protocol server, so an AI agent can discover them as tools and pay for them autonomously — no API key, no account. It is a live remote server over Streamable HTTP, listed in the official MCP Registry as com.onchaindiligence/compliance.
Connecting the server surfaces the checks and action-evidence preflights documented above as tools, each priced and payment-gated — the live server is the source of truth for the exact current tool list, since it changes as new evidence types ship.
How payment works
The MCP server settles with x402 — the open agent-payment standard built on HTTP 402. A tool call carries no payment on first attempt; the server returns the payment requirements, the agent signs a USDC authorization, and the call is retried with the payment attached. The server verifies and settles before running the check. The flow is non-custodial: payment goes directly from the agent's wallet to the recipient, and the server never holds funds.
| Field | Value |
|---|---|
| Protocol | x402 over MCP · payment in tools/call _meta |
| Currency | USDC |
| Network | Base mainnet |
| Settlement | On-chain, per call, non-custodial |
Discovery
The server is published in the official MCP Registry, the canonical source that downstream MCP clients and marketplaces draw from. An agent that speaks x402 can find it, read its tool schemas, pay, and call — with no manual configuration.
6 · SDK
TypeScript SDK
@onchaindiligence/sdk wraps the HTTP API for typed calls, and its separate /commerce export is the client-side orchestration layer for the provider executors documented above: createCommerceClient opens an operation, preflight() evaluates policy, your executor's execute() authorizes and submits, and observeAndFinalize() reports what independently settled.
import { createCommerceClient, MockCommerceExecutor, apiPurchasePolicy, BASE_USDC } from '@onchaindiligence/sdk/commerce' const ocd = createCommerceClient({ recovery: new NodeFileRecoveryStore('./ocd-recovery') }) const { policy } = apiPurchasePolicy({ maxAmount: '1.00', allowedNetwork: 'eip155:8453', allowedAsset: BASE_USDC }) const op = await ocd.open({ action: proposedPayment, policy }) const evaluation = await op.preflight() const execution = await op.execute({ executor: myExecutor }) const result = await op.observeAndFinalize()
npm install @onchaindiligence/sdk · Package on npm → · full quickstart and API-only usage on Developers.
7 · Evidence Providers
The checks below are OnchainDiligence's Evidence Providers — sources of external, verifiable fact that the infrastructure signs and delivers. They are inputs to the product, documented here for completeness; they are not the product's identity.
Sanctions screen
Checks a single wallet address against the Chainalysis on-chain sanctions oracle (US / EU / UN lists). Returns a boolean flag — sanctioned or not — signed and timestamped.
| Parameter | Description |
|---|---|
| address | The wallet address to screen, or an ENS name. 0x + 40 hex, or e.g. vitalik.eth |
const r = await od.screen('0x7f26…38E5') // or pass an ENS name: od.screen('vitalik.eth')
npx mppx https://api.onchaindiligence.com/screen/0x7f26…38E5
# MCP tool (Base / x402) Tool: screen_wallet Args: { "address": "0x7f26…38E5" }
{
"data": {
"address": "0x7f268357A8c2552623316e2562D90e642bB538E5",
"sanctioned": false,
"identifications": [],
"source": "Chainalysis on-chain sanctions oracle",
"checked_at": "2026-06-20T06:44:15.164Z"
},
"attestation": {
"signed": true,
"key_id": "ed25519-D8wfc7civVNG05Ds",
"algorithm": "ed25519",
"signature": "wFRfDRSiU5pMOG9y…yqHYfDQ"
}
}
A sanctioned address returns "sanctioned": true with one honest identification. The oracle returns a match flag only — it does not return rich case detail.
Counterparty verdict
Returns a single, signed decision — PASS or BLOCK — with human-readable reasons, rather than raw screening data for you to interpret. Built for agents that need to act, not analyse. Every response is signed with the same key as every other route, so the decision itself is verifiable.
| Parameter | Description |
|---|---|
| address | The wallet address to evaluate, or an ENS name. 0x + 40 hex, or e.g. vitalik.eth |
| Verdict | Meaning |
|---|---|
| BLOCK | The address is sanctioned (OFAC, via the Chainalysis on-chain oracle). A hard legal line — do not transact. |
| PASS | No sanctions match found. |
This endpoint never returns a false PASS. If an upstream check fails, it returns an error rather than passing the address through.
npx mppx https://api.onchaindiligence.com/verdict/0x7f26…38E5
# unpaid requests return 402 with a payment challenge curl https://api.onchaindiligence.com/verdict/0x7f26…38E5
{
"data": {
"verdict": "BLOCK",
"reasons": [
"Address is on the sanctions list (OFAC via Chainalysis on-chain oracle)."
],
"address": "0x8576acc5c05d6ce88f4e49bf65bdf0c62f91353c",
"signals": {
"sanctions": { "checked": true, "sanctioned": true },
"direct_counterparty_exposure": {
"checked": true,
"transfers_scanned": 200,
"counterparties_found": 112,
"counterparties_screened": 25,
"sanctioned_counterparties": [],
"scope": "Direct (one-hop) counterparties observed on Tempo mainnet only…"
}
},
"verdict_basis": {
"live_signals": ["sanctions", "direct_counterparty_exposure"],
"not_yet_evaluated": ["risk_score", "mixer_exposure", "wallet_age", "sanctions_proximity"],
"note": "BLOCK = this address is sanctioned. WARN = it is not, but a direct counterparty is. PASS = neither, within the stated scope — not a full risk clearance."
},
"checked_at": "2026-07-07T14:22:08.441Z"
},
"attestation": {
"signed": true,
"key_id": "ed25519-D8wfc7civVNG05Ds",
"algorithm": "ed25519",
"signature": "UN4TzBvkRsf0eGm4…ZFyElhq1Cg"
}
}
Three verdicts, and they mean different things. BLOCK means the address itself is sanctioned — a hard legal line. WARN means it is not sanctioned but transacted directly with an address that is: a counterparty risk signal, never a designation of the address you asked about. We do not designate by association. PASS means neither was found within the stated scope.
Read verdict_basis before you trust a PASS. live_signals lists the signals that actually ran for that request. If a signal could not be evaluated it is reported as not evaluated — never silently assumed clean — and the reasons say so explicitly. Direct counterparty exposure is deliberately narrow: one hop only (not a multi-hop proximity score), Tempo mainnet only (an address active elsewhere shows no counterparties here), and a bounded recent window. Those limits ride in signals.direct_counterparty_exposure.scope on every response. No sanctioned counterparty found does not mean none exists.
OFAC name screen
Screens a person or company name against the official U.S. Treasury OFAC Specially Designated Nationals (SDN) list — public-domain government data. Uses transparent fuzzy matching (token overlap + edit distance) against primary names and strong aliases, returning confidence-scored candidates. Weak AKAs are not screened, per OFAC guidance.
| Parameter | Description |
|---|---|
| name | Person or company name to screen. min 2 characters |
| threshold | Optional match cutoff, 0.5–1.0. default 0.85 |
const r = await od.screenName('Vladimir Putin') // optional: { threshold: 0.9 }
npx mppx 'https://api.onchaindiligence.com/screen-name?name=Vladimir Putin'{
"data": {
"query": "Vladimir Putin",
"hit": true,
"matches": [
{
"ent_num": 306,
"matched_name": "PUTIN, Vladimir Vladimirovich",
"matched_on": "primary",
"program": "RUSSIA-EO14024",
"score": 0.9
}
],
"threshold": 0.85,
"source": "U.S. Treasury OFAC SDN list (public domain)"
},
"attestation": { "signed": true, … }
}
Company check
Looks up a UK company by its registration number: status, type, incorporation date, registered address, and the people with significant control (PSC) behind it.
| Parameter | Description |
|---|---|
| companyNumber | UK Companies House registration number. e.g. 00000006 |
const r = await od.verifyCompany('00000006')
npx mppx https://api.onchaindiligence.com/company/00000006
# MCP tool (Base / x402) Tool: verify_uk_company Args: { "companyNumber": "00000006" }
{
"data": {
"profile": {
"companyNumber": "00000006",
"companyName": "MARINE AND GENERAL MUTUAL LIFE ASSURANCE SOCIETY",
"status": "dissolved",
"incorporatedOn": "1862-10-25",
"registeredAddress": "Cms Cameron Mckenna Llp Cannon Place, London, EC4N 6AF"
},
"pscList": [],
"source": "UK Companies House (open government data)"
},
"attestation": { "signed": true, "key_id": "ed25519-D8wfc7civVNG05Ds", … }
}
US company check
Looks up an SEC-registered US public company via EDGAR by ticker, CIK, or name: registered entity, CIK, industry (SIC), state of incorporation, exchanges, business address, and most recent filing. Covers public companies and funds only — private US companies register at the state level and are not in EDGAR, so the result carries an explicit coverage note.
| Parameter | Description |
|---|---|
| q | Ticker, SEC CIK, or company name. e.g. AAPL, 0000320193, Apple Inc |
const r = await od.verifyUSCompany('AAPL')
npx mppx https://api.onchaindiligence.com/us-company?q=AAPL
# MCP tool (Base / x402) Tool: verify_us_company Args: { "query": "AAPL" }
{
"data": {
"source": "SEC EDGAR",
"cik": "0000320193",
"name": "Apple Inc.",
"sic_description": "Electronic Computers",
"state_of_incorporation": "CA",
"exchanges": ["Nasdaq"],
"latest_filing": { "form": "4", "filing_date": "2026-06-17" },
"coverage_note": "EDGAR covers SEC-registered (public) companies only…"
},
"attestation": { "signed": true, "key_id": "ed25519-D8wfc7civVNG05Ds", … }
}
Combined diligence
Runs both checks in one call, in parallel. Returns independent results plus an explicit disclaimer that no link between the wallet and the company is established by the data.
| Query | Description |
|---|---|
| wallet | Wallet address to screen. 0x + 40 hex |
| company | UK company registration number. |
const r = await od.diligence({ wallet: '0x7f26…38E5', company: '00000006', })
npx mppx 'https://api.onchaindiligence.com/diligence?wallet=0x7f26…38E5&company=00000006'# MCP tool (Base / x402) Tool: diligence Args: { "wallet": "0x7f26…38E5", "company": "00000006" }
{
"data": {
"wallet_check": { "sanctioned": false, … },
"company_check": { "profile": { … } },
"link_disclaimer": "These are independent checks against separate data sources. No verified link between the wallet and the company is established by this data."
},
"attestation": { "signed": true, … }
}
8 · Anchoring
On-chain anchoring
Any signed attestation can be anchored on Tempo mainnet for an immutable, timestamped, tamper-evident record that a check happened. Only the keccak256 hash of the attestation signature is written on-chain — never the wallet, name, company, or result — so records stay private while remaining provable by anyone. Anchoring is decoupled from checks: it never blocks or delays a paid response.
Cryptographically verifies the complete v2 attestation envelope before anchoring its signature hash. Pass the original { data, attestation } response. Idempotent — re-anchoring sends no new transaction.
await od.anchor(r)
POST /anchor
{ "data": { … }, "attestation": { "signed": true, "schema_version": "onchaindiligence.attestation.v2", … } }Checks whether an attestation has been anchored on-chain, and when — so anyone can verify a record independently.
{
"anchor_hash": "0xadb12c9f…401d",
"anchored": true,
"anchored_at": "2026-06-24T14:44:21.000Z",
"chain": "Tempo",
"network": "mainnet",
"contract": "0xDe47…ea14"
}
ANCHOR_RPC_URL and ANCHOR_CHAIN_ID to be explicitly configured on the deployment; network and contract above always reflect the deployment's real, currently-configured values rather than an assumed default.9 · Reference infrastructure
Discovery & health
These endpoints are free and require no payment — they describe the service and report its status for humans and machines.
Reports whether each upstream data source is reachable and whether response signing is configured. Returns 200 when healthy and 503 when any upstream is degraded, so automated monitors can key off the status code.
{
"status": "ok",
"upstreams": {
"sanctions_oracle": "reachable",
"companies_house": "reachable",
"sec_edgar": "reachable"
},
"attestation": "configured"
}
Service information: the routes, prices, and attestation key URL.
A complete, machine-readable OpenAPI 3.1 document describing every route, parameter, response schema, and the per-call payment terms — for automatic agent and tooling discovery.
Errors
Errors use standard HTTP status codes. Bad input is rejected before any payment is requested.
WWW-Authenticate header carries the payment challenge.Rate limits
Requests are rate limited per client. When you exceed the limit the API returns 429 with a Retry-After header and an X-RateLimit-Remaining count on every response. Back off and retry after the indicated interval.
The full source for the HTTP API, MCP server, SDK, and anchoring contract lives on GitHub.