60-second forensics quickstart

Five copy-paste curls. dev-test-key-123 works without signup. All return live data from https://1f.ai.

1. Screen 3 mixed addresses (sanctions / CEX / mixer flags)

curl -s -X POST https://1f.ai/api/forensics/screen \
  -H "x-api-key: dev-test-key-123" \
  -H "Content-Type: application/json" \
  -d '{"addresses":["0xab5801a7d398351b8be11c439e05c5b3259aec9b","0xd551234ae421e3bcba99a0da6d736074f22192ff","0x910cbd523d972eb0a6f4cae4618ad62622b39dbf"]}'

2. CEX exposure for vitalik.eth

curl -s https://1f.ai/api/forensics/cex-exposure/0xab5801a7d398351b8be11c439e05c5b3259aec9b \
  -H "x-api-key: dev-test-key-123"

3. Cross-chain follow (recent ETH bridge tx)

curl -s https://1f.ai/api/cross-chain/follow/0x6234f5cd4d61b6cda83b6d35e5fe37c2acaa3ac9caea33a1c1c3a5be99a05432 \
  -H "x-api-key: dev-test-key-123"

4. Mixer correlation (known Tornado depositor)

curl -s https://1f.ai/api/forensics/mixer-correlate/0x910cbd523d972eb0a6f4cae4618ad62622b39dbf \
  -H "x-api-key: dev-test-key-123"

5. Pre-flight risk check (USDT → Tornado, expect BLOCK)

curl -s -X POST https://1f.ai/api/simulate/preflight \
  -H "x-api-key: dev-test-key-123" \
  -H "Content-Type: application/json" \
  -d '{"from":"0xdac17f958d2ee523a2206206994597c13d831ec7","to":"0x910cbd523d972eb0a6f4cae4618ad62622b39dbf","valueWei":"1000000000000000000"}'

Methodology

Honest documentation of how 1F's two highest-judgement endpoints work — bridge tx pairing and address risk scoring. Each doc lists inputs, formulas, and known limitations.

Authentication

All /api/* endpoints and the /ws WebSocket require an API key. Provide it via:

MethodExample
HTTP headerX-API-Key: your-key-here
Query parameter?apiKey=your-key-here

API keys are configured in appsettings.json under the 1F.ApiKeys array, or via the CHAINGRAPH_API_KEYS environment variable (comma-separated).

Note: Static files (index.html, docs.html, etc.) are served without authentication.

Arkham-Compatible Endpoints

Response format mirrors intel.arkm.com/api so existing Arkham users can switch with a URL change.

GET /api/intelligence/address/{address}

Returns intelligence summary for an address: transaction counts, top counterparties, tags, and entity data.

ParameterInDescription
addresspathHex address (0x-prefixed)

Example request

curl -H "X-API-Key: YOUR_KEY" \ https://your-host/api/intelligence/address/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045

Example response

{ "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "addrId": 42781, "chain": "ethereum", "isContract": false, "arkpidia": null, "tags": [], "txCountOut": 1284, "txCountIn": 5732, "txCountTotal": 7016, "topCounterpartiesOut": [ { "address": "0x7a250d...", "txCount": 89 } ], "topCounterpartiesIn": [ { "address": "0xdead...", "txCount": 214 } ] }

GET /api/counterparties/address/{address}?limit=N

Returns the top counterparties for an address, ranked by total transaction count (sent + received).

ParameterInDescription
addresspathHex address (0x-prefixed)
limitqueryMax results (default 25, max 100)

Example request

curl -H "X-API-Key: YOUR_KEY" \ https://your-host/api/counterparties/address/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045?limit=5

Example response

{ "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "counterparties": [ { "address": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", "txsSent": 45, "txsReceived": 12, "txsTotal": 57 } ], "totalCounterparties": 842 }

Search for addresses by hex prefix. Returns up to 20 matching addresses from the index.

ParameterInDescription
queryqueryHex address prefix to search

Example request

curl -H "X-API-Key: YOUR_KEY" \ https://your-host/api/intelligence/search?query=0xd8dA6B

Example response

{ "results": [ "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" ] }

Cross-1F Endpoints

GET /api/graph/neighbors/{address}?direction=out|in|both&limit=N

Returns edges (transactions) for an address. Supports directional filtering and pagination.

ParameterInDescription
addresspathHex address (0x-prefixed)
directionqueryout (default), in, or both
limitqueryMax edges returned (default 100, max 10000)

Example request

curl -H "X-API-Key: YOUR_KEY" \ https://your-host/api/graph/neighbors/0xd8dA6BF2...?direction=both&limit=10

Example response

{ "address": "0xd8dA6BF2...", "direction": "both", "edgeCount": 7016, "edges": [ { "address": "0x7a250d56...", "addrId": 1023, "blockOffset": 18420, "txOffset": 42 } ] }

GET /api/graph/bfs/{address}?hops=3&maxResults=100

Breadth-first search from an address. Discovers all reachable addresses within N hops. Essential for fund tracing and cluster analysis.

ParameterInDescription
addresspathStarting hex address
hopsqueryMax traversal depth (default 3, max 6)
maxResultsqueryMax paths returned (default 100, max 10000)

Example request

curl -H "X-API-Key: YOUR_KEY" \ https://your-host/api/graph/bfs/0xd8dA6BF2...?hops=2&maxResults=50

Example response

{ "address": "0xd8dA6BF2...", "maxHops": 2, "pathCount": 48, "paths": [ { "depth": 1, "path": [ { "from": "0xd8dA6BF2...", "to": "0x7a250d56..." } ] } ] }

GET /api/graph/shortest-path/{from}/{to}?maxHops=5

Find the shortest path between two addresses in the transaction graph. Returns up to 5 shortest paths.

ParameterInDescription
frompathSource hex address
topathDestination hex address
maxHopsqueryMax traversal depth (default 5, max 8)

Example request

curl -H "X-API-Key: YOUR_KEY" \ https://your-host/api/graph/shortest-path/0xd8dA6BF2.../0x7a250d56...?maxHops=4

Example response

{ "from": "0xd8dA6BF2...", "to": "0x7a250d56...", "found": true, "paths": [ { "depth": 2, "path": [ { "from": "0xd8dA...", "to": "0xabcd..." }, { "from": "0xabcd...", "to": "0x7a25..." } ] } ] }

Natural Language Query

POST /api/query

Submit a question in plain English. The LLM translates it into graph query primitives and executes them against the index. Requires a Groq API key to be configured.

Request body

{ "question": "Has this address interacted with Tornado Cash on any chain?" }

Example request

curl -X POST -H "X-API-Key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"question":"Where did 0xd8dA6BF2... send funds after bridging to Base?"}' \ https://your-host/api/query

Example response

{ "question": "Where did 0xd8dA6BF2... send funds after bridging to Base?", "plan": { "plan": [ { "step": 1, "action": "resolve_address", "params": { "address": "0xd8dA6BF2..." } }, { "step": 2, "action": "neighbors_out", "params": { "addr_id": "$step1.result.addr_id" } } ], "explanation": "Resolve the address, then find all outgoing transactions.", "limitations": "Cannot filter by bridge contract without contract labels." }, "results": [ { "step": 1, "action": "resolve_address", "result": { "addr_id": 42781, "found": true } }, { "step": 2, "action": "neighbors_out", "result": { "count": 1284, "top": [...] } } ] }
Requires LLM: This endpoint needs a Groq API key configured in appsettings.json or via GROQ_API_KEY env var. Returns an error if not configured.

Meta

GET /api/stats

Returns index statistics: total addresses, edges, hash table size, and load status.

Example request

curl -H "X-API-Key: YOUR_KEY" https://your-host/api/stats

Example response

{ "addrCount": 12458203, "edgeCount": 89421547, "tableSize": 16777216, "isLoaded": true, "version": "v7-1f-live" }

GET /api/chains

Returns the list of supported chains and their ingestion status.

Example request

curl -H "X-API-Key: YOUR_KEY" https://your-host/api/chains

Example response

{ "chains": [ { "id": "ethereum", "name": "Ethereum", "status": "active" }, { "id": "bsc", "name": "BNB Chain", "status": "active" }, { "id": "arbitrum", "name": "Arbitrum", "status": "planned" }, { "id": "base", "name": "Base", "status": "planned" }, { "id": "polygon", "name": "Polygon", "status": "planned" } ] }

Real-Time Event Stream

Subscribe to live mempool + confirmed-tx events across 13 EVM chains. Filter by chain, address, token (with calldata substring scan for Uniswap/Pancake-style swaps), protocol decoder name, and origin chain (for cross-chain bridge pairing). Server pushes JSON frames over WebSocket with per-plan rate caps.

POST /api/stream/subscribe

Create a subscription. Returns a subscriptionId and a ready-to-use wsUrl.

curl -s -X POST https://1f.ai/api/stream/subscribe \
  -H "x-api-key: $KEY" -H "content-type: application/json" \
  -d '{
    "chains": ["bsc", "ethereum"],
    "tokens": ["0x2170ed0880ac9a755fd29b2688956bd959f933f8"],
    "protocols": ["uniswapV2", "pancake"],
    "originChain": "ethereum",
    "addresses": ["0xabc..."],
    "includePending": true,
    "includeConfirmed": false
  }'

Filters (all optional, AND-combined):

Plan caps (events/sec; over-cap clients get {"type":"throttled","dropped":N} heartbeats every 5s instead of disconnect):

PlanCap
Free5/sec
Starter50/sec
Pro500/sec
Enterpriseunlimited

WS /api/stream/ws?subscriptionId=&apiKey=

WebSocket — server pushes one JSON frame per matched event. The apiKey on the WS URL must match the one that created the subscription.

websocat "wss://1f.ai/api/stream/ws?subscriptionId=$SID&apiKey=$KEY"
# >> {"type":"hello","subscriptionId":"...","plan":"pro","capPerSec":500}
# >> {"type":"pending","chain":"bsc","txHash":"0x...","from":"0x...","to":"0x...",
#      "valueWei":1.2e18,"protocol":"ERC-20 transfer","decodedProtocol":"uniswapV2.swap",
#      "erc20To":"0x...","erc20Value":4.2e22,"seenAtUtc":"2026-05-03T02:22:42Z"}
# >> {"type":"heartbeat","dropped":0,"ts":"..."}            // every 5s
# >> {"type":"throttled","dropped":127,"ts":"..."}          // when capped
Mempool visibility: events are sourced from the upstream RPC's mempool feed. For higher-volume coverage on production workloads, configure a paid endpoint per chain via env var ChainGraph__Chains__<chain>__wssUrl=... (Alchemy, QuickNode, NodeReal Meganode, Helius). The endpoint surface and filter semantics are identical regardless of upstream.

GET /api/stream/list    DELETE /api/stream/unsubscribe

List the API key's active subscriptions, or remove one by id. Only the original publisher (matching key) can unsubscribe.

Forensics & Risk Screening

POST /api/forensics/screen

Bulk-screen up to 1,000 addresses per call. Returns BLOCK / REVIEW / WARN / ALLOW per address with reason codes (sanctions hit, mixer touch, hacker-active, fraud network, mule pattern). Designed to be called at withdrawal-creation time.

curl -s -X POST https://1f.ai/api/forensics/screen \
  -H "x-api-key: $KEY" -H "content-type: application/json" \
  -d '{"addresses":["0x...","0x..."]}'

POST /api/forensics/screen/subscribe

Register a webhook that fires when a watched address interacts with a flagged counterparty (sanctions, mixer, hacker-active, fraud network). Body: {address, webhookUrl, hmacSecret}. Webhook payload is HMAC-SHA256 signed in the X-1F-Signature header.

GET /api/forensics/exposure-multihop/{address}?dir=in&hops=2

Multi-hop weighted exposure score. dir=in = "every address that sent funds to this seed within N hops" (the reverse-sweep pattern in Method 2.4 of Investigator-Methods). dir=out for forward exposure. Each hop's weight decays; results sorted by exposure score.

GET /api/forensics/scam-network/{address}

Given a seed scam address, BFS 2 hops and surface candidate cluster members scored by shared-funder + shared-receiver overlap within 24-hour windows. Implements first-hop cluster mapping from Method 2.2.

GET /api/forensics/cex-exposure/{address}

Per-CEX deposit/withdrawal counts and USD totals. Identifies which exchanges this address has touched and the direction of flow.

GET /api/forensics/mixer-correlate/{address}

For an address that has interacted with Tornado/Railgun/Privacy Pools, returns candidate withdrawal endpoints based on commitment-nullifier pairing + timing analysis.

POST /api/simulate/preflight

Pre-confirmation risk check. Body is a partially-signed tx (or just from/to/value). Runs the full screen against the destination + decodes the calldata for protocol-level red flags. Fires before the tx hits a block.

KYC & Sanctions

POST /api/kyc/screen

Wallet-level KYC decision. Returns BLOCK / REVIEW / WARN / ALLOW with mule-pattern, synthetic-account, and fraud-network signals. Call this during your KYC flow with the wallet the customer is depositing from. Pairs with off-chain KYC vendors (Persona, Onfido, Veriff) — we are the on-chain layer.

GET /api/sanctions/check/{address}    GET /api/sanctions/stats

OFAC SDN match check. Refreshed daily from upstream OFAC feeds. /stats returns total entries + last refresh time.

Entity & Attribution

GET /api/entity/{address}

Aggregated entity card: name, category, jurisdiction, icon, color, confidence, source list, summary counts (transfers/deposits/withdrawals/sentUsd/receivedUsd), top-20 counterparties with category labels, related cluster (if any).

GET /api/known-labels/{address}    GET /api/known-labels

Curated label store backed by Sybil verified.json, Etherscan name tags, OFAC, and community-feed posts. Single-address lookup or full dump.

GET /api/custom-entities    POST /api/custom-entities    DELETE /api/custom-entities/{address}

Per-customer entity overrides. Set name, category, icon, color on the canvas; persists across sessions. /lookup returns the resolved attribution (custom takes precedence over global).

GET /api/notes/{address}    POST /api/notes

Investigator notes attached to an address. Multi-author with timestamps; visible across the customer's team.

Cross-Chain Tracing

GET /api/cross-chain/follow/{txHash}

Given a source-chain bridge tx hash, return the paired destination-chain tx — exact match via BridgeMessageRegistry for LayerZero/Wormhole/CCTP/Stargate/Hop/Across, plus heuristic match for Synapse/deBridge. Falls back to the heuristic (value, time-window, bridge-id) tuple when no message id is available.

GET /api/cross-chain/portfolio/{address}

Per-chain balance snapshot for the address. Aggregated USD value, breakdown by chain + token.

GET /api/cross-chain/bridges/{address}    GET /api/cross-chain/timeline/{address}    GET /api/cross-chain/transfers/{address}

Bridges this address has used (with frequency + USD volume), full chronological cross-chain timeline, and token-transfer breakdown.

GET /api/bridge-messages/source/{txHash}    GET /api/bridge-messages/stats

Direct lookup into the BridgeMessageRegistry. Records src→dst pairings extracted by Tier-1, Tier-2, and Tier-5 bridge decoders (LayerZero V1/V2, Wormhole, CCTP, Stargate, Hop, Across, Synapse, deBridge, Arbitrum, OP-stack, Polygon PoS, Binance Bridge, Avalanche Bridge, Linea, Scroll, Mantle, zkSync, Polygon zkEVM).

Mixer Tracing

GET /api/mixer-commitments/deposit/{txHash}    GET /api/mixer-commitments/withdraw/{txHash}    GET /api/mixer-commitments/stats

Tornado / Railgun / Privacy Pools / Aztec / THORChain commitment + nullifier registry. Given a deposit, returns matching withdraws (or candidates if not yet revealed). Given a withdraw, returns the deposit set narrowed by anonymity-set heuristics.

GET /api/forensics/tornado/pools

List of tracked Tornado pools (0.1/1/10/100 ETH on mainnet, equivalents on each chain) with active anonymity set sizes.

Investigator-Merged Clusters

Group N addresses (typically the first-hop withdrawal addresses of unrelated victims) under one shared name, icon, color. The cluster becomes a first-class entity referenced everywhere downstream.

POST /api/clusters/merge

curl -X POST https://1f.ai/api/clusters/merge \
  -H "x-api-key: $KEY" -H "content-type: application/json" \
  -d '{
    "name": "40% Return Scam",
    "category": "scam",
    "icon": "🎣", "color": "#f85149",
    "addresses": ["0x...", "0x...", "0x..."],
    "note": "Merged from victims #1042, #1118, #1244"
  }'

GET /api/clusters    GET /api/clusters/{clusterId}    GET /api/clusters/by-address/{address}    DELETE /api/clusters/{clusterId}

Cross-Tenant Community Feed

Publish newly-identified scam clusters to a shared feed. Other customers pull the feed and auto-merge fresh entries into their local EntityStore with source="community". Each post is signed with a per-publisher SHA-256 fingerprint.

POST /api/community/share

curl -X POST https://1f.ai/api/community/share \
  -H "x-api-key: $KEY" -H "content-type: application/json" \
  -d '{
    "address": "0x...",
    "category": "scam",
    "name": "Pig butchering — Telegram group XYZ",
    "evidenceUrl": "https://...",
    "note": "Confirmed by 3 victim reports"
  }'

GET /api/community/feed?limit=100&since=ISO    GET /api/community/by-address/{address}    POST /api/community/retract    GET /api/community/stats

Pull the feed (paginated by since), look up posts for a specific address, retract a post (publisher-only — fingerprint must match), or check active post count.

Cases & Court-Ready Reports

GET /api/cases    POST /api/cases    GET /api/cases/{caseId}    POST /api/cases/{caseId}/addresses

One case per investigation. Multiple addresses, full audit log of every action by every actor (timestamped, append-only).

GET /case/{caseId}/report    GET /api/cases/{caseId}/report-data

Print-styled HTML report (browser File→Print = court-ready PDF). Every case bundle is signed with HMAC-SHA256 chain-of-custody over {caseId}|{updatedAt}|{caseCanonical}|{reportTime}|{actor} keyed by CASE_SIGNING_KEY. Tamper-detectable via SHA-256 over canonical case body.

GET /case/{caseId}/sar

FinCEN Form 111-style SAR adapter. Renders Filing Institution / Subject Information / Suspicious Activity / Narrative + Audit / Cryptographic Integrity sections. Auto-derives typology checkboxes from address categories. Companion document to an officially-filed Form 111 — not a substitute.

AI Investigation Agent

GET /api/agent/investigate/{address}

End-to-end automated investigation of a target address. Combines: entity attribution, cluster lookup, multi-hop in/out exposure, CEX endpoints, mixer correlation, sanctions check, scam-network BFS, and a natural-language narrative summary. Returns a structured findings[] + narrative blob suitable for display in an investigator UI.

Newsworthy & Threat Feed

GET /api/newsworthy

Last 1,000 detected events from ThreatMonitorService. Six detector classes: SANCTIONED_TOUCH, HACKER_ACTIVE, MIXER_INFLOW_BURST, CEX_LARGE_OUTFLOW, FRAUDULENT_ENTITY_TX, SCREEN_SUBSCRIBE_FLAG. 60-second scan interval. Each detection also fires registered webhooks.

Coverage & Meta

GET /api/coverage    GET /api/status/chains

Per-chain status: enabled / mempool / WSS active / last block ingested / index size on disk. Public (no API key).

GET /api/decoders

Live registry of protocol decoders. Currently registered: Uniswap V2/V3, Curve, Balancer, 1inch, PancakeSwap V3, Aerodrome, LayerZero V1/V2, Wormhole, Stargate, Hop, Across, Synapse, CCTP, deBridge, Arbitrum L1 Inbox, OP-stack L1 Bridge, Polygon PoS, Binance Bridge, Avalanche Bridge, Linea/Scroll/Mantle/zkSync/Polygon zkEVM canonical, Aave V2/V3, Compound V2/V3, Lido, EigenLayer, Maker DSR, Tornado, Railgun, Privacy Pools, Aztec, THORChain. Each decoder lists its selectors, name, category, optional contract address, and chain id.

GET /api/reorgs

Recent reorganizations detected by ReorgWatcherService. Detect-and-flag only — does not roll back the index. Useful for downstream consumers to know when to re-fetch a recent block range.

Legacy: Binary Graph WebSocket

ws://host/ws

Binary frame protocol for real-time graph exploration. Used by the Graph Explorer UI.

The WebSocket accepts binary frames where the first byte is the command type:

CommandIDPayload
NEIGHBORS_OUT1uint32 addr_id
NEIGHBORS_IN2uint32 addr_id
BFS3uint32 addr_id + uint8 hops + uint16 maxResults
BLOCK_LOOKUP4uint32 block_offset
ADDR_EXISTS5uint32 addr_id
STATS6(none)

Responses are binary frames. See the Graph Explorer source for parsing details.

Tip: For most use cases, the REST API is simpler. Use WebSocket only when you need sub-millisecond latency for interactive graph exploration.