Behaviour and plug helper for handling incoming Column webhook deliveries.
Usage with Plug/Phoenix
Implement the Column.WebhookHandler behaviour in your handler module:
defmodule MyApp.ColumnWebhookHandler do
use Column.WebhookHandler,
secret: System.get_env("COLUMN_WEBHOOK_SECRET")
@impl Column.WebhookHandler
def handle_event("transfer.ach.settled", payload, _meta) do
transfer = payload["data"]
MyApp.Payments.mark_settled(transfer["id"])
:ok
end
def handle_event("transfer.ach.returned", payload, _meta) do
MyApp.Payments.mark_returned(payload["data"]["id"])
:ok
end
def handle_event(_type, _payload, _meta) do
# Ignore unhandled events — always return :ok
:ok
end
endThen add it to your router (Phoenix example):
# router.ex
post "/webhooks/column", MyApp.ColumnWebhookHandler, []Or use as a Plug in a pipeline:
plug MyApp.ColumnWebhookHandlerBehaviour callbacks
Your module must implement:
handle_event(type, payload, meta)— called for each verified webhook. Return:okto acknowledge,{:error, reason}to signal failure (Column will retry the delivery).
Signature verification
use Column.WebhookHandler injects plug pipeline code that:
- Reads the raw request body (before JSON parsing destroys byte-exactness)
- Validates the
column-signatureHMAC-SHA256 header - Rejects with 401 if invalid
- Decodes the JSON body and dispatches to
handle_event/3 - Returns 200 on
:ok, 500 on{:error, _}
Important: You must configure your Plug/Phoenix endpoint to cache raw body for webhook routes. In Phoenix:
# endpoint.ex
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
body_reader: {MyApp.CacheBodyReader, :read_body, []},
json_decoder: Phoenix.json_library()
Summary
Callbacks
Handle a verified Column webhook event.