Current section
Files
Jump to
Current section
Files
lib/coffrify/resources/webhooks.ex
defmodule Coffrify.Resources.Webhooks do
@moduledoc """
Webhook subscriptions.
Coffrify signs every delivery with Standard Webhooks v2 (`webhook-id`,
`webhook-timestamp`, `webhook-signature`). Verify with
`Coffrify.Webhook.Verification.verify/4`.
"""
alias Coffrify
@doc "List every webhook subscribed in the workspace."
@spec list(Coffrify.t()) :: {:ok, map()} | {:error, Exception.t()}
def list(client), do: Coffrify.request(client, :get, "/webhooks")
@doc """
Create a webhook subscription.
The returned `secret` MUST be stored — it is only returned once.
## Example
{:ok, %{"webhook" => wh, "secret" => secret}} =
Coffrify.Resources.Webhooks.create(client, %{
name: "Production",
url: "https://api.example.com/hooks/coffrify",
events: ["transfer.created", "transfer.downloaded"]
})
"""
@spec create(Coffrify.t(), map() | keyword()) :: {:ok, map()} | {:error, Exception.t()}
def create(client, opts) do
Coffrify.request(client, :post, "/webhooks", Map.new(opts))
end
@doc "Patch a webhook (URL, events, is_active, …)."
@spec update(Coffrify.t(), String.t(), map()) :: {:ok, map()} | {:error, Exception.t()}
def update(client, id, updates) when is_map(updates) do
Coffrify.request(client, :patch, "/webhooks/#{URI.encode(id)}", updates)
end
@doc "Delete a webhook subscription."
@spec delete(Coffrify.t(), String.t()) :: {:ok, map()} | {:error, Exception.t()}
def delete(client, id) do
Coffrify.request(client, :delete, "/webhooks/#{URI.encode(id)}")
end
@doc """
Send a sandbox event to verify signing & URL. Receiver gets
`x-coffrify-test-delivery: true`.
"""
@spec test(Coffrify.t(), String.t(), keyword() | map()) :: {:ok, map()} | {:error, Exception.t()}
def test(client, id, opts \\ []) do
body = Map.new(opts)
Coffrify.request(client, :post, "/webhooks/#{URI.encode(id)}/test", body)
end
@doc """
Rotate the webhook signing secret. Both new + previous secrets remain
valid during the grace window (default 24h, max 168h).
"""
@spec rotate_secret(Coffrify.t(), String.t(), keyword() | map()) ::
{:ok, map()} | {:error, Exception.t()}
def rotate_secret(client, id, opts \\ []) do
body =
opts
|> Map.new()
|> then(fn m ->
Map.put_new_lazy(m, :grace_window_hours, fn -> Map.get(m, :grace_hours) end)
end)
Coffrify.request(client, :post, "/webhooks/#{URI.encode(id)}/rotate-secret", body)
end
@doc """
Replay a past delivery. Preserves the original `event_id` so the
receiver can dedupe via `webhook-id`.
"""
@spec replay(Coffrify.t(), String.t()) :: {:ok, map()} | {:error, Exception.t()}
def replay(client, delivery_id) do
Coffrify.request(client, :post, "/webhooks/deliveries/#{URI.encode(delivery_id)}/replay")
end
@doc "Force a non-success delivery back into the retry queue."
@spec retry(Coffrify.t(), String.t()) :: {:ok, map()} | {:error, Exception.t()}
def retry(client, delivery_id) do
Coffrify.request(client, :post, "/webhooks/deliveries/#{URI.encode(delivery_id)}/retry")
end
@doc "Fetch the recent delivery attempts for a webhook (debug)."
@spec deliveries(Coffrify.t(), String.t(), keyword()) :: {:ok, map()} | {:error, Exception.t()}
def deliveries(client, id, opts \\ []) do
Coffrify.request(client, :get, "/webhooks/#{URI.encode(id)}/deliveries", nil, query: opts)
end
end