Skip to content

Attributes

[Webhook<TValidator>]

Marks a dispatch handler as a webhook endpoint that requires signature validation before processing. Applied alongside [CommandHandler] and an HTTP method attribute.

[CommandHandler]
[HttpPost("/webhooks/twilio/sms")]
[Webhook<TwilioWebhookValidator>]
public static Eff<AppRuntime, Unit> HandleInboundSms(AcceptSmsReply cmd) => ...
Property Type Description
TValidator Type parameter Must implement IWebhookValidator

How It Works

  1. The generated endpoint reads the signature header and raw request body
  2. A WebhookValidationContext is constructed with the signature, body, timestamp, and headers
  3. TValidator.Validate(context) is called before the handler executes
  4. If validation fails, the endpoint returns 401 Unauthorized
  5. If validation passes, the handler processes the webhook

Dispatch Pipeline Integration

[Webhook<T>] works with standard dispatch attributes:

[CommandHandler]
[HttpPost("/webhooks/stripe")]
[Webhook<StripeWebhookValidator>]
[Public]  // Webhooks are unauthenticated (validated by signature instead)
public static Eff<AppRuntime, Unit> HandleStripeEvent(ProcessStripeEvent cmd) => ...

The [Public] attribute bypasses auth for the endpoint — webhook authentication is handled by the validator.

WebhookValidationContext

The context record passed to validators:

Property Type Description
Signature string Raw signature from the request header
RawBody string Raw request body as string
Timestamp string? Timestamp header (e.g., Slack's X-Slack-Request-Timestamp)
RequestUrl string? Full request URL (used by Twilio's validation scheme)
Headers IReadOnlyDictionary<string, string>? Additional headers (case-insensitive keys)

Provider-Specific Context

Each provider uses different fields from the context:

Provider Signature Source Uses Timestamp Uses URL
Slack X-Slack-Signature Yes No
Stripe Stripe-Signature Yes (embedded) No
Twilio X-Twilio-Signature No Yes
Bandwidth Basic auth header No No
SNS X-Amz-Sns-* headers No No