Concepts
The signal lifecycle
Three live stages, three terminal ones, and the two terminal states that are most often misread as an all-clear.
A match between an enforcement event and one of your addresses has a stage. The stage is what the caller acts on.
The live stages
Reported as stage on every match, and in the exposure.match webhook.
| Stage | What it means to a caller |
|---|---|
PROPOSED | The event exists and has not been 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. |
PROPOSED is not a rumour and it is not a prediction. It is an observation of something that exists on-chain. What it does not carry is any promise about how long you have: the interval between a proposal and its execution is a by-product of the issuer’s governance structure, not a service Foreshock provides, and an issuer can change how it executes at any time. Build the workflow so that it is correct when the interval is zero.
The terminal stages
A match that leaves the active set is announced through the exposure.revoked webhook with a RevocationStage, and disappears from subsequent pages of GET /v1/exposure.
| Stage | What it means |
|---|---|
REVOKED | The underlying event was withdrawn. |
EXPIRED | The event did not execute within its configured window. |
STALE | The underlying data can no longer be trusted. |
REVOKED is not a clearance
A revocation states one thing: the match is no longer in your active match set. It does not state that the address is safe. A withdrawn event can be re-raised.
Removals are always announced as their own event and are never silent. That is deliberate, and it is the reason the webhook exists at all: if removals were silent, your local list would diverge from ours and you would not know. Handle exposure.revoked as a state change to record, not as permission to release funds you were holding for other reasons.
STALE is not safe
STALE means the data behind the match can no longer be trusted — not that the risk went away. Treat the affected scope as a blind spot, not as an all-clear, and fall back to a more conservative policy until it clears.
The same reading applies wherever the word appears. LIST_STALE and REGISTRY_ENTRY_STALE are degradation reason codes on the decision path, and both produce HOLD. A stale input never produces a CREDIT.
Reconciliation is the feed, not the webhook
Webhook delivery is at-least-once, and a delivery can fail for 24 hours before it lands in a dead-letter queue. An event you did not receive is not an event that did not happen.
GET /v1/exposure with your last cursor is the authoritative reconciliation path. Run it on a schedule regardless of webhook health, and run it after any period where your receiver was down.