Foreshock

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.

Contract version
1.0.0
Contract digest
0c75891a35d8
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 next_cursor of a previous response. Omit to start from the beginning of your currently retained window. Cursor contents are not structured and must not be parsed, constructed, or ordered by the client.

Constraints maxLength=512

Example eyJFWEFNUExFIjoiY3Vyc29yLXBsYWNlaG9sZGVyIn0

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.

Constraints minimum=1 maximum=200 default=100

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.

Example 600

RateLimit-Remaining integer no

Requests remaining in the current window.

Example 597

RateLimit-Reset integer no

Seconds until the current window resets.

Example 42

Name Type Required Description
as_of string(date-time) yes

Instant this response describes.

Constraints format=date-time

Example 2026-08-24T09:05:00Z

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 since on the next call, or null when you are caught up. Opaque; do not parse.

Example eyJFWEFNUExFIjoibmV4dC1jdXJzb3ItcGxhY2Vob2xkZXIifQ

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 HOLD until a 200 is received.

Example 30

RateLimit-Limit integer no

Requests permitted in the current window for this account.

Example 600

RateLimit-Remaining integer no

Requests remaining in the current window.

Example 597

RateLimit-Reset integer no

Seconds until the current window resets.

Example 42

Name Type Required Description
type string(uri) yes

Stable URI identifying the problem class.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 HOLD until a 200 is received.

Example 30

Name Type Required Description
type string(uri) yes

Stable URI identifying the problem class.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

Example 600

RateLimit-Remaining integer no

Requests remaining in the current window.

Example 597

RateLimit-Reset integer no

Seconds until the current window resets.

Example 42

Name Type Required Description
as_of string(date-time) yes

Instant this response describes.

Constraints format=date-time

Example 2026-08-24T09:05:00Z

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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 HOLD until a 200 is received.

Example 30

RateLimit-Limit integer no

Requests permitted in the current window for this account.

Example 600

RateLimit-Remaining integer no

Requests remaining in the current window.

Example 597

RateLimit-Reset integer no

Seconds until the current window resets.

Example 42

Name Type Required Description
type string(uri) yes

Stable URI identifying the problem class.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 HOLD until a 200 is received.

Example 30

Name Type Required Description
type string(uri) yes

Stable URI identifying the problem class.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 409. Keys are scoped to your account and retained for 24 hours. Use a UUID.

Constraints minLength=8 maxLength=255

Example 22222222-2222-4222-8222-222222222222

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 GET /v1/coverage. New chains appear without a version bump of this document.

Constraints minLength=1 maxLength=64

Example example-chain

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.

Constraints minLength=1 maxLength=128

Example 0xEXAMPLEaddress0000000000000000000000000

depth string no

How far to screen. A runs authoritative lists only and is not billed. B additionally permits third-party vendor calls, subject to your routing rules, cost gate, and cache; requesting B does not guarantee a vendor call is made.

Values AB

Constraints default=A

Example A

{
  "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. ok means every relevant authority is currently observable and within its lag and verification thresholds; degraded means at least one is not.

This header is a pointer, not a report: on degraded, call GET /v1/coverage for the per-authority detail. It exists so that coverage health is never absent from a response, without altering the response body.

Values okdegraded

Example ok

Idempotency-Replayed boolean no

Present and true when this response was replayed from a stored idempotent result.

Example true

RateLimit-Limit integer no

Requests permitted in the current window for this account.

Example 600

RateLimit-Remaining integer no

Requests remaining in the current window.

Example 597

RateLimit-Reset integer no

Seconds until the current window resets.

Example 42

Name Type Required Description
layer_a LayerAResult yes
layer_b LayerBResult | null yes

Vendor results, or null when no vendor call was made — because depth was A, because your cost gate suppressed it, or because a cached result covered it.

billable_units integer yes

Billable units consumed by this call. Layer A is never billed, so a layer-A only response is 0.

Constraints minimum=0

Example 0

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 HOLD until a 200 is received.

Example 30

RateLimit-Limit integer no

Requests permitted in the current window for this account.

Example 600

RateLimit-Remaining integer no

Requests remaining in the current window.

Example 597

RateLimit-Reset integer no

Seconds until the current window resets.

Example 42

Name Type Required Description
type string(uri) yes

Stable URI identifying the problem class.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 HOLD until a 200 is received.

Example 30

Name Type Required Description
type string(uri) yes

Stable URI identifying the problem class.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

ConditionVerdictReason codeHTTP
Vendor timeout or vendor unavailableHOLDSCREENING_DEGRADED200
Customer daily budget exhaustedHOLDBUDGET_EXHAUSTED200
A required list is older than its freshness SLOHOLDLIST_STALE200
A registry entry has not been re-verified in timeHOLDREGISTRY_ENTRY_STALE200
An authority in scope for this deposit is not observableHOLDCOVERAGE_BLIND_SPOT200
Decision could not complete within its latency budgetHOLDDECISION_TIMEOUT200
Foreshock is unavailableno 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 409. Keys are scoped to your account and retained for 24 hours. Use a UUID.

Constraints minLength=8 maxLength=255

Example 22222222-2222-4222-8222-222222222222

Request body · application/json · DecideDepositRequest

Name Type Required Description
deposit_ref string yes

Your own identifier for this deposit. Echoed into the decision record.

Constraints maxLength=128

Example deposit-EXAMPLE-0001

chain Chain yes

Chain identifier. Values are data, not a fixed vocabulary: the set supported for your account is returned by GET /v1/coverage. New chains appear without a version bump of this document.

Constraints minLength=1 maxLength=64

Example example-chain

tx_hash string yes

Transaction hash of the inbound transfer.

Constraints maxLength=128

Example 0xEXAMPLEtxhash000000000000000000000000000000000000000000000000000

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.

Constraints minLength=1 maxLength=128

Example 0xEXAMPLEaddress0000000000000000000000000

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.

Constraints minLength=1 maxLength=128

Example 0xEXAMPLEaddress0000000000000000000000000

asset string yes

Asset symbol or contract reference for the transferred asset. Values are data; the supported set for your account is reflected in GET /v1/coverage.

Constraints maxLength=128

Example EXAMPLEUSD

amount_usd string yes

Amount in USD, as a decimal string to avoid floating-point loss. Used against the thresholds you configured.

Constraints pattern=^[0-9]+(\.[0-9]+)?$

Example 25000.00

user_ref string no

Your identifier for the depositing user. Foreshock does not perform KYC and stores this only as a reference.

Constraints maxLength=128

Example user-EXAMPLE-0001

kyc_ref string no

Your identifier for the user's existing KYC record. A reference only; Foreshock does not perform or evaluate KYC.

Constraints maxLength=128

Example kyc-EXAMPLE-0001

{
  "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. ok means every relevant authority is currently observable and within its lag and verification thresholds; degraded means at least one is not.

This header is a pointer, not a report: on degraded, call GET /v1/coverage for the per-authority detail. It exists so that coverage health is never absent from a response, without altering the response body.

Values okdegraded

Example ok

Idempotency-Replayed boolean no

Present and true when this response was replayed from a stored idempotent result.

Example true

RateLimit-Limit integer no

Requests permitted in the current window for this account.

Example 600

RateLimit-Remaining integer no

Requests remaining in the current window.

Example 597

RateLimit-Reset integer no

Seconds until the current window resets.

Example 42

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.

Constraints format=uuid

Example 11111111-1111-4111-8111-111111111111

verdict string yes
  • CREDIT — the deposit may be credited.
  • HOLD — the deposit must not be credited yet. Every degraded path resolves here.
  • REJECT — the deposit must not be credited.

There is no fourth value, and no path returns CREDIT when any input was degraded, stale, unavailable, or over budget.

Values CREDITHOLDREJECT

Example HOLD

risk_level RiskLevel yes

Ordinal risk band assigned to a decision, R0 (lowest) through R4 (highest). A band, not a score; no numeric score is published.

Values R0R1R2R3R4

Example R1

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.

Values cleantaggedquarantine

Example quarantine

reasons array<DecisionReason> yes

Classification codes behind the verdict. Empty only on a clean CREDIT.

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.

Example

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.

Example rules-EXAMPLE-2026.08.01

required_actions array<RequiredAction> yes

Steps your workflow must complete before this deposit can leave HOLD.

sla_deadline string | null no

When the required actions are due under your configured service levels. null when no deadline applies.

Constraints format=date-time

Example 2026-08-24T13:05:00Z

evidence_ref SnapshotRef yes

Content-addressed reference to an immutable snapshot of a raw upstream response, of the form cas:sha256:<hex>. Snapshots are append-only and version-locked, so a reference resolves to the same bytes forever. Derived conclusions can be recomputed; the raw data behind them cannot be rewritten.

Constraints pattern=^cas:sha256:[0-9a-zA-Z]{8,128}$

Example cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000000

billable_units integer yes

Billable units consumed by this decision.

Constraints minimum=0

Example 1

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 HOLD until a 200 is received.

Example 30

RateLimit-Limit integer no

Requests permitted in the current window for this account.

Example 600

RateLimit-Remaining integer no

Requests remaining in the current window.

Example 597

RateLimit-Reset integer no

Seconds until the current window resets.

Example 42

Name Type Required Description
type string(uri) yes

Stable URI identifying the problem class.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 HOLD until a 200 is received.

Example 30

Name Type Required Description
type string(uri) yes

Stable URI identifying the problem class.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 HOLD until a 200 is received.

Example 30

Name Type Required Description
type string(uri) yes

Stable URI identifying the problem class.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 HOLD until a 200 is received.

Example 30

Name Type Required Description
type string(uri) yes

Stable URI identifying the problem class.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.pdf

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

Constraints format=uuid

Example 11111111-1111-4111-8111-111111111111

format query string no

Bundle format. Only bundle is defined in v1.

Values bundle

Constraints default=bundle

Responses

200 application/zip · string

The evidence bundle.

Response headers

Name Type Required Description
Content-Disposition string no

Suggested filename for the archive.

Example attachment; filename="foreshock-evidence-11111111-1111-4111-8111-111111111111.zip"

RateLimit-Limit integer no

Requests permitted in the current window for this account.

Example 600

RateLimit-Remaining integer no

Requests remaining in the current window.

Example 597

RateLimit-Reset integer no

Seconds until the current window resets.

Example 42

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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 HOLD until a 200 is received.

Example 30

RateLimit-Limit integer no

Requests permitted in the current window for this account.

Example 600

RateLimit-Remaining integer no

Requests remaining in the current window.

Example 597

RateLimit-Reset integer no

Seconds until the current window resets.

Example 42

Name Type Required Description
type string(uri) yes

Stable URI identifying the problem class.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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 HOLD until a 200 is received.

Example 30

Name Type Required Description
type string(uri) yes

Stable URI identifying the problem class.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

{
  "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.

Constraints format=uuid

Example 44444444-4444-4444-8444-444444444444

event_type string yes

Type of this event. Mirrors the Foreshock-Event-Type header.

Values exposure.matchexposure.revokedcoverage.health_changed

Example exposure.match

occurred_at string(date-time) yes

When the event occurred. Retries do not change this value.

Constraints format=date-time

Example 2026-08-24T09:04:11Z

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.

Example [object Object]

{
  "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.

Constraints format=uuid

Example 44444444-4444-4444-8444-444444444444

event_type string yes

Type of this event. Mirrors the Foreshock-Event-Type header.

Values exposure.matchexposure.revokedcoverage.health_changed

Example exposure.match

occurred_at string(date-time) yes

When the event occurred. Retries do not change this value.

Constraints format=date-time

Example 2026-08-24T09:04:11Z

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.

Constraints minLength=1 maxLength=128

Example caref_EXAMPLE0000000000000001

stage RevocationStage yes

Terminal stage that removed a match from the active set.

  • REVOKED — the underlying event was withdrawn. Not a clearance.
  • EXPIRED — the event did not execute within its configured window.
  • STALE — the underlying data can no longer be trusted. Not a clearance either; treat the affected scope as a blind spot and fall back to a more conservative policy.

Values REVOKEDEXPIREDSTALE

Example REVOKED

evidence_ref SnapshotRef yes

Content-addressed reference to an immutable snapshot of a raw upstream response, of the form cas:sha256:<hex>. Snapshots are append-only and version-locked, so a reference resolves to the same bytes forever. Derived conclusions can be recomputed; the raw data behind them cannot be rewritten.

Constraints pattern=^cas:sha256:[0-9a-zA-Z]{8,128}$

Example cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000000

{
  "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.

Constraints format=uuid

Example 44444444-4444-4444-8444-444444444444

event_type string yes

Type of this event. Mirrors the Foreshock-Event-Type header.

Values exposure.matchexposure.revokedcoverage.health_changed

Example exposure.match

occurred_at string(date-time) yes

When the event occurred. Retries do not change this value.

Constraints format=date-time

Example 2026-08-24T09:04:11Z

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 t=<unix_seconds>,v1=<hex> where v1 is HMAC-SHA256(signing_secret, "<t>.<raw_request_body>") in lowercase hex.

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 |now - t| > 300 seconds, to bound replay.

During secret rotation a delivery may carry more than one v1= value; accept the delivery if any of them verifies.

Example t=1700000000,v1=EXAMPLEsignature00000000000000000000000000000000000000000000000

Foreshock-Signature-Timestamp integer yes

Unix seconds at which the delivery was signed. Same value as t in the signature.

Example 1700000000

Foreshock-Delivery-Id string(uuid) yes

Unique identifier of this delivery attempt. Distinct from event_id: retries of the same event carry the same event_id and different Foreshock-Delivery-Id. Deduplicate on event_id, log on Foreshock-Delivery-Id.

Example 33333333-3333-4333-8333-333333333333

Foreshock-Event-Type string yes

Event type of this delivery. Mirrors event_type in the body.

Values exposure.matchexposure.revokedcoverage.health_changed

Example exposure.match

Foreshock-Webhook-Attempt integer yes

1-based attempt number for this event. Increments on each retry.

Example 1

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.

Constraints format=uri

Example https://foreshock.org/problems/unavailable

title string yes

Short, human-readable summary of the problem class.

Example Service unavailable

status integer yes

HTTP status code, repeated for convenience.

Example 503

detail string no

Human-readable explanation specific to this occurrence.

Example No verdict was produced; treat as HOLD.

fail_closed_action string no

Present on decision-path failures. Instructs the client what to do in the absence of a verdict. Always HOLD.

Values HOLD

request_id string no

Identifier for this request, for support correlation.

Example req_EXAMPLE0000000013

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 Idempotency-Key was already used with a different request body.

TooManyRequests 429 —

Rate limit exceeded.

TooManyRequestsFailClosed 429 HOLD

Rate limit exceeded. No verdict was produced. Treat as HOLD. Back off and retry; do not credit the deposit in the meantime.

ServiceUnavailable 503 —

Foreshock is temporarily unavailable.

InternalErrorFailClosed 500 HOLD

Foreshock failed to produce a decision. No verdict exists. Treat as HOLD.

ServiceUnavailableFailClosed 503 HOLD

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.

GatewayTimeoutFailClosed 504 HOLD

The request did not complete in time at the edge. No verdict exists. Treat as HOLD. A client-side timeout is the same case.