defmodule Codat.Platform.Webhooks do
@moduledoc """
Manage webhook consumers via the Codat Platform API.
Webhook consumers are HTTPS endpoints that Codat POSTs to when events occur.
This module wraps the Platform API for creating and managing them programmatically.
For receiving and verifying webhooks in your Elixir app, see:
- `Codat.Webhooks.Plug` — drop-in Plug for Phoenix/Plug apps
- `Codat.Webhooks.Verifier` — standalone signature verification
- `Codat.Webhooks.Handler` — behaviour for typed event handling
## Example
# Create a webhook consumer
{:ok, consumer} = Codat.Platform.Webhooks.create(client, %{
name: "Production Events",
url: "https://myapp.com/webhooks/codat",
eventTypes: ["company.dataConnectionStatusChanged", "invoices.write.successful"]
})
# List all configured consumers
{:ok, page} = Codat.Platform.Webhooks.list(client)
"""
alias Codat.Client
alias Codat.Error
alias Codat.Pagination
alias Codat.Platform.Shared
import Codat.API, only: [resolve_client: 1]
@base_path "/webhooks"
# ---------------------------------------------------------------------------
# List
# ---------------------------------------------------------------------------
@doc """
Lists all webhook consumers.
## Example
{:ok, page} = Codat.Platform.Webhooks.list(client)
consumers = page.results
"""
@spec list(Client.t() | keyword(), keyword()) ::
{:ok, Pagination.t()} | {:error, Error.t()}
def list(client_or_opts \\ [], opts \\ [])
def list(%Client{} = client, opts) do
Shared.paginated_get(client.config, @base_path, opts, __MODULE__, :list)
end
def list(opts, []) when is_list(opts) do
list(resolve_client(opts), opts)
end
# ---------------------------------------------------------------------------
# Get
# ---------------------------------------------------------------------------
@doc """
Fetches a webhook consumer by ID.
## Example
{:ok, consumer} = Codat.Platform.Webhooks.get(client, "webhook-id")
consumer["url"] # => "https://myapp.com/webhooks/codat"
consumer["status"] # => "Active"
"""
@spec get(Client.t() | String.t(), String.t() | keyword()) ::
{:ok, map()} | {:error, Error.t()}
def get(client_or_id, id_or_opts \\ [])
def get(%Client{} = client, id) when is_binary(id) do
Codat.HTTP.Client.get(client.config, "#{@base_path}/#{id}",
api_module: __MODULE__,
operation: :get
)
end
def get(id, opts) when is_binary(id) do
get(resolve_client(opts), id)
end
# ---------------------------------------------------------------------------
# Create
# ---------------------------------------------------------------------------
@doc """
Creates a new webhook consumer endpoint.
## Required Fields
- `url` — HTTPS endpoint to POST events to (must respond with 2xx within 15s)
## Optional Fields
- `name` — human-readable label
- `eventTypes` — list of event type strings to subscribe to.
Pass `["*"]` to receive all events. Defaults to all events if omitted.
- `companyId` — scope to a single company
- `type` — `"Portal"` or `"API"` (informational)
## Example
{:ok, consumer} = Codat.Platform.Webhooks.create(client, %{
name: "Invoice Events",
url: "https://myapp.com/webhooks/codat",
eventTypes: [
"invoices.write.successful",
"invoices.write.unsuccessful"
]
})
"""
@spec create(Client.t() | map(), map() | keyword()) ::
{:ok, map()} | {:error, Error.t()}
def create(client_or_body, body_or_opts \\ [])
def create(%Client{} = client, body) when is_map(body) do
Codat.HTTP.Client.post(client.config, @base_path, body,
api_module: __MODULE__,
operation: :create
)
end
def create(body, opts) when is_map(body) do
create(resolve_client(opts), body)
end
# ---------------------------------------------------------------------------
# Update
# ---------------------------------------------------------------------------
@doc """
Updates an existing webhook consumer.
## Example
{:ok, consumer} = Codat.Platform.Webhooks.update(client, "webhook-id", %{
eventTypes: ["*"]
})
"""
@spec update(Client.t() | String.t(), String.t() | map(), map() | keyword()) ::
{:ok, map()} | {:error, Error.t()}
def update(client_or_id, id_or_body, body_or_opts \\ [])
def update(%Client{} = client, id, body) when is_binary(id) and is_map(body) do
Codat.HTTP.Client.put(client.config, "#{@base_path}/#{id}", body,
api_module: __MODULE__,
operation: :update
)
end
def update(id, body, opts) when is_binary(id) and is_map(body) do
update(resolve_client(opts), id, body)
end
# ---------------------------------------------------------------------------
# Delete
# ---------------------------------------------------------------------------
@doc """
Deletes a webhook consumer.
## Example
{:ok, nil} = Codat.Platform.Webhooks.delete(client, "webhook-id")
"""
@spec delete(Client.t() | String.t(), String.t() | keyword()) ::
{:ok, nil} | {:error, Error.t()}
def delete(client_or_id, id_or_opts \\ [])
def delete(%Client{} = client, id) when is_binary(id) do
Codat.HTTP.Client.delete(client.config, "#{@base_path}/#{id}",
api_module: __MODULE__,
operation: :delete
)
end
def delete(id, opts) when is_binary(id) do
delete(resolve_client(opts), id)
end
# ---------------------------------------------------------------------------
# Enable / Disable
# ---------------------------------------------------------------------------
@doc """
Enables a previously disabled webhook consumer.
## Example
{:ok, consumer} = Codat.Platform.Webhooks.enable(client, "webhook-id")
consumer["status"] # => "Active"
"""
@spec enable(Client.t() | String.t(), String.t() | keyword()) ::
{:ok, map()} | {:error, Error.t()}
def enable(client_or_id, id_or_opts \\ [])
def enable(%Client{} = client, id) when is_binary(id) do
Codat.HTTP.Client.post(client.config, "#{@base_path}/#{id}/enable", %{},
api_module: __MODULE__,
operation: :enable
)
end
def enable(id, opts) when is_binary(id) do
enable(resolve_client(opts), id)
end
@doc """
Disables a webhook consumer so it no longer receives events.
## Example
{:ok, consumer} = Codat.Platform.Webhooks.disable(client, "webhook-id")
consumer["status"] # => "Inactive"
"""
@spec disable(Client.t() | String.t(), String.t() | keyword()) ::
{:ok, map()} | {:error, Error.t()}
def disable(client_or_id, id_or_opts \\ [])
def disable(%Client{} = client, id) when is_binary(id) do
Codat.HTTP.Client.post(client.config, "#{@base_path}/#{id}/disable", %{},
api_module: __MODULE__,
operation: :disable
)
end
def disable(id, opts) when is_binary(id) do
disable(resolve_client(opts), id)
end
# ---------------------------------------------------------------------------
# Message log
# ---------------------------------------------------------------------------
@doc """
Returns the delivery log for a specific webhook consumer.
## Example
{:ok, page} = Codat.Platform.Webhooks.messages(client, "webhook-id")
msgs = page.results
"""
@spec messages(Client.t() | String.t(), String.t() | keyword(), keyword()) ::
{:ok, Pagination.t()} | {:error, Error.t()}
def messages(client_or_id, id_or_opts \\ [], opts \\ [])
def messages(%Client{} = client, id, opts) when is_binary(id) do
params = Pagination.to_params(opts)
case Codat.HTTP.Client.get(client.config, "#{@base_path}/#{id}/messages",
params: params,
api_module: __MODULE__,
operation: :messages
) do
{:ok, body} when is_map(body) -> {:ok, Pagination.from_response(body)}
{:error, _reason} = error -> error
end
end
def messages(id, opts, []) when is_binary(id) do
messages(resolve_client(opts), id, opts)
end
def messages(id, [], []) when is_binary(id) do
messages(resolve_client([]), id, [])
end
end