A drop-in endpoint for receiving Daytona webhook deliveries.
Signature verification needs the raw request body, which is a
notorious footgun behind Plug.Parsers (the body is consumed and
re-encoding breaks the signature). This plug closes the loop: mount it
above Plug.Parsers with :at, and it reads the raw body,
verifies the Svix signature via ExDaytona.Webhooks.verify/4, invokes
your handler, and answers with the status the provider's retry logic
expects — while every other request passes through untouched.
In a Phoenix endpoint (before plug Plug.Parsers):
plug ExDaytona.Webhooks.Plug,
at: "/webhooks/daytona",
secret: {MyApp.Config, :daytona_webhook_secret, []},
handler: MyApp.DaytonaWebhooksIn a bare Plug.Router, the same options work with plug ... when action-style mounting, or omit :at when the plug terminates a
dedicated pipeline that only ever sees webhook traffic.
Options
:secret(required) — the endpoint's signing secret (whsec_...): a binary, a{module, function, args}tuple, or a zero-arity fun (both resolved per request, so runtime configuration works):handler(required) — who receives verified events: a module implementingExDaytona.Webhooks.Handler, a{module, function}tuple called asfunction(event, conn), or a fun of arity 1 (event) or 2 (event, conn):at— request path to handle; other paths pass through untouched. Omit to handle every request reaching the plug.:tolerance_seconds— max timestamp skew (default300):max_body_bytes— reject larger deliveries with413(default1_048_576)
Responses
204— signature valid, handler returned:ok/{:ok, _}400— missing/invalid signature headers, stale timestamp, or signature mismatch (the provider will not retry a4xx)405— non-POST request at:at413— body exceeded:max_body_bytes500— handler returned{:error, _}or raised (the provider retries the delivery later)
This module is compiled only when the optional :plug dependency is
present.