Skip to content

Verify Webhooks with Go

Webhook verification helpers validate the X-Billing-Webhook-Timestamp and X-Billing-Webhook-Signature headers against the raw request body.

func billingWebhookHandler(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "read body", http.StatusBadRequest)
return
}
if err = sdk.VerifyWebhookRequest(r, body, os.Getenv("BILLING_WEBHOOK_SECRET")); err != nil {
http.Error(w, "invalid signature", http.StatusUnauthorized)
return
}
// Decode and handle the webhook after verification.
w.WriteHeader(http.StatusNoContent)
}

The default timestamp tolerance is five minutes.

Use VerifyWebhookRequestWithTolerance to override the default timestamp tolerance.

err := sdk.VerifyWebhookRequestWithTolerance(
req,
body,
os.Getenv("BILLING_WEBHOOK_SECRET"),
10*time.Minute,
)

Use VerifyWebhookSignature if you already parsed the timestamp yourself.

err := sdk.VerifyWebhookSignature(secret, signatureHeader, timestamp, body)

Webhook headers exported by the SDK:

Constant Header
WebhookIDHeader X-Billing-Webhook-ID
WebhookDeliveryIDHeader X-Billing-Webhook-Delivery-ID
WebhookTimestampHeader X-Billing-Webhook-Timestamp
WebhookSignatureHeader X-Billing-Webhook-Signature

Webhook helpers return sentinel errors that can be checked with errors.Is.

Error Meaning
ErrWebhookMissingSecret Signing secret is empty.
ErrWebhookNilRequest Request is nil.
ErrWebhookMissingTimestamp Timestamp header is missing.
ErrWebhookInvalidTimestamp Timestamp header is not a valid Unix timestamp.
ErrWebhookMissingSignature Signature header is missing.
ErrWebhookInvalidSignature Signature header is malformed or unsupported.
ErrWebhookSignatureMismatch Signature does not match the body and timestamp.
ErrWebhookTimestampExpired Timestamp is outside the configured tolerance.