Turns the consumer's resource into the inbound webhook ledger — the
durable deduplication record used by AshHooks.Ingress.
Attach next to the consumer's own data layer:
use Ash.Resource,
data_layer: AshSqlite.DataLayer,
extensions: [AshHooks, AshHooks.InboundDelivery]
inbound_delivery do
scope_identity([:account_id])
endThe extension injects the ledger fields (provider, external ids, payload +
digest, the fenced state machine's status/token/lease/attempts), the
unique_ingest identity spanning provider + external event id + the
declared scope slots, and the action primitives used by
AshHooks.Ingress. These include ingest, claim/finalize/renew, payload
redaction, bounded retention, and explicit legacy identity adoption.
The uniqueness identity must be backed by a unique index on the
consumer's data layer. Storage-level uniqueness preserves one ledger row
under concurrent ingests; the lease and token govern handler ownership.
Scope slots
must be non-nullable attributes: a nullable slot would make nil scope
values distinct on SQL unique indexes and silently break dedup for
scope-less redeliveries.
Provider identity upgrades use
AshHooks.Ingress.plan_legacy_identity_adoption/3 and
AshHooks.Ingress.adopt_legacy_identity/3. Adoption keeps one deterministic
representative per canonical identity and marks every retained sibling
terminal :superseded; those rows cannot be reaped into handler execution.
Applying adoption requires a transaction-capable data layer and quiesced
ingress and reapers. See the ordered upgrade in UPGRADING.md.
The injected actions are low-level primitives: the conditional gates
(claim only from :received or an expired lease; mark/renew only by the
current token under an unexpired lease) live in the query filters that
AshHooks.Ingress builds. Call the driver APIs to preserve those
conditions; restrict direct access to the generated machine actions.
This ledger stores decoded provider payloads, the signed-body digest, event IDs, and scope keys. Payloads can contain personal information. Define read policies in your domain; the extension does not inject them. The README's Security section shows an example.
Summary
Functions
The fenced state machine's statuses, including terminal :superseded rows
retained by legacy identity adoption.
Functions
@spec statuses() :: [atom()]
The fenced state machine's statuses, including terminal :superseded rows
retained by legacy identity adoption.