#[webhook]Expand description
Marks a function as a webhook handler.
Webhooks are HTTP endpoints for receiving external events (e.g., from Stripe, GitHub). They support signature validation, idempotency, and bypass authentication middleware.
§Attributes
path = "/webhooks/stripe"- URL path (required)signature = WebhookSignature::hmac_sha256("Header", "SECRET_ENV")- Signature validationidempotency = "header:X-Request-Id"- Idempotency key sourcetimeout = "30s"- Request timeout. Also becomes the default outbound HTTP timeout forctx.http()when explicitly set
§Signature Validation
Use WebhookSignature helper. Pick the constructor that matches the sender:
WebhookSignature::hmac_sha256("Header", "SECRET_ENV")- HMAC-SHA256 (e.g. GitHub)WebhookSignature::stripe_webhooks("SECRET_ENV")- Stripe (stripe-signatureheader, 300s replay window)WebhookSignature::shopify_webhooks("SECRET_ENV")- Shopify (x-shopify-hmac-sha256, base64)WebhookSignature::ed25519("Header", "PUBKEY_ENV")- Ed25519 with a base64-encoded public key
§Idempotency
Specify source as "header:Header-Name" or "body:$.json.path":
"header:X-GitHub-Delivery"- From header"body:$.id"- From JSON body field
§Example
ⓘ
#[forge::webhook(
path = "/webhooks/github",
signature = WebhookSignature::hmac_sha256("X-Hub-Signature-256", "GITHUB_SECRET"),
idempotency = "header:X-GitHub-Delivery",
)]
pub async fn github_webhook(ctx: &WebhookContext, payload: Value) -> Result<WebhookResult> {
let event_type = ctx.header("X-GitHub-Event").unwrap_or("unknown");
ctx.dispatch_job("process_github_event", &payload).await?;
Ok(WebhookResult::Accepted)
}
#[forge::webhook(path = "/webhooks/stripe", timeout = "60s")]
pub async fn stripe_webhook(ctx: &WebhookContext, payload: Value) -> Result<WebhookResult> {
// Process Stripe event
Ok(WebhookResult::Ok)
}