Manage webhook subscriptions and verify incoming webhook signatures.
Receiving webhooks
Duffel signs every webhook delivery with the X-Duffel-Signature
header. Verify it against the raw (unparsed) request body before
trusting the payload:
case Duffel.Webhooks.verify_signature(signature_header, raw_body, secret) do
:ok -> handle_event(Jason.decode!(raw_body))
{:error, reason} -> reject(reason)
endSee the Duffel documentation and the receiving webhooks guide.
Summary
Functions
Creates a webhook subscription.
Deletes a webhook subscription.
Lists one page of webhooks.
Sends a test (ping) event to the webhook's URL.
Lazily streams all webhooks across pages.
Updates a webhook's URL, subscribed events or active status.
Verifies the X-Duffel-Signature header of a webhook delivery.
Functions
@spec create(Duffel.Client.t(), map(), keyword()) :: {:ok, map()} | {:error, Duffel.Error.t()}
Creates a webhook subscription.
The response includes the shared secret used to sign deliveries —
it is only returned on creation, so store it.
Examples
Duffel.Webhooks.create(client, %{
url: "https://example.com/webhooks/duffel",
events: ["order.created", "order.airline_initiated_change_detected"]
})
@spec delete(Duffel.Client.t(), String.t()) :: :ok | {:error, Duffel.Error.t()}
Deletes a webhook subscription.
@spec list(Duffel.Client.t(), keyword() | map()) :: {:ok, Duffel.Page.t()} | {:error, Duffel.Error.t()}
Lists one page of webhooks.
Parameters
:limit/:after/:before- pagination (seeDuffel.Page)
@spec ping(Duffel.Client.t(), String.t()) :: :ok | {:error, Duffel.Error.t()}
Sends a test (ping) event to the webhook's URL.
@spec stream(Duffel.Client.t(), keyword() | map()) :: Enumerable.t()
Lazily streams all webhooks across pages.
Raises Duffel.Error if a page request fails.
@spec update(Duffel.Client.t(), String.t(), map()) :: {:ok, map()} | {:error, Duffel.Error.t()}
Updates a webhook's URL, subscribed events or active status.
Examples
Duffel.Webhooks.update(client, "sev_123", %{active: false})
@spec verify_signature(String.t() | nil, binary(), String.t(), keyword()) :: :ok | {:error, :invalid_format | :invalid_signature | :timestamp_out_of_tolerance}
Verifies the X-Duffel-Signature header of a webhook delivery.
The header has the form t=<unix timestamp>,v1=<signature>, where the
signature is the lowercase hex HMAC-SHA256 of "<timestamp>.<raw body>"
keyed with the webhook's shared secret. raw_body must be the exact
bytes received — verify before any JSON parsing or re-encoding.
Comparison is constant-time. Deliveries older than :tolerance
seconds (default 300) are rejected to limit replay
attacks; pass tolerance: :infinity to disable the timestamp check.
Options
:tolerance- max allowed age in seconds, or:infinity(default300):now- current unix time in seconds, for testing (defaultSystem.system_time(:second))
Return values
:ok- signature is valid{:error, :invalid_format}- header missing or malformed{:error, :invalid_signature}- signature does not match{:error, :timestamp_out_of_tolerance}- delivery too old