Foreshock

API reference

Types, enumerations, and reason codes

Every named schema in the contract, including the full reason-code enumeration. Reproduced verbatim from the contract.

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

Enumerations

Relation

An enum indicating how the customer address relates to the enforcement event.

  • DIRECT — the customer address is itself the subject of the event.
  • INDIRECT — the event does not name the customer address, but is related to it.
  • PATH — the relation is expressed at the level of the inbound route, not at the level of any individual party on it.

The values are ordinal in specificity only. No further detail about the relation is available through this API by any parameter, plan, or mode.

DIRECTINDIRECTPATH

Severity

Ordinal severity band of the match. R0 is the lowest band and R2 the highest band exposed on the exposure endpoint.

A band is not a score. Foreshock publishes no numeric risk score, on this endpoint or any other, in any mode.

R0R1R2

RiskLevel

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

R0R1R2R3R4

MatchStage

Stage of the enforcement event underlying this match, at observed_at.

  • PROPOSED — the event exists and is not yet executed; treat as live and begin declining inbound funds on the matched address.
  • CONFIRMING — the event is progressing toward execution.
  • EXECUTED — the event has been carried out.

Matches that leave this set are announced through the exposure.revoked webhook and disappear from subsequent pages. They are never removed silently.

PROPOSEDCONFIRMINGEXECUTED

RevocationStage

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.

REVOKEDEXPIREDSTALE

Pool

Segregation pool the funds should be routed to. Keeping pools separate is what prevents differently classified funds from being swept into one collection address.

cleantaggedquarantine

ReasonCode

Classification code explaining why a verdict came out the way it did.

Reason codes classify. They do not describe derivation, and none of them is accompanied by an enforcement target address, a path detail, or a score.

Risk classifications

CodeMeaning
ACTIVE_ENFORCEMENT_RELATIONThe counterparty relates to an enforcement event that is not in a terminal stage.
EXECUTED_ENFORCEMENT_MATCHThe counterparty relates to an enforcement event that has been executed.
SANCTIONS_LIST_MATCHThe counterparty appears on an authoritative sanctions list.
ISSUER_LIST_MATCHThe counterparty appears on an issuer-published enforcement list.
AUTHORITY_DRIFTThe on-chain control authority for an asset in scope no longer matches its registry entry.
PATH_HEALTH_DEGRADEDThe inbound route for this deposit is currently not healthy. Route-level, not party-level.
VENDOR_HIGH_RISKA third-party vendor returned its own highest-risk classification. Reported verbatim, not rescaled.
VENDOR_DISAGREEMENTVendors returned classifications that are not mutually consistent. Reported, never reconciled into one answer.
AMOUNT_THRESHOLDThe amount crossed a threshold you configured for elevated handling.
CUSTOMER_RULEA rule in your active rule version produced this outcome.
MANUAL_OVERRIDEA human operator recorded this outcome.

Degradations — every one of these yields HOLD with HTTP 200

CodeMeaning
SCREENING_DEGRADEDA screening source timed out or was unavailable.
BUDGET_EXHAUSTEDYour configured screening budget for the period is spent.
LIST_STALEA required list is older than its freshness objective.
REGISTRY_ENTRY_STALEA registry entry in scope has not been re-verified within its window.
COVERAGE_BLIND_SPOTAn authority in scope for this deposit is not currently observable.
DECISION_TIMEOUTThe decision could not be completed within its latency budget.

There is no reason code that permits a credit on degradation, and none will be added. New codes may be introduced in a minor version; clients must treat an unrecognised code as at least as serious as the accompanying risk_level.

ACTIVE_ENFORCEMENT_RELATIONEXECUTED_ENFORCEMENT_MATCHSANCTIONS_LIST_MATCHISSUER_LIST_MATCHAUTHORITY_DRIFTPATH_HEALTH_DEGRADEDVENDOR_HIGH_RISKVENDOR_DISAGREEMENTAMOUNT_THRESHOLDCUSTOMER_RULEMANUAL_OVERRIDESCREENING_DEGRADEDBUDGET_EXHAUSTEDLIST_STALEREGISTRY_ENTRY_STALECOVERAGE_BLIND_SPOTDECISION_TIMEOUT

RequiredAction

An operational step your workflow is expected to complete before the deposit can move out of HOLD.

  • COLLECT_SOURCE_OF_FUNDS — obtain source-of-funds documentation from the user.
  • L1_REVIEW — first-line analyst review.
  • L2_REVIEW — escalated review.
  • QUARANTINE_ROUTE — route the funds to the quarantine pool and keep them out of shared collection paths.
  • NOTIFY_COMPLIANCE_OFFICER — notify the responsible compliance officer.
  • OPEN_CASE — open a case record for tracking and later audit.
  • RE_DECIDE_AFTER_RECOVERY — the hold was caused by a degradation; call the decision endpoint again once the degradation clears rather than crediting.

COLLECT_SOURCE_OF_FUNDSL1_REVIEWL2_REVIEWQUARANTINE_ROUTENOTIFY_COMPLIANCE_OFFICEROPEN_CASERE_DECIDE_AFTER_RECOVERY

Scalar types

Chain string

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.

minLength=1 maxLength=64

example-chain

Address string

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.

minLength=1 maxLength=128

0xEXAMPLEaddress0000000000000000000000000

AuthorityId string

Identifier of one enforcement authority — a single "who can freeze what, where" entry — as it appears in your account's coverage. Treat it as an opaque, stable string. The live set of authorities is returned by GET /v1/coverage to authenticated customers and is not enumerated in this document.

minLength=1 maxLength=128

authority-EXAMPLE-0001

SnapshotRef string

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.

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

cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000000

CustomerAddressRef string

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.

minLength=1 maxLength=128

caref_EXAMPLE0000000000000001

Object schemas

Problem

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

CoverageHealthEntry

Observability health of one enforcement authority in scope for your account.

Read observable: false, a verified_at outside its window, or an abnormal collector_lag_s as a blind spot, not as an all-clear.

Name Type Required Description
authority_id AuthorityId yes

Identifier of one enforcement authority — a single "who can freeze what, where" entry — as it appears in your account's coverage. Treat it as an opaque, stable string. The live set of authorities is returned by GET /v1/coverage to authenticated customers and is not enumerated in this document.

Constraints minLength=1 maxLength=128

Example authority-EXAMPLE-0001

observable boolean yes

Whether this authority is currently observable to Foreshock. Observability is a property of the authority, not a Foreshock capability, and it can change without notice.

Example true

verified_at string(date-time) yes

When this authority's registry entry was last verified against the chain. An entry that has not been re-verified within its window is reported as degraded and downgrades any decision that depends on it.

Constraints format=date-time

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

collector_lag_s integer yes

Current lag, in seconds, between chain state and Foreshock's view of this authority.

Constraints minimum=0

Example 3

{
  "authority_id": "authority-EXAMPLE-0001",
  "observable": true,
  "verified_at": "2026-08-24T09:00:00Z",
  "collector_lag_s": 3
}

ExposureMatch

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.

Name Type Required Description
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

relation Relation yes

An enum indicating how the customer address relates to the enforcement event.

  • DIRECT — the customer address is itself the subject of the event.
  • INDIRECT — the event does not name the customer address, but is related to it.
  • PATH — the relation is expressed at the level of the inbound route, not at the level of any individual party on it.

The values are ordinal in specificity only. No further detail about the relation is available through this API by any parameter, plan, or mode.

Values DIRECTINDIRECTPATH

Example PATH

severity Severity yes

Ordinal severity band of the match. R0 is the lowest band and R2 the highest band exposed on the exposure endpoint.

A band is not a score. Foreshock publishes no numeric risk score, on this endpoint or any other, in any mode.

Values R0R1R2

Example R1

stage MatchStage yes

Stage of the enforcement event underlying this match, at observed_at.

  • PROPOSED — the event exists and is not yet executed; treat as live and begin declining inbound funds on the matched address.
  • CONFIRMING — the event is progressing toward execution.
  • EXECUTED — the event has been carried out.

Matches that leave this set are announced through the exposure.revoked webhook and disappear from subsequent pages. They are never removed silently.

Values PROPOSEDCONFIRMINGEXECUTED

Example PROPOSED

observed_at string(date-time) yes

When Foreshock observed this match.

Constraints format=date-time

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

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

{
  "customer_address_ref": "caref_EXAMPLE0000000000000001",
  "relation": "PATH",
  "severity": "R1",
  "stage": "PROPOSED",
  "observed_at": "2026-08-24T09:04:11Z",
  "evidence_ref": "cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000000"
}

ExposureResponse

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"
}

CoverageResponse

Coverage health for the authorities in scope for the authenticated account. Scope is per-customer; this is not a directory of Foreshock's registry.

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
    }
  ]
}

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"
}

LayerAHit

Result from one authoritative list source, with a reference to the raw response snapshot that produced it. Answers are references rather than bare booleans so that any result can be re-derived and audited.

Name Type Required Description
source string yes

Identifier of the list source. Values are data; the set available to your account is not enumerated in this document.

Example example_issuer_blacklist

hit boolean yes

Whether the address is present on this source.

Example true

detail object no

Source-specific detail about the hit, present only when hit is true. Contents vary by source and are passed through as the source expressed them.

list_version string no

Version identifier of the list as published by its source.

Example example-list-v2026.08.24

snapshot_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

LayerAResult

Name Type Required Description
hits array<LayerAHit> yes

One entry per authoritative list source consulted, hit or not.

latency_ms integer yes

Wall-clock time spent on layer A, in milliseconds.

Constraints minimum=0

Example 41

VendorResult

One third-party vendor's result, reproduced as the vendor expressed it.

verdict, score, and categories are the vendor's own. They are not translated, rescaled, or merged. Scores from different vendors are on different scales and are not comparable; Foreshock does not convert between them, because converting would manufacture precision that does not exist and would break the audit trail back to the vendor's original judgement.

Name Type Required Description
vendor string yes

Vendor name.

Example example-vendor

verdict string yes

The vendor's own classification, verbatim.

Example EXAMPLE_HIGH

score number | null no

The vendor's own score, if the vendor produces one, on the vendor's own scale. null when the vendor does not provide one. Never normalised, never combined with any other source, and never a Foreshock score.

Example 87

categories array<string> no

The vendor's own labels, untranslated.

Example example-category-aexample-category-b

list_version string no

Version identifier of the vendor data set consulted.

Example example-vendor-2026.08.24

raw_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

LayerBResult

Third-party vendor results, listed side by side. Foreshock does not reconcile them and does not emit a combined verdict or a combined score.

Name Type Required Description
results array<VendorResult> yes
latency_ms integer yes

Wall-clock time spent on layer B, in milliseconds.

Constraints minimum=0

Example 612

ScreenAddressResponse

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
}

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"
}

DecisionReason

One classification behind the verdict. A reason states what class of finding applied. It does not state how the finding was made.

Name Type Required Description
code ReasonCode yes

Classification code explaining why a verdict came out the way it did.

Reason codes classify. They do not describe derivation, and none of them is accompanied by an enforcement target address, a path detail, or a score.

Risk classifications

CodeMeaning
ACTIVE_ENFORCEMENT_RELATIONThe counterparty relates to an enforcement event that is not in a terminal stage.
EXECUTED_ENFORCEMENT_MATCHThe counterparty relates to an enforcement event that has been executed.
SANCTIONS_LIST_MATCHThe counterparty appears on an authoritative sanctions list.
ISSUER_LIST_MATCHThe counterparty appears on an issuer-published enforcement list.
AUTHORITY_DRIFTThe on-chain control authority for an asset in scope no longer matches its registry entry.
PATH_HEALTH_DEGRADEDThe inbound route for this deposit is currently not healthy. Route-level, not party-level.
VENDOR_HIGH_RISKA third-party vendor returned its own highest-risk classification. Reported verbatim, not rescaled.
VENDOR_DISAGREEMENTVendors returned classifications that are not mutually consistent. Reported, never reconciled into one answer.
AMOUNT_THRESHOLDThe amount crossed a threshold you configured for elevated handling.
CUSTOMER_RULEA rule in your active rule version produced this outcome.
MANUAL_OVERRIDEA human operator recorded this outcome.

Degradations — every one of these yields HOLD with HTTP 200

CodeMeaning
SCREENING_DEGRADEDA screening source timed out or was unavailable.
BUDGET_EXHAUSTEDYour configured screening budget for the period is spent.
LIST_STALEA required list is older than its freshness objective.
REGISTRY_ENTRY_STALEA registry entry in scope has not been re-verified within its window.
COVERAGE_BLIND_SPOTAn authority in scope for this deposit is not currently observable.
DECISION_TIMEOUTThe decision could not be completed within its latency budget.

There is no reason code that permits a credit on degradation, and none will be added. New codes may be introduced in a minor version; clients must treat an unrecognised code as at least as serious as the accompanying risk_level.

Values ACTIVE_ENFORCEMENT_RELATIONEXECUTED_ENFORCEMENT_MATCHSANCTIONS_LIST_MATCHISSUER_LIST_MATCHAUTHORITY_DRIFTPATH_HEALTH_DEGRADEDVENDOR_HIGH_RISKVENDOR_DISAGREEMENTAMOUNT_THRESHOLDCUSTOMER_RULEMANUAL_OVERRIDESCREENING_DEGRADEDBUDGET_EXHAUSTEDLIST_STALEREGISTRY_ENTRY_STALECOVERAGE_BLIND_SPOTDECISION_TIMEOUT

Example ACTIVE_ENFORCEMENT_RELATION

severity RiskLevel | null no

Severity band this reason contributed, when the reason carries one. A band, not a score.

Values R0R1R2R3R4

Example R1

vendor string no

Vendor name, present only on reasons that originate from a third-party vendor.

Example example-vendor

raw_ref SnapshotRef | null no

Reference to the immutable snapshot of the raw upstream response behind this reason, when one exists.

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

Example cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000000

DecideDepositResponse

The verdict for one deposit.

A verdict only ever arrives in a 200 response. Any 5xx means no verdict exists and the client must apply HOLD.

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
}

WebhookEnvelope

Fields common to every webhook delivery.

Delivery semantics — at least once. The same event_id may arrive more than once, and out of order relative to other events. Deduplicate on event_id and order your own state by occurred_at.

Retry envelope — 24 hours. A delivery is successful only on a 2xx returned within 5 seconds. Anything else is retried with exponential backoff and jitter for up to 24 hours from occurred_at. After that the event moves to a dead-letter queue, an alert is raised on your account, and the events remain replayable from the console.

Signature. Verify Foreshock-Signature over the raw request body before parsing it. See that header's description for the exact procedure.

A missed webhook must not be read as an all-clear. GET /v1/exposure with your last cursor is the authoritative reconciliation path; the webhook stream is a latency optimisation over it.

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

ExposureMatchEvent

A match involving one of your own addresses was created or changed.

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"
  }
}

ExposureRevokedEvent

A match was removed from your active match set. Announced explicitly so your local list cannot silently diverge from ours. Not a clearance.

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"
}

CoverageHealthChangedEvent

The observability health of an authority in scope for your account changed.

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
    }
  ]
}