# AshHooks v0.2.2 - Table of Contents

> Webhooks for Ash Framework — inbound verification + dedup, outbound signing + delivery

## Pages

- [AshHooks](readme.md)
- [Changelog](changelog.md)
- [ash_hooks usage rules](usage-rules.md)

- Tutorials
  - [Get Started](get-started-1.md)

- Livebooks
  - [AshHooks — a guided tour](get-started-2.md)

- DSLs
  - [AshHooks.Endpoint](dsl-ashhooks-endpoint.md)
  - [AshHooks.InboundDelivery](dsl-ashhooks-inbounddelivery.md)
  - [AshHooks.OutboundDelivery](dsl-ashhooks-outbounddelivery.md)
  - [AshHooks.Subscription](dsl-ashhooks-subscription.md)
  - [AshHooks](dsl-ashhooks.md)

## Modules

- [AshHooks](AshHooks.md): Webhooks for Ash Framework — inbound (receive, verify, dedup → domain events)
and outbound (sign, deliver, retry, track).
- [AshHooks.BodyReader](AshHooks.BodyReader.md): Endpoint `body_reader` that caches the raw, pre-parser request bytes for
signature verification.
- [AshHooks.Delivery](AshHooks.Delivery.md): 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).
- [AshHooks.Dispatcher](AshHooks.Dispatcher.md): 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`.
- [AshHooks.Endpoint](AshHooks.Endpoint.md): Turns the consumer's resource into the outbound webhook Endpoint — the
delivery target with its durable circuit-breaker state and its secret
REFERENCES.
- [AshHooks.Endpoint.SecretRef](AshHooks.Endpoint.SecretRef.md): A reference to a consumer-held secret — never the secret itself.
- [AshHooks.Endpoint.Url](AshHooks.Endpoint.Url.md): 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).
- [AshHooks.Errors](AshHooks.Errors.md): Splode error hierarchy for ash_hooks.
- [AshHooks.Event](AshHooks.Event.md): The outbound pipeline's unit of work — what a consumer emits and the
fanout dispatcher delivers.
- [AshHooks.Http](AshHooks.Http.md): The HTTP adapter behaviour the delivery runtime sends through.
- [AshHooks.Http.Bounded](AshHooks.Http.Bounded.md): 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.
- [AshHooks.Http.Httpc](AshHooks.Http.Httpc.md): The default `AshHooks.Http` adapter: OTP's `:httpc`, hardened.
- [AshHooks.InboundDelivery](AshHooks.InboundDelivery.md): Turns the consumer's resource into the inbound webhook ledger — the
durable, fenced dedup substrate of the inbound pipeline (ADR-0003).
- [AshHooks.InboundDelivery.Payload](AshHooks.InboundDelivery.Payload.md): A decoded inbound webhook body: a JSON object OR a JSON array.
- [AshHooks.Info](AshHooks.Info.md): Accessors for a resource's `webhooks` declarations, read via
`Spark.Dsl.Extension` (the ash_age pattern — no generated getter in this
spark version).

- [AshHooks.Ingress](AshHooks.Ingress.md): The inbound pipeline driver: verify → ingest → claim → handle → mark,
over a ledger resource carrying the `AshHooks.InboundDelivery` extension.
- [AshHooks.Legacy](AshHooks.Legacy.md): 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.
- [AshHooks.OutboundDelivery](AshHooks.OutboundDelivery.md): 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`).
- [AshHooks.Provider](AshHooks.Provider.md): Behaviour for inbound webhook providers.
- [AshHooks.Provider.ComplyCube](AshHooks.Provider.ComplyCube.md): 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`).
- [AshHooks.Provider.ComplyCube.Event](AshHooks.Provider.ComplyCube.Event.md): Typed event echoed by `AshHooks.Provider.ComplyCube.handle_event/2`.
- [AshHooks.Provider.HubSpotV3](AshHooks.Provider.HubSpotV3.md): 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
- [AshHooks.Provider.HubSpotV3.Event](AshHooks.Provider.HubSpotV3.Event.md): Typed event echoed by `AshHooks.Provider.HubSpotV3.handle_event/2`.
- [AshHooks.Provider.Mock](AshHooks.Provider.Mock.md): 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.
- [AshHooks.Provider.Mock.Event](AshHooks.Provider.Mock.Event.md): Typed event echoed by `AshHooks.Provider.Mock.handle_event/2`.
- [AshHooks.Signing](AshHooks.Signing.md): 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`.
- [AshHooks.Ssrf](AshHooks.Ssrf.md): The SSRF destination classifier (ADR-0005 — enforced at endpoint
registration AND again at send time).
- [AshHooks.Subscription](AshHooks.Subscription.md): Turns the consumer's resource into the outbound Subscription — which
events go to which endpoint, and with which signature envelope.
- [AshHooks.Telemetry](AshHooks.Telemetry.md): 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.
- [AshHooks.Worker](AshHooks.Worker.md): 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).

- Exceptions
  - [AshHooks.Errors.Invalid](AshHooks.Errors.Invalid.md): The invalid error class — the webhook input or its verification failed.
  - [AshHooks.Errors.Invalid.InvalidSignature](AshHooks.Errors.Invalid.InvalidSignature.md): An inbound webhook's signature did not verify under the provider's scheme.
  - [AshHooks.Errors.Invalid.MalformedPayload](AshHooks.Errors.Invalid.MalformedPayload.md): The webhook payload does not have the shape the provider expects.
  - [AshHooks.Errors.Invalid.NoWebhookSecret](AshHooks.Errors.Invalid.NoWebhookSecret.md): The signing secret for an inbound webhook's provider is unconfigured or
unavailable. The caller verifies nothing and does not run the handler.

  - [AshHooks.Errors.Invalid.StaleTimestamp](AshHooks.Errors.Invalid.StaleTimestamp.md): The timestamp carried by a windowed provider's signature falls outside the
allowed replay window.

  - [AshHooks.Errors.Invalid.UnknownEventType](AshHooks.Errors.Invalid.UnknownEventType.md): The payload's event type is not one the provider knows.
  - [AshHooks.Errors.Unknown](AshHooks.Errors.Unknown.md): The unknown error class — unrecognized failures inside ash_hooks.
  - [AshHooks.Errors.Unknown.UnknownError](AshHooks.Errors.Unknown.UnknownError.md): An error ash_hooks did not recognize.

## Mix Tasks

- [mix ash_hooks.install](Mix.Tasks.AshHooks.Install.md): Installs ash_hooks into a host application

