Daytona webhooks: enabling them for an organization, and verifying the deliveries your endpoint receives.
Setting up
Daytona delivers webhooks through Svix. Initialize the org once, then configure endpoints and event subscriptions in the Svix app portal:
{:ok, _status} = ExDaytona.Webhooks.initialize(client, org_id)
{:ok, %{url: portal_url}} = ExDaytona.Webhooks.portal(client, org_id)
# open portal_url in a browser to add endpoints / choose eventsReceiving
Deliveries are signed (Svix / Standard Webhooks scheme). In your
endpoint, verify with the endpoint's signing secret (whsec_..., from
the portal) using the raw request body — any re-encoding breaks the
signature:
# e.g. in a Phoenix controller with a raw-body plug
case ExDaytona.Webhooks.verify(raw_body, conn.req_headers, secret) do
{:ok, event} -> handle(event["type"] || event, ...)
{:error, %ExDaytona.Error{}} -> send_resp(conn, 400, "invalid signature")
endverify/4 checks the HMAC signature and the timestamp tolerance, then
returns the decoded JSON payload.
Summary
Functions
Enable webhooks for an organization (idempotent server-side). Returns
the ExDaytona.Model.WebhookInitializationStatus.
A short-lived URL (and token) for the organization's Svix app portal,
where endpoints and event subscriptions are managed. Returns
{:ok, %{url, token}}.
Re-sync the organization's webhook endpoint configuration. Returns
:ok.
The organization's webhook initialization status.
Verify a webhook delivery and return its decoded JSON payload.
Functions
@spec initialize(ExDaytona.Client.t(), String.t()) :: {:ok, ExDaytona.Model.WebhookInitializationStatus.t()} | {:error, ExDaytona.Error.t()}
Enable webhooks for an organization (idempotent server-side). Returns
the ExDaytona.Model.WebhookInitializationStatus.
@spec portal(ExDaytona.Client.t(), String.t()) :: {:ok, %{url: String.t(), token: String.t() | nil}} | {:error, ExDaytona.Error.t()}
A short-lived URL (and token) for the organization's Svix app portal,
where endpoints and event subscriptions are managed. Returns
{:ok, %{url, token}}.
@spec refresh_endpoints(ExDaytona.Client.t(), String.t()) :: :ok | {:error, ExDaytona.Error.t()}
Re-sync the organization's webhook endpoint configuration. Returns
:ok.
@spec status(ExDaytona.Client.t(), String.t()) :: {:ok, ExDaytona.Model.WebhookInitializationStatus.t()} | {:error, ExDaytona.Error.t()}
The organization's webhook initialization status.
@spec verify(binary(), Enumerable.t(), String.t(), keyword()) :: {:ok, map() | list() | binary()} | {:error, ExDaytona.Error.t()}
Verify a webhook delivery and return its decoded JSON payload.
payload— the raw request body, exactly as receivedheaders— the request headers (a map or a[{name, value}]list; names are matched case-insensitively). Svix'ssvix-id,svix-timestamp,svix-signatureand the Standard Webhookswebhook-*aliases are both accepted.secret— the endpoint's signing secret (whsec_...)
Options
:tolerance_seconds— max allowed clock skew for the timestamp (default300):now— unix seconds to validate against (defaults to the current time; useful in tests)