Modules
Webhooks for Ash Framework — inbound (receive, verify, dedup → domain events) and outbound (sign, deliver, retry, track).
Endpoint body_reader that caches the raw, pre-parser request bytes for
signature verification.
The delivery runtime driver: one pending delivery row → signed HTTP
send → reconciled ledger row. Pure functions over the consumer's
resource modules and an injected HTTP adapter — Oban-free by
construction; use AshHooks.Worker wraps this as an Oban worker inside
the consuming app (ADR-0004's host-injection boundary).
The outbound fanout driver: one event → every matching subscription's
endpoint → a durable per-endpoint delivery row (+ an enqueue handoff),
with per-endpoint isolation — the outbound twin of AshHooks.Ingress.
Turns the consumer's resource into the outbound webhook Endpoint — the delivery target with its durable circuit-breaker state and its secret REFERENCES.
A reference to a consumer-held secret — never the secret itself.
An outbound webhook destination URL, validated at EVERY write path by living in the type: http(s) scheme, host not a known metadata name, and a literal-IP host (v4, v6, mapped/compatible v6) never in a private/loopback/link-local/reserved range (ADR-0005's registration-time floor — deterministic and offline-safe).
Splode error hierarchy for ash_hooks.
The invalid error class — the webhook input or its verification failed.
An inbound webhook's signature did not verify under the provider's scheme.
The webhook payload does not have the shape the provider expects.
The signing secret for an inbound webhook's provider is unconfigured or unavailable. The caller verifies nothing and does not run the handler.
The timestamp carried by a windowed provider's signature falls outside the allowed replay window.
The payload's event type is not one the provider knows.
The unknown error class — unrecognized failures inside ash_hooks.
An error ash_hooks did not recognize.
The outbound pipeline's unit of work — what a consumer emits and the fanout dispatcher delivers.
The HTTP adapter behaviour the delivery runtime sends through.
A minimal, memory-bounded HTTP/1.1 client over :gen_tcp/:ssl — the
DEFAULT adapter (derisk-2): EVERY read is capped, under all framings,
so no response — 2xx or not — can balloon a delivery worker's memory.
The default AshHooks.Http adapter: OTP's :httpc, hardened.
Turns the consumer's resource into the inbound webhook ledger — the durable, fenced dedup substrate of the inbound pipeline (ADR-0003).
A decoded inbound webhook body: a JSON object OR a JSON array.
Accessors for a resource's webhooks declarations, read via
Spark.Dsl.Extension (the ash_age pattern — no generated getter in this
spark version).
The inbound pipeline driver: verify → ingest → claim → handle → mark,
over a ledger resource carrying the AshHooks.InboundDelivery extension.
The incumbent webhook envelope, reproduced byte-identically for the
:dual migration mode (ADR-0002), with verify/5 as the in-package
oracle proving that identity.
Turns the consumer's resource into the outbound delivery ledger — the
durable, effect-once record of "this event owes this endpoint a
delivery" (the outbound twin of AshHooks.InboundDelivery).
Behaviour for inbound webhook providers.
ComplyCube webhook verifier: lowercase-hex HMAC-SHA256 of the RAW request
body under the webhook endpoint's secret, carried in the
ComplyCube-Signature header — no timestamp, no replay window (verified
against the vendor docs and all three official SDKs; the acceptance vector
is the PHP SDK's own test fixture, quoted in
test/ash_hooks/provider/comply_cube_test.exs).
Typed event echoed by AshHooks.Provider.ComplyCube.handle_event/2.
HubSpot v3 webhook verifier: HMAC-SHA256 over the concatenated canonical
string requestMethod + requestUri + requestBody + timestamp (no
separators), keyed with the app's client secret, base64-encoded — carried
in the X-HubSpot-Signature-v3 header, with the millisecond timestamp in
a SEPARATE X-HubSpot-Request-Timestamp header (verified against the
vendor docs first-hand 2026-08-21; the acceptance vector is the docs
page's own Java example, quoted in
test/ash_hooks/provider/hub_spot_v3_test.exs). Sign the raw body bytes
exactly as received and reconstruct the signed URI caller-side — Plug's
conn.query_string excludes the ?, and conn.host excludes a
non-default port, so join explicitly
Typed event echoed by AshHooks.Provider.HubSpotV3.handle_event/2.
Reference provider for tests and examples: HMAC-SHA256 over the raw body via
the default implementation, event type from payload["type"], and an
echoing typed event.
Typed event echoed by AshHooks.Provider.Mock.handle_event/2.
Standard Webhooks canon signing and verification — v1 (HMAC-SHA256) and
v1a (ed25519) from day one (ADR-0006), over the canonical string
msg_id.timestamp.payload.
The SSRF destination classifier (ADR-0005 — enforced at endpoint registration AND again at send time).
Turns the consumer's resource into the outbound Subscription — which events go to which endpoint, and with which signature envelope.
The package's telemetry surface (ADR-0005's telemetry floor): events
carry ids, integers, fixed-vocabulary atoms, and classified reason
strings ONLY — never secrets, bodies, or payloads. Where a consumer
wants secret IDENTITY in an event, fingerprint/1 is the sanctioned
8-hex form; no package event needs it today.
The host-injected Oban worker (ADR-0004): the consuming app defines ONE
module, and the Oban beam compiles only where Oban exists — this macro
expands use Oban.Worker inside the HOST's compilation, so the package
itself never references a loaded Oban module and compiles Oban-free
(the CI no-optional leg's proof).
Mix Tasks
Installs ash_hooks into a host application