Screening
Address risk, traces and on-chain transaction inspection.
Jump to an operation:
Check a destination address before sign-in — advice only — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: public token · Test mode: unavailable
This operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
An advisory check of one destination address before the person signs in — the exchange widget's "Send to" step (D-04). PUBLIC and UNAUTHENTICATED: no key or session is read. It runs the same free screening POST /v1/screenings runs and answers only decision (allow, offer_report, block, not_screenable) with the time the screening data was produced — no score, no level, no labels, so it cannot be used as a risk oracle. Nothing is stored for the caller, no credit is spent, and the address never appears in the URL or the answer. The address is screened exactly as given — a Bitcoin, Tron or Solana address is never lowercased. A network screening does not cover, or an address that cannot exist on the named network, is decision: not_screenable (nothing was checked — neither clean nor risky); a screening that cannot complete is 503 temporarily_unavailable — never treat that as allow. It is advice, never the binding check: POST /v1/orders screens the destination again server-side and refuses a blocked one (destination_blocked); never present this answer as that check. Answers 200: nothing is created. Protection: the strictest of the public limits, per client IP (429; a limiter outage is 503), and a client IP on the Swaps block list is refused 403 ip_not_allowed before any screening. Switch: dark-flag — the api_v1.address_checks kill switch answers 503 temporarily_unavailable while it is off, which it is until the founder turns it on at launch. No MCP tool: an agent screens through the authenticated check_address_risk, which records the screening for its account.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Check a destination address before sign-in — advice only — dark-flag › Request Body
networkThe network the address will receive on, by chain id or name, case-insensitive — the same vocabulary as ScreeningCreateRequest.chain: Ethereum (1, eth), BNB Chain (56, bsc), Polygon (137), Arbitrum (42161), Optimism (10), Base (8453), Avalanche (43114), bitcoin, tron, solana, xrp, ton, cardano, cosmos. A network screening does not cover is answered decision: not_screenable, never screened as Ethereum.
addressThe address exactly as it will be submitted. Case-preserved: a Bitcoin, Tron or Solana address is never lowercased. It is not stored and never echoed back.
Check a destination address before sign-in — advice only — dark-flag › Responses
The advisory decision.
objectnetworkThe request's network, trimmed.
decisionallow — no risk signal above the reporting threshold. offer_report — a moderate signal: offer the person a report before they continue. block — a severe signal or a sanctions match; POST /v1/orders refuses a destination its own screening blocks (destination_blocked). not_screenable — screening does not cover this network or address format (for example Litecoin, or an address that cannot exist on the named network); nothing was checked, so it is neither a clean nor a risky answer. The same four decisions the exchange widget uses today.
checked_atWhen the screening data behind this decision was produced — earlier than now when it was served from the screening cache.
List the caller's past screenings — available
Status: Available · Callers: business key, agent (business key), dashboard session · Scope:
screenings.read· Test mode: unavailable
Callers business key, agent. Test mode fixture addresses.
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
List the caller's past screenings — available › Responses
A page of screenings.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Screen one address before sending funds — available
Status: Available · Callers: business key, agent (business key), dashboard session · Scope:
screenings.write· Test mode: unavailable
Runs a free risk screen on one address and chain — unmetered by design (matches prod: guard.check has never spent a credit; only screenings.reports.create's full report does, D-84, which for a business key spends the account owner's own credit balance — see that operation). The result is a signal, never a verdict — action is a suggestion, never an authorization. The account-scoped api_screenings row stores the address itself (case-preserved, never a hash) so it can be listed back and embedded in address_book; it is never shared across accounts. chain must name a network screening covers (ScreeningCreateRequest.chain); any other value, or an address that cannot exist on that network, is refused 409 network_not_supported before any check runs or any row is written — never screened as Ethereum. Do not call this for a transaction hash — use GET /v1/transactions/{chain}/{hash} — and never chain action straight into a transfer without a human in the loop. Callers: business key, agent. Test mode: fixture addresses.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Screen one address before sending funds — available › Request Body
addressCase-preserved verbatim — never lowercase a BTC, Tron or Solana address.
chainThe network to screen on, by chain id or name, case-insensitive: Ethereum (1, eth), BNB Chain (56, bsc), Polygon (137), Arbitrum (42161), Optimism (10), Base (8453), Avalanche (43114), bitcoin, tron, solana, xrp (XRP Ledger), ton, cardano, cosmos. Any other value — blank, tempo, litecoin — whatever the address, and an address that cannot exist on the named network (an EVM address on bitcoin, a Bitcoin address on tron) are refused 409 network_not_supported; nothing is ever screened as Ethereum by default. An address is judged by the named network's own address format; on solana that is any base58 string of 32–44 characters.
Screen one address before sending funds — available › Responses
The screening.
id^scr_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectlivemodecreated_ataddressCase-preserved verbatim.
chainrisk_scorerisk_levelverdictDerived, coarser than risk_level. Still a signal, never a block decision.
actionA suggestion the caller may act on — never an authorization by itself.
cachedscreened_atupdated_atflagsFree-form, source-shaped detail — never re-typed per source.
confidencecoverageThe sources actually checked, so a client can see what was not.
provider_versionRead one screening by id — available
Status: Available · Callers: business key, agent (business key), dashboard session · Scope:
screenings.read· Test mode: unavailable
Callers business key, agent. Test mode fixture addresses.
path Parameters
id^scr_ · requiredHeaders
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Read one screening by id — available › Responses
The screening.
id^scr_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectlivemodecreated_ataddressCase-preserved verbatim.
chainrisk_scorerisk_levelverdictDerived, coarser than risk_level. Still a signal, never a block decision.
actionA suggestion the caller may act on — never an authorization by itself.
cachedscreened_atupdated_atflagsFree-form, source-shaped detail — never re-typed per source.
confidencecoverageThe sources actually checked, so a client can see what was not.
provider_versionList the reports run on one screening — available
Status: Available · Callers: business key, agent (business key), dashboard session · Scope:
screenings.read· Test mode: unavailable
Callers business key, agent. Test mode fixture addresses.
path Parameters
id^scr_ · requiredquery Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
List the reports run on one screening — available › Responses
A page of reports.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Generate the full report and evidence PDF — available
Status: Available · Callers: business key, agent (business key), dashboard session · Scope:
screenings.write· Test mode: unavailable
Spends 1 credit unless an equivalent report for this address was generated within the last 30 days, in which case the cached report is returned at no cost (cached: true). No account-level credit ledger exists yet (D-82) — a business-key call spends the account's OWNER member's own personal credit balance (resolveBillingUserId), the same balance GET /v1/credits reads. Callers: business key, agent. Test mode: fixture addresses.
path Parameters
id^scr_ · requiredHeaders
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Generate the full report and evidence PDF — available › Responses
The report — freshly generated or served from the 30-day cache.
id^rpt_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectlivemodecreated_atcachedTrue when this report was served from the 30-day cache rather than spending a fresh credit.
updated_atRead one full screening report — available
Status: Available · Callers: business key, agent (business key), dashboard session · Scope:
screenings.read· Test mode: unavailable
Callers business key, agent. Test mode fixture addresses.
path Parameters
id^scr_ · requiredreport_id^rpt_ · requiredHeaders
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Read one full screening report — available › Responses
The report.
id^rpt_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectlivemodecreated_atcachedTrue when this report was served from the 30-day cache rather than spending a fresh credit.
updated_atDownload a report's evidence PDF — planned
Status: Planned — not built yet · Callers: business key, agent (business key), dashboard session · Scope:
screenings.read· Test mode: fixturesThis operation is planned — not built yet. It is documented for the contract it will carry, but it does not run today. Never call it expecting a live result.
Returns the generated PDF only when evidence_pdf.available is true on the report — a 404 otherwise, never an empty 200. Callers: business key, agent. Test mode: fixture addresses.
path Parameters
id^scr_ · requiredreport_id^rpt_ · requiredHeaders
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Download a report's evidence PDF — planned › Responses
The PDF.
Follow where a transaction's funds moved — available
Status: Available · Callers: business key, agent (business key), dashboard session · Scope:
screenings.write· Test mode: unavailable
Starting from one origin transaction hash, follows value across hops and names the counterparties reached. Slow and expensive relative to a screening — call it after check_address_risk returns high or critical, not before; on a clean address it tells you nothing new. Takes a transaction hash, not a bare address — an address alone has no single flow to follow; use the incident transaction (e.g. the transfer that sent funds to a scammer). Callers: business key, agent. Test mode: fixture addresses.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Follow where a transaction's funds moved — available › Request Body
hashThe origin transaction hash to trace forward from (the incident transfer, e.g. the transaction that sent funds to a scammer) — a trace always starts from a specific transaction, never a bare address, since an address alone has no single "flow" to follow.
chainFollow where a transaction's funds moved — available › Responses
The trace.
id^trc_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectlivemodecreated_atupdated_atInspect one on-chain transaction — available
Status: Available · Callers: business key, agent (business key), dashboard session · Scope:
screenings.read· Test mode: unavailable
Reads one on-chain transaction by hash: amounts, transfers, both sides, and a short risk brief on each counterparty. Use screenings.create for an address and orders.get for a Swaps order — this is neither. Callers: business key, agent. Test mode: fixture addresses.
path Parameters
chainhashHeaders
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Inspect one on-chain transaction — available › Responses
The transaction.
chainhashA short risk brief on each side of the transaction.