# Screening — Swaps API

Address risk, traces and on-chain transaction inspection.

**Jump to an operation:**

- [POST /address_checks](#check-a-destination-address-before-sign-in-advice-only-dark-flag)
- [GET /screenings](#list-the-callers-past-screenings-available)
- [POST /screenings](#screen-one-address-before-sending-funds-available)
- [GET /screenings/{id}](#read-one-screening-by-id-available)
- [GET /screenings/{id}/reports](#list-the-reports-run-on-one-screening-available)
- [POST /screenings/{id}/reports](#generate-the-full-report-and-evidence-pdf-available)
- [GET /screenings/{id}/reports/{report_id}](#read-one-full-screening-report-available)
- [GET /screenings/{id}/reports/{report_id}/evidence_pdf](#download-a-reports-evidence-pdf-planned)
- [POST /traces](#follow-where-a-transactions-funds-moved-available)
- [GET /transactions/{chain}/{hash}](#inspect-one-on-chain-transaction-available)

> Generated from the bundled OpenAPI contract. The HTML page and this Markdown twin share the same processed operation descriptions and status annotations.

<a id="check-a-destination-address-before-sign-in-advice-only-dark-flag"></a>
## `POST /address_checks` — Check a destination address before sign-in — advice only — dark-flag

**Operation ID:** `address_checks.create`

> **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 answers `503 temporarily_unavailable`; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers `409 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.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [AddressCheckRequest](/reference/~schemas.md#addresscheckrequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The advisory decision. | `application/json` [AddressCheck](/reference/~schemas.md#addresscheck) |
| `400` | — | `BadRequest` |
| `403` | — | `Forbidden` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="list-the-callers-past-screenings-available"></a>
## `GET /screenings` — List the caller's past screenings — available

**Operation ID:** `screenings.list`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `screenings.read` · **Test mode:** unavailable

Callers business key, agent. Test mode fixture addresses.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `Limit` | component | — | — | Reusable parameter component |
| `Cursor` | component | — | — | Reusable parameter component |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | A page of screenings. | `application/json` [ScreeningList](/reference/~schemas.md#screeninglist) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="screen-one-address-before-sending-funds-available"></a>
## `POST /screenings` — Screen one address before sending funds — available

**Operation ID:** `screenings.create`

> **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.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [ScreeningCreateRequest](/reference/~schemas.md#screeningcreaterequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `201` | The screening. | `application/json` [Screening](/reference/~schemas.md#screening) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `402` | — | `PaymentRequired` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | `capability_unavailable` — code `network_not_supported` (`param: chain`): `chain` names no network screening covers (blank, `tempo`, `litecoin`, …) or the address cannot exist on it; nothing was screened or recorded — do not retry with the same `chain`. Also the idempotency-conflict codes every mutating `/v1` operation with an `Idempotency-Key` shares: `idempotency_error` (the key was reused with a different body), `idempotency_in_progress` (a request with this key is still running) and `idempotency_failed` (the first attempt answered a 5xx — reconcile, then retry with a new key). | `application/json` [Error](/reference/~schemas.md#error) |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="read-one-screening-by-id-available"></a>
## `GET /screenings/{id}` — Read one screening by id — available

**Operation ID:** `screenings.get`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `screenings.read` · **Test mode:** unavailable

Callers business key, agent. Test mode fixture addresses.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The screening. | `application/json` [Screening](/reference/~schemas.md#screening) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="list-the-reports-run-on-one-screening-available"></a>
## `GET /screenings/{id}/reports` — List the reports run on one screening — available

**Operation ID:** `screenings.reports.list`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `screenings.read` · **Test mode:** unavailable

Callers business key, agent. Test mode fixture addresses.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `Limit` | component | — | — | Reusable parameter component |
| `Cursor` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | A page of reports. | `application/json` [ScreeningReportList](/reference/~schemas.md#screeningreportlist) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="generate-the-full-report-and-evidence-pdf-available"></a>
## `POST /screenings/{id}/reports` — Generate the full report and evidence PDF — available

**Operation ID:** `screenings.reports.create`

> **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.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `201` | The report — freshly generated or served from the 30-day cache. | `application/json` [ScreeningReport](/reference/~schemas.md#screeningreport) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `402` | — | `PaymentRequired` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="read-one-full-screening-report-available"></a>
## `GET /screenings/{id}/reports/{report_id}` — Read one full screening report — available

**Operation ID:** `screenings.reports.get`

> **Status:** Available · **Callers:** business key, agent (business key), dashboard session · **Scope:** `screenings.read` · **Test mode:** unavailable

Callers business key, agent. Test mode fixture addresses.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |
| `report_id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The report. | `application/json` [ScreeningReport](/reference/~schemas.md#screeningreport) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="download-a-reports-evidence-pdf-planned"></a>
## `GET /screenings/{id}/reports/{report_id}/evidence_pdf` — Download a report's evidence PDF — planned

**Operation ID:** `screenings.reports.evidence_pdf.get`

> **Status:** Planned — not built yet · **Callers:** business key, agent (business key), dashboard session · **Scope:** `screenings.read` · **Test mode:** fixtures
>
> This 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.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `id` | path | yes | `string` | — |
| `report_id` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The PDF. | `application/pdf` `string` |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `503` | — | `ServiceUnavailable` |

---

<a id="follow-where-a-transactions-funds-moved-available"></a>
## `POST /traces` — Follow where a transaction's funds moved — available

**Operation ID:** `traces.create`

> **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.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `IdempotencyKey` | component | — | — | Reusable parameter component |

### Request body

Required: **yes**

| Media type | Schema |
| --- | --- |
| `application/json` | [TraceRequest](/reference/~schemas.md#tracerequest) |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `201` | The trace. | `application/json` [Trace](/reference/~schemas.md#trace) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `409` | — | `Conflict` |
| `413` | — | `PayloadTooLarge` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |

---

<a id="inspect-one-on-chain-transaction-available"></a>
## `GET /transactions/{chain}/{hash}` — Inspect one on-chain transaction — available

**Operation ID:** `transactions.get`

> **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.

### Parameters

| Name | In | Required | Schema | Description |
| --- | --- | --- | --- | --- |
| `SwapsVersion` | component | — | — | Reusable parameter component |
| `SwapsAccount` | component | — | — | Reusable parameter component |
| `chain` | path | yes | `string` | — |
| `hash` | path | yes | `string` | — |

### Responses

| Status | Description | Body |
| --- | --- | --- |
| `200` | The transaction. | `application/json` [ChainTransaction](/reference/~schemas.md#chaintransaction) |
| `400` | — | `BadRequest` |
| `401` | — | `Unauthorized` |
| `403` | — | `Forbidden` |
| `404` | — | `NotFound` |
| `429` | — | `TooManyRequests` |
| `500` | — | `InternalError` |
| `503` | — | `ServiceUnavailable` |
