Webhooks for Ash Framework — inbound (receive, verify, dedup → domain events) and outbound (sign, deliver, retry, track).
Attach to a resource:
use Ash.Resource,
extensions: [AshHooks]
webhooks do
# convention-resolves to AshHooks.Provider.ComplyCube
inbound :comply_cube do
secret {:app_env, [:my_app, :complycube_secret]}
# optional: extract a stable event id (payload -> {:ok, id} | :error);
# without it a deterministic content-hash identity is used
end
# convention-resolves to AshHooks.Provider.HubSpotV3 — the
# vendor-default 300s replay window applies; override it either way
inbound :hub_spot_v3 do
secret {:app_env, [:my_app, :hubspot_client_secret]}
end
outbound :order_paid do
signing_mode :dual
end
endBoth halves are independently consumable: inbound-only consumers pull no queue infrastructure (ADR-0004).
webhooks
Webhook declarations — inbound sources and outbound events for this resource.
Nested DSLs
webhooks.inbound
inbound nameDeclares an inbound webhook source — a provider whose deliveries this resource receives, verifies, deduplicates, and handles.
Arguments
| Name | Type | Default | Docs |
|---|---|---|---|
name | atom | The provider name (e.g. :comply_cube). |
Options
| Name | Type | Default | Docs |
|---|---|---|---|
secret | any | The signing-secret source: an {M, f, a} callback, {:app_env, path}, or a function returning {:ok, secret} | {:error, :no_webhook_secret}. A literal binary is rejected at parse time (ADR-0005). Scope: this net catches the secret passed AS the option value; arguments of an MFA source are the consumer's own code. | |
provider | atom | The provider MODULE implementing AshHooks.Provider. When unset, the ingress resolves AshHooks.Provider.<Camelized(name)> and fails closed when that module does not exist or does not implement the behaviour. | |
event_id | any | Extractor for the provider's external event id from the decoded payload (payload -> {:ok, id} | :error). Providers without a stable id fall back to a deterministic content-hash identity — never a fresh UUID (ADR-0003). | |
replay_window_seconds | pos_integer | Replay-protection window for providers whose scheme carries a trustworthy timestamp (e.g. HubSpot v3). Providers without timestamps (e.g. ComplyCube) MUST leave this unset — the verifier rejects a window whose provider declares no timestamp header (AshHooks.Provider.timestamp_header/1 returns nil). The window value is passed to the provider's verify_signature/3 in the context map for scheme-specific enforcement. |
webhooks.outbound
outbound nameDeclares an outbound webhook event this resource emits to subscribed endpoints — signed (Standard Webhooks canon by default) and delivered with retry/backoff/dead-letter semantics.
Arguments
| Name | Type | Default | Docs |
|---|---|---|---|
name | atom | The outbound event name (e.g. :order_paid). |
Options
| Name | Type | Default | Docs |
|---|---|---|---|
signing_mode | :legacy | :dual | :standard | :standard | Signature envelope: :standard (SW canon), :dual (SW canon + legacy envelope for migrating receivers), :legacy (ADR-0002). |
subscriptions | atom | The consumer's Subscription resource module (carrying AshHooks.Subscription) this event fans out through — the dispatcher matches its rows against the event type. | |
deliveries | atom | The consumer's OutboundDelivery resource module (carrying AshHooks.OutboundDelivery) the dispatcher writes the durable per-endpoint rows into. |