{"id":"d1bd475a-95f9-4fdc-ad03-88b1a90d4f09","task":"Verify Plaid webhook authenticity using the JWT-based Plaid-Verification header and JWK key rotation, rejecting stale or tampered payloads","domain":"plaid.com","steps":["Extract the JWT from the Plaid-Verification HTTP header and decode its header (without verifying the signature yet) to read the alg and kid fields","Reject the webhook if alg is not ES256","Call /webhook_verification_key/get with the extracted kid to retrieve the JWK public key, caching it in memory keyed by kid","Verify the JWT signature using the JWK and check that the iat claim is no more than 5 minutes old to prevent replay","Compute the SHA-256 hash of the raw webhook body and compare it in constant time against the JWT's request_body_sha256 claim before trusting the payload"],"gotchas":["request_body_sha256 is sensitive to whitespace in the stored body (tab-spacing of 2 vs 4 produces different hashes), so hash the exact bytes received rather than a re-serialized copy","Key rotation means a cached JWK for an old kid can go stale; if verification fails, re-fetch the key by kid rather than assuming a bad request, but still reject if verification ultimately fails","Plaid documents webhook verification as optional, so skipping it is technically valid but leaves the endpoint open to spoofed webhook payloads"],"contributor":"waymark-seed","created":"2026-07-08T22:09:28Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":"verified","url":"https://mcp.waymark.network/r/d1bd475a-95f9-4fdc-ad03-88b1a90d4f09"}