API reference
Endpoints, webhooks, and the error model
Rendered from the OpenAPI contract at build time. Field descriptions are reproduced verbatim from the contract and are not edited here.
- Source
- api/openapi.yaml
- Contract version
- 1.0.0
- Specification
- OpenAPI 3.1.0
- Contract digest
- sha256:0c75891a35d8
Contract overview
Foreshock normalizes heterogeneous stablecoin freeze mechanisms across issuers and chains into one event model, one API, and one deposit-handling workflow.
The API answers three questions:
- Exposure — which of your own addresses currently relate to an enforcement event, how, and how severely.
- Screening — what authoritative lists say about an address, with a reference to the raw response snapshot behind each answer.
- Decision — whether an inbound deposit may be credited, held, or rejected, with an immutable evidence record.
Fail-closed by contract
Every degraded path in this API resolves to HOLD. There is no fail-open branch. Vendor timeouts, exhausted budgets, and stale lists return HTTP 200 with verdict: HOLD and a reason code. Foreshock being unavailable returns 5xx with no verdict, and clients MUST treat any 5xx as HOLD. The official SDKs do this by default; if you write your own client, you must implement it yourself.
No synthetic scores
Foreshock does not publish a blended risk score. Results from multiple sources are returned side by side, unmodified, each with a snapshot reference. Third-party vendor scores are passed through verbatim and are not converted to a common scale — different vendors' scores are not comparable, and converting them would manufacture false precision. There is no debug or verbose mode that reveals an internal score, because no internal score is ever placed in an outbound response body.
Blind spots are disclosed, not hidden
Enforcement visibility is a by-product of each issuer's governance structure, not a capability Foreshock provides, and an issuer can change how it executes at any time. coverage_health therefore accompanies exposure results and is separately inspectable at GET /v1/coverage, so you can tell "nothing is wrong" apart from "we cannot currently see". observable: false, a stale verified_at, or an abnormal collector_lag_s should be treated as a blind spot, and your client should fall back to a more conservative policy.
What this API deliberately does not return
- The address that is the subject of an enforcement event.
- Any path detail, upstream address, or counterparty identity.
- Any bulk export of enforcement data in a non-terminal stage.
- Any expectation of how much advance notice a given issuer will provide.
Examples in this document
Every address, transaction hash, identifier, and snapshot reference in the examples below is a synthetic placeholder and is intentionally not valid on any chain. Placeholders contain the literal string EXAMPLE or a repeated digit so they cannot be mistaken for real data.
Authentication
http · bearer · Foreshock API key
Authorization: Bearer <api_key>.
Keys are scoped to a single customer account. Cross-customer reads are not expressible: customer_id is the leading key column and partition key of every customer data table, so a key cannot address another account's data even in principle.
Operations marked x-kyb-required: true additionally require the account to have completed institutional KYB verification. A valid key on a non-verified account receives 403 with type: .../kyb-required, not 401.
Servers
https://api.foreshock.org
Managed endpoint.
https://{host}
Self-hosted deployment. Self-hosting is a first-class delivery form: the customer address book never leaves the customer environment.
Endpoints
GET /v1/exposure
List matches between enforcement events and your own addresses
Bearer key KYB required
Returns the enforcement matches for your own address book, newest first by cursor order, together with the coverage health of the authorities relevant to your account.
Each match answers exactly three things: which of your addresses matched (as an opaque reference), how it relates to the enforcement event, and how severe the relation is. It does not identify the subject of the enforcement event, and it does not describe how the relation was established.
Pagination. Pass the next_cursor from the previous response as since to continue. When next_cursor is null you have consumed everything currently available. limit is capped; this endpoint is a change feed, not an export.
No bulk export. There is no endpoint, parameter, or format that dumps enforcement data in a non-terminal stage, in this API or elsewhere.
Access requires a KYB-verified institutional account.
Contract extensions
- x-kyb-required
- true
- x-no-bulk-export
- true
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| since | query | string | no | Opaque cursor from the |
| limit | query | integer | no | Maximum number of matches to return in one page. The cap is deliberate: this endpoint is a change feed for your own address book, not an export interface. |
Responses
200 application/json · ExposureResponse
Matches and coverage health as of the returned timestamp.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| RateLimit-Limit | integer | no | Requests permitted in the current window for this account. |
| RateLimit-Remaining | integer | no | Requests remaining in the current window. |
| RateLimit-Reset | integer | no | Seconds until the current window resets. |
| Name | Type | Required | Description |
|---|---|---|---|
| as_of | string(date-time) | yes | Instant this response describes. |
| coverage_health | array<CoverageHealthEntry> | yes | Coverage health of the authorities in scope for your account, returned with every exposure response so that "no matches" can be told apart from "we cannot currently see". |
| matches | array<ExposureMatch> | yes | Matches against your own address book, in cursor order. |
| next_cursor | string | null | no | Cursor to pass as |
{
"as_of": "2026-08-24T09:05:00Z",
"coverage_health": [
{
"authority_id": "authority-EXAMPLE-0001",
"observable": true,
"verified_at": "2026-08-24T09:00:00Z",
"collector_lag_s": 3
},
{
"authority_id": "authority-EXAMPLE-0002",
"observable": false,
"verified_at": "2026-08-23T21:40:00Z",
"collector_lag_s": 1
}
],
"matches": [
{
"customer_address_ref": "caref_EXAMPLE0000000000000001",
"relation": "PATH",
"severity": "R1",
"stage": "PROPOSED",
"observed_at": "2026-08-24T09:04:11Z",
"evidence_ref": "cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000000"
}
],
"next_cursor": "eyJFWEFNUExFIjoibmV4dC1jdXJzb3ItcGxhY2Vob2xkZXIifQ"
} 400 application/problem+json · Problem
The request was malformed or failed validation.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "`chain` is required.",
"request_id": "req_EXAMPLE0000000001"
} 401 application/problem+json · Problem
Missing, malformed, or revoked API key.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "API key missing or not recognised.",
"request_id": "req_EXAMPLE0000000002"
} 403 application/problem+json · Problem
The key is valid, but this operation is restricted to KYB-verified institutional accounts. Enforcement data in a non-terminal stage is not available to unverified accounts under any plan.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/kyb-required",
"title": "KYB verification required",
"status": 403,
"detail": "This account has not completed institutional KYB verification.",
"request_id": "req_EXAMPLE0000000004"
} 429 application/problem+json · Problem
Rate limit exceeded.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Retry-After | integer | no | Seconds to wait before retrying. On the decision endpoint, waiting is not a substitute for a verdict: the deposit stays on |
| RateLimit-Limit | integer | no | Requests permitted in the current window for this account. |
| RateLimit-Remaining | integer | no | Requests remaining in the current window. |
| RateLimit-Reset | integer | no | Seconds until the current window resets. |
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/rate-limited",
"title": "Too many requests",
"status": 429,
"detail": "Rate limit exceeded for this account.",
"request_id": "req_EXAMPLE0000000007"
} 503 application/problem+json · Problem
Foreshock is temporarily unavailable.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Retry-After | integer | no | Seconds to wait before retrying. On the decision endpoint, waiting is not a substitute for a verdict: the deposit stays on |
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/unavailable",
"title": "Service unavailable",
"status": 503,
"detail": "Temporarily unavailable.",
"request_id": "req_EXAMPLE0000000009"
} GET /v1/coverage
Inspect coverage health
Bearer key KYB required
Returns the current observability health of the enforcement authorities relevant to your account: whether each is currently observable, when its registry entry was last verified, and the current collector lag.
Scope is per-customer. An authenticated caller sees the authorities that bear on the assets and addresses they have registered — not the full Foreshock registry. The set of authorities, issuers, and chains is returned as data to authenticated customers; it is not enumerated in this schema and is not published to unauthenticated parties. Absence of an entry means the authority is out of scope for your account, and must not be read as a statement about Foreshock's registry as a whole.
Use this endpoint to distinguish "no alert" from "we cannot currently see".
Access requires a KYB-verified institutional account.
Contract extensions
- x-kyb-required
- true
- x-scope
- per-customer
Responses
200 application/json · CoverageResponse
Coverage health for the authorities in scope for the caller.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| RateLimit-Limit | integer | no | Requests permitted in the current window for this account. |
| RateLimit-Remaining | integer | no | Requests remaining in the current window. |
| RateLimit-Reset | integer | no | Seconds until the current window resets. |
| Name | Type | Required | Description |
|---|---|---|---|
| as_of | string(date-time) | yes | Instant this response describes. |
| coverage_health | array<CoverageHealthEntry> | yes | |
{
"as_of": "2026-08-24T09:05:00Z",
"coverage_health": [
{
"authority_id": "authority-EXAMPLE-0001",
"observable": true,
"verified_at": "2026-08-24T09:00:00Z",
"collector_lag_s": 3
},
{
"authority_id": "authority-EXAMPLE-0002",
"observable": false,
"verified_at": "2026-08-23T21:40:00Z",
"collector_lag_s": 1
}
]
} 401 application/problem+json · Problem
Missing, malformed, or revoked API key.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "API key missing or not recognised.",
"request_id": "req_EXAMPLE0000000002"
} 403 application/problem+json · Problem
The key is valid, but this operation is restricted to KYB-verified institutional accounts. Enforcement data in a non-terminal stage is not available to unverified accounts under any plan.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/kyb-required",
"title": "KYB verification required",
"status": 403,
"detail": "This account has not completed institutional KYB verification.",
"request_id": "req_EXAMPLE0000000004"
} 429 application/problem+json · Problem
Rate limit exceeded.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Retry-After | integer | no | Seconds to wait before retrying. On the decision endpoint, waiting is not a substitute for a verdict: the deposit stays on |
| RateLimit-Limit | integer | no | Requests permitted in the current window for this account. |
| RateLimit-Remaining | integer | no | Requests remaining in the current window. |
| RateLimit-Reset | integer | no | Seconds until the current window resets. |
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/rate-limited",
"title": "Too many requests",
"status": 429,
"detail": "Rate limit exceeded for this account.",
"request_id": "req_EXAMPLE0000000007"
} 503 application/problem+json · Problem
Foreshock is temporarily unavailable.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Retry-After | integer | no | Seconds to wait before retrying. On the decision endpoint, waiting is not a substitute for a verdict: the deposit stays on |
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/unavailable",
"title": "Service unavailable",
"status": 503,
"detail": "Temporarily unavailable.",
"request_id": "req_EXAMPLE0000000009"
} POST /v1/screen/address
Screen one address against authoritative lists
Bearer key
Screens a single address. Layer A covers authoritative lists and is not billed. Layer B covers third-party attribution vendors and is billed per call; whether Layer B runs is governed by your routing and cost-gate configuration.
Every hit and every vendor result carries a snapshot_ref / raw_ref pointing at the immutable, content-addressed snapshot of the raw upstream response that produced it. Answers are references, not bare booleans, so any result can be re-derived and audited later.
Vendor scores are returned exactly as the vendor produced them. They are not rescaled, not merged, and not combined into any Foreshock score.
This endpoint reports on an address. It is not a credit decision — use POST /v1/decide/deposit for that, which is the only endpoint that produces a binding, evidence-backed verdict.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | no | Client-generated key that makes this request safely retryable. A repeat of the same key with an identical body replays the stored response and is not billed again; the same key with a different body returns |
Request body · application/json · ScreenAddressRequest
| Name | Type | Required | Description |
|---|---|---|---|
| chain | Chain | yes | Chain identifier. Values are data, not a fixed vocabulary: the set supported for your account is returned by |
| address | Address | yes | On-chain address, in the canonical textual form for its chain. On chains where the freeze primitive operates on a token account rather than a wallet, state is per token account: two token accounts of the same wallet can be in different states, and must be submitted separately. |
| depth | string | no | How far to screen. |
{
"chain": "example-chain",
"address": "0xEXAMPLEaddress0000000000000000000000000",
"depth": "A"
} Responses
200 application/json · ScreenAddressResponse
Screening result.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Foreshock-Coverage-Health | string | no | Coarse coverage signal for the authorities in scope for this request. This header is a pointer, not a report: on |
| Idempotency-Replayed | boolean | no | Present and |
| RateLimit-Limit | integer | no | Requests permitted in the current window for this account. |
| RateLimit-Remaining | integer | no | Requests remaining in the current window. |
| RateLimit-Reset | integer | no | Seconds until the current window resets. |
| Name | Type | Required | Description |
|---|---|---|---|
| layer_a | LayerAResult | yes | |
| layer_b | LayerBResult | null | yes | Vendor results, or |
| billable_units | integer | yes | Billable units consumed by this call. Layer A is never billed, so a layer-A only response is |
{
"layer_a": {
"hits": [
{
"source": "example_issuer_blacklist",
"hit": true,
"detail": {
"stage": "EXECUTED",
"executed_at": "2026-08-22T14:31:07Z"
},
"snapshot_ref": "cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000001"
},
{
"source": "sanctions_list",
"hit": false,
"list_version": "example-list-v2026.08.24",
"snapshot_ref": "cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000002"
}
],
"latency_ms": 41
},
"layer_b": null,
"billable_units": 0
} 400 application/problem+json · Problem
The request was malformed or failed validation.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "`chain` is required.",
"request_id": "req_EXAMPLE0000000001"
} 401 application/problem+json · Problem
Missing, malformed, or revoked API key.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "API key missing or not recognised.",
"request_id": "req_EXAMPLE0000000002"
} 409 application/problem+json · Problem
The Idempotency-Key was already used with a different request body.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/idempotency-conflict",
"title": "Idempotency key reused with a different body",
"status": 409,
"detail": "Use a new key, or resend the original body to replay the stored result.",
"request_id": "req_EXAMPLE0000000006"
} 429 application/problem+json · Problem
Rate limit exceeded.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Retry-After | integer | no | Seconds to wait before retrying. On the decision endpoint, waiting is not a substitute for a verdict: the deposit stays on |
| RateLimit-Limit | integer | no | Requests permitted in the current window for this account. |
| RateLimit-Remaining | integer | no | Requests remaining in the current window. |
| RateLimit-Reset | integer | no | Seconds until the current window resets. |
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/rate-limited",
"title": "Too many requests",
"status": 429,
"detail": "Rate limit exceeded for this account.",
"request_id": "req_EXAMPLE0000000007"
} 503 application/problem+json · Problem
Foreshock is temporarily unavailable.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Retry-After | integer | no | Seconds to wait before retrying. On the decision endpoint, waiting is not a substitute for a verdict: the deposit stays on |
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/unavailable",
"title": "Service unavailable",
"status": 503,
"detail": "Temporarily unavailable.",
"request_id": "req_EXAMPLE0000000009"
} POST /v1/decide/deposit
Decide whether an inbound deposit may be credited
Bearer key KYB required Fail-closed
The credit gate. Returns CREDIT, HOLD, or REJECT for one inbound deposit, and writes an append-only decision record with an evidence reference.
Fail-closed. There is no branch of this endpoint that returns CREDIT on degradation.
| Condition | Verdict | Reason code | HTTP |
|---|---|---|---|
| Vendor timeout or vendor unavailable | HOLD | SCREENING_DEGRADED | 200 |
| Customer daily budget exhausted | HOLD | BUDGET_EXHAUSTED | 200 |
| A required list is older than its freshness SLO | HOLD | LIST_STALE | 200 |
| A registry entry has not been re-verified in time | HOLD | REGISTRY_ENTRY_STALE | 200 |
| An authority in scope for this deposit is not observable | HOLD | COVERAGE_BLIND_SPOT | 200 |
| Decision could not complete within its latency budget | HOLD | DECISION_TIMEOUT | 200 |
| Foreshock is unavailable | no response body | — | 5xx |
Clients MUST treat any 5xx response from this endpoint as HOLD. Do not credit the deposit, do not retry-until-success and credit on the first 2xx without re-evaluating, and do not fall back to a local allow rule. The official Foreshock SDKs implement 5xx-as-HOLD as the default behaviour and it cannot be disabled by configuration; if you build your own client, this is your obligation. A timeout on your side, a TLS failure, or a DNS failure is the same case: HOLD.
reasons are classification codes, not derivations. They tell you why the deposit was stopped. They do not tell you how the determination was reached, and they never contain an enforcement target address, a path detail, or a score.
Idempotency: send an Idempotency-Key. A repeat of the same key with the same body replays the stored decision, including its original decision_id, and is not billed again.
Access requires a KYB-verified institutional account.
Contract extensions
- x-fail-closed
- true
- x-kyb-required
- true
- x-client-contract
- Any 5xx, connection error, or client-side timeout MUST be treated as verdict HOLD.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | no | Client-generated key that makes this request safely retryable. A repeat of the same key with an identical body replays the stored response and is not billed again; the same key with a different body returns |
Request body · application/json · DecideDepositRequest
| Name | Type | Required | Description |
|---|---|---|---|
| deposit_ref | string | yes | Your own identifier for this deposit. Echoed into the decision record. |
| chain | Chain | yes | Chain identifier. Values are data, not a fixed vocabulary: the set supported for your account is returned by |
| tx_hash | string | yes | Transaction hash of the inbound transfer. |
| from_address | Address | yes | On-chain address, in the canonical textual form for its chain. On chains where the freeze primitive operates on a token account rather than a wallet, state is per token account: two token accounts of the same wallet can be in different states, and must be submitted separately. |
| to_address | Address | yes | On-chain address, in the canonical textual form for its chain. On chains where the freeze primitive operates on a token account rather than a wallet, state is per token account: two token accounts of the same wallet can be in different states, and must be submitted separately. |
| asset | string | yes | Asset symbol or contract reference for the transferred asset. Values are data; the supported set for your account is reflected in |
| amount_usd | string | yes | Amount in USD, as a decimal string to avoid floating-point loss. Used against the thresholds you configured. |
| user_ref | string | no | Your identifier for the depositing user. Foreshock does not perform KYC and stores this only as a reference. |
| kyc_ref | string | no | Your identifier for the user's existing KYC record. A reference only; Foreshock does not perform or evaluate KYC. |
{
"deposit_ref": "deposit-EXAMPLE-0001",
"chain": "example-chain",
"tx_hash": "0xEXAMPLEtxhash000000000000000000000000000000000000000000000000000",
"from_address": "0xEXAMPLEaddress0000000000000000000000001",
"to_address": "0xEXAMPLEaddress0000000000000000000000002",
"asset": "EXAMPLEUSD",
"amount_usd": "25000.00",
"user_ref": "user-EXAMPLE-0001",
"kyc_ref": "kyc-EXAMPLE-0001"
} Responses
200 application/json · DecideDepositResponse
A decision was produced. This includes every degraded case in the table above, which returns 200 with verdict: HOLD and a reason code.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Foreshock-Coverage-Health | string | no | Coarse coverage signal for the authorities in scope for this request. This header is a pointer, not a report: on |
| Idempotency-Replayed | boolean | no | Present and |
| RateLimit-Limit | integer | no | Requests permitted in the current window for this account. |
| RateLimit-Remaining | integer | no | Requests remaining in the current window. |
| RateLimit-Reset | integer | no | Seconds until the current window resets. |
| Name | Type | Required | Description |
|---|---|---|---|
| decision_id | string(uuid) | yes | Identifier of the append-only decision record. Use it to fetch the evidence bundle. Decision records are never updated or deleted. |
| verdict | string | yes |
There is no fourth value, and no path returns |
| risk_level | RiskLevel | yes | Ordinal risk band assigned to a decision, |
| pool | Pool | yes | Segregation pool the funds should be routed to. Keeping pools separate is what prevents differently classified funds from being swept into one collection address. |
| reasons | array<DecisionReason> | yes | Classification codes behind the verdict. Empty only on a clean |
| vendor_disagreement | boolean | yes | Whether third-party vendors returned mutually inconsistent classifications. Reported so you can see the disagreement. Foreshock does not resolve it into a single answer. |
| rule_version | string | yes | Version of your rule set that produced this decision. Rule versions are stored, two-person reviewed, and reproducible, so a past decision can be replayed against the rules that were actually in force at the time. |
| required_actions | array<RequiredAction> | yes | Steps your workflow must complete before this deposit can leave |
| sla_deadline | string | null | no | When the required actions are due under your configured service levels. |
| evidence_ref | SnapshotRef | yes | Content-addressed reference to an immutable snapshot of a raw upstream response, of the form |
| billable_units | integer | yes | Billable units consumed by this decision. |
{
"decision_id": "11111111-1111-4111-8111-111111111111",
"verdict": "HOLD",
"risk_level": "R1",
"pool": "quarantine",
"reasons": [
{
"code": "ACTIVE_ENFORCEMENT_RELATION",
"severity": "R1"
},
{
"code": "VENDOR_HIGH_RISK",
"vendor": "example-vendor",
"raw_ref": "cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000003"
}
],
"vendor_disagreement": false,
"rule_version": "rules-EXAMPLE-2026.08.01",
"required_actions": [
"COLLECT_SOURCE_OF_FUNDS",
"L2_REVIEW"
],
"sla_deadline": "2026-08-24T13:05:00Z",
"evidence_ref": "cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000004",
"billable_units": 1
} 400 application/problem+json · Problem
The request was malformed or failed validation.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "`chain` is required.",
"request_id": "req_EXAMPLE0000000001"
} 401 application/problem+json · Problem
Missing, malformed, or revoked API key.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "API key missing or not recognised.",
"request_id": "req_EXAMPLE0000000002"
} 403 application/problem+json · Problem
The key is valid, but this operation is restricted to KYB-verified institutional accounts. Enforcement data in a non-terminal stage is not available to unverified accounts under any plan.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/kyb-required",
"title": "KYB verification required",
"status": 403,
"detail": "This account has not completed institutional KYB verification.",
"request_id": "req_EXAMPLE0000000004"
} 409 application/problem+json · Problem
The Idempotency-Key was already used with a different request body.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/idempotency-conflict",
"title": "Idempotency key reused with a different body",
"status": 409,
"detail": "Use a new key, or resend the original body to replay the stored result.",
"request_id": "req_EXAMPLE0000000006"
} 429 application/problem+json · Problem
Rate limit exceeded. No verdict was produced. Treat as HOLD. Back off and retry; do not credit the deposit in the meantime.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Retry-After | integer | no | Seconds to wait before retrying. On the decision endpoint, waiting is not a substitute for a verdict: the deposit stays on |
| RateLimit-Limit | integer | no | Requests permitted in the current window for this account. |
| RateLimit-Remaining | integer | no | Requests remaining in the current window. |
| RateLimit-Reset | integer | no | Seconds until the current window resets. |
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/rate-limited",
"title": "Too many requests",
"status": 429,
"detail": "Rate limit exceeded. No verdict was produced; treat as HOLD.",
"fail_closed_action": "HOLD",
"request_id": "req_EXAMPLE0000000008"
} 500 application/problem+json · Problem
Foreshock failed to produce a decision. No verdict exists. Treat as HOLD.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Retry-After | integer | no | Seconds to wait before retrying. On the decision endpoint, waiting is not a substitute for a verdict: the deposit stays on |
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/internal-error",
"title": "Internal error",
"status": 500,
"detail": "No verdict was produced; treat as HOLD.",
"fail_closed_action": "HOLD",
"request_id": "req_EXAMPLE0000000010"
} 503 application/problem+json · Problem
Foreshock is unavailable. No verdict exists. Treat as HOLD. This is the "our side is down" row of the degradation table: it is deliberately not returned as a 200 with a verdict, so that a client which ignores status codes cannot mistake an outage for a decision.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Retry-After | integer | no | Seconds to wait before retrying. On the decision endpoint, waiting is not a substitute for a verdict: the deposit stays on |
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/unavailable",
"title": "Service unavailable",
"status": 503,
"detail": "No verdict was produced; treat as HOLD.",
"fail_closed_action": "HOLD",
"request_id": "req_EXAMPLE0000000011"
} 504 application/problem+json · Problem
The request did not complete in time at the edge. No verdict exists. Treat as HOLD. A client-side timeout is the same case.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Retry-After | integer | no | Seconds to wait before retrying. On the decision endpoint, waiting is not a substitute for a verdict: the deposit stays on |
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/gateway-timeout",
"title": "Gateway timeout",
"status": 504,
"detail": "No verdict was produced; treat as HOLD.",
"fail_closed_action": "HOLD",
"request_id": "req_EXAMPLE0000000012"
} GET /v1/decisions/{decision_id}/evidence
Download the evidence bundle for one decision
Bearer key
Returns the immutable evidence bundle for a single decision the caller made, as a ZIP archive.
Bundle contents:
manifest.json decision fields, evidence_hash, and verification instructions
layer_a/*.json raw response snapshots from each authoritative list source
layer_b/*.json raw vendor response bodies, subject to vendor licence terms
rule/{rule_version}.json
chain/{tx_hash}.json
report.pdfevidence_hash in manifest.json lets you verify the bundle independently, without trusting Foreshock.
Where a vendor's licence does not permit redistribution of its raw response, the layer_b/ directory contains a call record summary plus the vendor name and list version instead of the raw body. The bundle states which form it used.
One decision per request. There is no batch or date-range export of evidence.
Contract extensions
- x-no-bulk-export
- true
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| decision_id | path | string(uuid) | yes | Identifier of a decision previously returned to this account. |
| format | query | string | no | Bundle format. Only |
Responses
200 application/zip · string
The evidence bundle.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Content-Disposition | string | no | Suggested filename for the archive. |
| RateLimit-Limit | integer | no | Requests permitted in the current window for this account. |
| RateLimit-Remaining | integer | no | Requests remaining in the current window. |
| RateLimit-Reset | integer | no | Seconds until the current window resets. |
401 application/problem+json · Problem
Missing, malformed, or revoked API key.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "API key missing or not recognised.",
"request_id": "req_EXAMPLE0000000002"
} 403 application/problem+json · Problem
The key is valid but not entitled to this resource.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "This decision does not belong to the authenticated account.",
"request_id": "req_EXAMPLE0000000003"
} 404 application/problem+json · Problem
No such resource for this account.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/not-found",
"title": "Not found",
"status": 404,
"detail": "No decision with that identifier.",
"request_id": "req_EXAMPLE0000000005"
} 429 application/problem+json · Problem
Rate limit exceeded.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Retry-After | integer | no | Seconds to wait before retrying. On the decision endpoint, waiting is not a substitute for a verdict: the deposit stays on |
| RateLimit-Limit | integer | no | Requests permitted in the current window for this account. |
| RateLimit-Remaining | integer | no | Requests remaining in the current window. |
| RateLimit-Reset | integer | no | Seconds until the current window resets. |
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/rate-limited",
"title": "Too many requests",
"status": 429,
"detail": "Rate limit exceeded for this account.",
"request_id": "req_EXAMPLE0000000007"
} 503 application/problem+json · Problem
Foreshock is temporarily unavailable.
Response headers
| Name | Type | Required | Description |
|---|---|---|---|
| Retry-After | integer | no | Seconds to wait before retrying. On the decision endpoint, waiting is not a substitute for a verdict: the deposit stays on |
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
{
"type": "https://foreshock.org/problems/unavailable",
"title": "Service unavailable",
"status": 503,
"detail": "Temporarily unavailable.",
"request_id": "req_EXAMPLE0000000009"
} Webhooks
Inbound to you exposure.match
A match between an enforcement event and one of your addresses was created or updated
Sent when a match involving one of your own addresses is created, or when its stage or severity changes.
Payload carries the same fields as a match in GET /v1/exposure: an opaque reference to your address, the relation, the severity, the stage, the timestamp, and an evidence reference. It does not identify the subject of the enforcement event.
See #/components/schemas/WebhookEnvelope for delivery, signature, and retry semantics, and the request headers below for the verification inputs.
Payload · ExposureMatchEvent
| Name | Type | Required | Description |
|---|---|---|---|
| event_id | string(uuid) | yes | Stable identifier for this event. Constant across retries. Deduplicate on this. |
| event_type | string | yes | Type of this event. Mirrors the |
| occurred_at | string(date-time) | yes | When the event occurred. Retries do not change this value. |
| match | ExposureMatch | yes | One match between an enforcement event and an entry in your own address book. This object is the complete match record. There is no expanded, verbose, or premium form of it that adds the subject address, any path detail, or any score, and no parameter that requests one. |
{
"event_id": "44444444-4444-4444-8444-444444444444",
"event_type": "exposure.match",
"occurred_at": "2026-08-24T09:04:11Z",
"match": {
"customer_address_ref": "caref_EXAMPLE0000000000000001",
"relation": "PATH",
"severity": "R1",
"stage": "PROPOSED",
"observed_at": "2026-08-24T09:04:11Z",
"evidence_ref": "cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000000"
}
} Responses
2XX
Acknowledged. Return any 2xx within 5 seconds. Anything else, or no response, is treated as a failed delivery and re-sent under the retry envelope.
default
Treated as a failed delivery. Delivery is retried with exponential backoff for up to 24 hours, after which the event goes to a dead-letter queue and an alert is raised on your account.
Inbound to you exposure.revoked
A match was removed from your match set
Sent when a previously delivered match is removed from your match set.
Removals are always announced as their own event and are never silent. If they were silent, your local list would diverge from ours without you knowing.
A revocation is not a statement that the address is safe. It states only that the match is no longer in your active match set. Treat it as a change of state, not as a clearance.
Payload · ExposureRevokedEvent
| Name | Type | Required | Description |
|---|---|---|---|
| event_id | string(uuid) | yes | Stable identifier for this event. Constant across retries. Deduplicate on this. |
| event_type | string | yes | Type of this event. Mirrors the |
| occurred_at | string(date-time) | yes | When the event occurred. Retries do not change this value. |
| customer_address_ref | CustomerAddressRef | yes | Opaque reference to one entry in your own address book. Stable for the lifetime of that entry and resolvable only by your account. It is not the address and cannot be reversed into one. |
| stage | RevocationStage | yes | Terminal stage that removed a match from the active set.
|
| evidence_ref | SnapshotRef | yes | Content-addressed reference to an immutable snapshot of a raw upstream response, of the form |
{
"event_id": "55555555-5555-4555-8555-555555555555",
"event_type": "exposure.revoked",
"occurred_at": "2026-08-24T10:12:00Z",
"customer_address_ref": "caref_EXAMPLE0000000000000001",
"stage": "REVOKED",
"evidence_ref": "cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000005"
} Responses
2XX
Acknowledged.
default
Treated as a failed delivery and retried under the 24-hour envelope.
Inbound to you coverage.health_changed
The observability health of an authority in scope for your account changed
Sent when an authority in scope for your account becomes unobservable, becomes observable again, exceeds its lag threshold, or has a registry entry that is no longer within its verification window.
This is the blind-spot notification. On receipt, treat the affected scope as unverified and fall back to a more conservative policy until health is restored.
Payload · CoverageHealthChangedEvent
| Name | Type | Required | Description |
|---|---|---|---|
| event_id | string(uuid) | yes | Stable identifier for this event. Constant across retries. Deduplicate on this. |
| event_type | string | yes | Type of this event. Mirrors the |
| occurred_at | string(date-time) | yes | When the event occurred. Retries do not change this value. |
| coverage_health | array<CoverageHealthEntry> | yes | Current health of the affected authorities. |
{
"event_id": "66666666-6666-4666-8666-666666666666",
"event_type": "coverage.health_changed",
"occurred_at": "2026-08-24T10:20:00Z",
"coverage_health": [
{
"authority_id": "authority-EXAMPLE-0002",
"observable": false,
"verified_at": "2026-08-23T21:40:00Z",
"collector_lag_s": 1
}
]
} Responses
2XX
Acknowledged.
default
Treated as a failed delivery and retried under the 24-hour envelope.
Delivery headers
| Name | Type | Required | Description |
|---|---|---|---|
| Foreshock-Signature | string | yes | Signature over the delivery, formatted as Verify before parsing the body: 1. Compute the HMAC over the raw bytes, not a re-serialized object. 2. Compare in constant time. 3. Reject if During secret rotation a delivery may carry more than one |
| Foreshock-Signature-Timestamp | integer | yes | Unix seconds at which the delivery was signed. Same value as |
| Foreshock-Delivery-Id | string(uuid) | yes | Unique identifier of this delivery attempt. Distinct from |
| Foreshock-Event-Type | string | yes | Event type of this delivery. Mirrors |
| Foreshock-Webhook-Attempt | integer | yes | 1-based attempt number for this event. Increments on each retry. |
Error model
CREDIT HOLD REJECT
RFC 9457 problem detail. All non-2xx responses use this shape.
fail_closed_action is present whenever the failure occurred on a decision path. Its only defined value is HOLD. It is a machine-readable restatement of the client obligation, not a verdict: a verdict only ever arrives in a 200 response body.
| Name | Type | Required | Description |
|---|---|---|---|
| type | string(uri) | yes | Stable URI identifying the problem class. |
| title | string | yes | Short, human-readable summary of the problem class. |
| status | integer | yes | HTTP status code, repeated for convenience. |
| detail | string | no | Human-readable explanation specific to this occurrence. |
| fail_closed_action | string | no | Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always |
| request_id | string | no | Identifier for this request, for support correlation. |
Problem responses
| Response | Status | Client action | Description |
|---|---|---|---|
| BadRequest | 400 | — | The request was malformed or failed validation. |
| Unauthorized | 401 | — | Missing, malformed, or revoked API key. |
| Forbidden | 403 | — | The key is valid but not entitled to this resource. |
| ForbiddenKyb | 403 | — | The key is valid, but this operation is restricted to KYB-verified institutional accounts. Enforcement data in a non-terminal stage is not available to unverified accounts under any plan. |
| NotFound | 404 | — | No such resource for this account. |
| IdempotencyConflict | 409 | — | The |
| TooManyRequests | 429 | — | Rate limit exceeded. |
| TooManyRequestsFailClosed | 429 | HOLD | Rate limit exceeded. No verdict was produced. Treat as |
| ServiceUnavailable | 503 | — | Foreshock is temporarily unavailable. |
| InternalErrorFailClosed | 500 | HOLD | Foreshock failed to produce a decision. No verdict exists. Treat as |
| ServiceUnavailableFailClosed | 503 | HOLD | Foreshock is unavailable. No verdict exists. Treat as |
| GatewayTimeoutFailClosed | 504 | HOLD | The request did not complete in time at the edge. No verdict exists. Treat as |