Standard Webhooks Signature Support (Go)
Verify Primitive webhook deliveries using the Standard Webhooks header format instead of the default Primitive-Signature HMAC scheme, using VerifyStandardWebhooksSignature in the Go SDK.
Use VerifyStandardWebhooksSignature when a receiver already expects the Standard Webhooks convention (webhook-id / webhook-timestamp / webhook-signature headers, whsec_-prefixed secret) instead of Primitive's own Primitive-Signature header. Reach for it only when your tooling is already wired for that convention; every other Go SDK integration should verify with the default HMAC scheme documented in Receiving and Verifying Webhooks.
Both schemes sign the exact same raw request body, so the trust guarantee is identical.
When to use this instead of the default#
Use Standard Webhooks only when a receiver, gateway, or shared verification library in your stack already speaks the webhook-id / webhook-timestamp / webhook-signature convention. Otherwise use Primitive's default Primitive-Signature: t=<unix-seconds>,v1=<hex> header, verified automatically by primitive.Receive and primitive.HandleWebhook, which needs no extra secret formatting.
Prerequisites#
- The Go SDK installed:
go get github.com/primitivedotdev/sdks/sdk-go@latest - Your webhook secret from
GET /account/webhook-secret, in itswhsec_-prefixed form - The raw, unparsed request body and its
webhook-id,webhook-timestamp, andwebhook-signatureheaders
Verify against the exact raw request body bytes, before any JSON decoding or re-serialization. Re-serializing the body (even reformatting whitespace) changes the bytes the signature was computed over and verification will fail.
Verify a delivery#
VerifyStandardWebhooksSignature takes a StandardWebhooksVerifyOptions struct with the raw body, the three header values, and your secret, and returns (bool, error). Deliveries older than the 300-second (5-minute) default tolerance, or more than 60 seconds in the future, are rejected.
- 1
Extract the three Standard Webhooks headers#
Pull
webhook-id,webhook-timestamp, andwebhook-signaturefrom the incoming request. Header names are case-insensitive per RFC 7230; match them accordingly. - 2
Call VerifyStandardWebhooksSignature#
Pass the raw body, the three header values, and your
whsec_-prefixed secret:package main import ( "fmt" "log" "net/http" primitive "github.com/primitivedotdev/sdks/sdk-go" ) func handle(w http.ResponseWriter, r *http.Request, rawBody []byte) { ok, err := primitive.VerifyStandardWebhooksSignature(primitive.StandardWebhooksVerifyOptions{ RawBody: rawBody, MsgID: r.Header.Get("webhook-id"), Timestamp: r.Header.Get("webhook-timestamp"), SignatureHeader: r.Header.Get("webhook-signature"), Secret: "whsec_...", }) if err != nil { log.Printf("invalid webhook signature: %v", err) http.Error(w, "invalid signature", http.StatusBadRequest) return } if !ok { http.Error(w, "invalid signature", http.StatusBadRequest) return } fmt.Fprintln(w, "verified") } - 3
Handle a verification failure#
VerifyStandardWebhooksSignaturereturns a non-nil error on a malformed header, an expired timestamp, or a signature mismatch. Respond with a 4xx status and do not process the payload; a genuine delivery from Primitive is redelivered on a 5xx or timeout, not on a 4xx rejection of a bad signature.
Timestamp tolerance#
The default tolerance rejects any delivery whose webhook-timestamp is more than 300 seconds (5 minutes) older than your wall clock, or more than 60 seconds in the future, which defends against replayed captures of old but otherwise-valid signatures. Set ToleranceSeconds on StandardWebhooksVerifyOptions to change the past-age window.
Next steps#
Verify inbound webhook signatures with the default Primitive-Signature scheme and normalize the payload with primitive.Receive.
Webhook Event TypesLearn the email, payment, and interaction webhook event families and the X-Webhook-Event header discriminator.
Error HandlingInspect WebhookVerificationError and the other typed Go SDK error types to handle failures precisely.
Webhook Events OverviewUnderstand the shared webhook contract across every SDK, including signature verification and forward-compatibility guarantees.
Was this page helpful?