[{"data":1,"prerenderedAt":1486},["ShallowReactive",2],{"content-query-xanfSdOP2U":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"layout":10,"crumb":11,"term":11,"summary":12,"group":13,"order":14,"updated":15,"cta":16,"related":24,"faq":43,"body":56,"_type":1479,"_id":1480,"_source":1481,"_file":1482,"_stem":1483,"_extension":1484,"sitemap":1485},"/glossary/standard-webhooks","glossary",false,"","What is Standard Webhooks? Headers, signing and examples","Standard Webhooks is an open spec for sending webhooks consistently: webhook-id, webhook-timestamp and webhook-signature headers, whsec_ secrets and HMAC-SHA256 signing.","reference","Standard Webhooks","An open spec for consistent webhook headers, signing and secrets.","Security",13,"2026-10-28",{"eyebrow":17,"title":18,"text":19,"to":20,"label":21,"secondaryTo":22,"secondaryLabel":23},"Free tools","Verify or generate a Standard Webhooks signature","Check a captured request against a whsec_ secret, or generate correctly signed test requests with the signature generator. Both run in your browser.","/verify-standard-webhooks-signature/","Verify a signature","/standard-webhooks-signature-generator/","Generate a signature",[25,28,31,34,37,40],{"title":26,"to":27},"Webhook signature","/glossary/webhook-signature/",{"title":29,"to":30},"Webhook secret","/glossary/webhook-secret/",{"title":32,"to":33},"HMAC","/glossary/hmac/",{"title":35,"to":36},"Idempotency","/glossary/idempotency/",{"title":38,"to":39},"Receiving Outbound Gateway webhooks","/docs/outbound/receiving/",{"title":41,"to":42},"Outbound webhooks","/outbound-webhooks/",[44,47,50,53],{"q":45,"a":46},"What is Standard Webhooks?","Standard Webhooks is an open specification for how to send webhooks, so receivers can verify every compliant provider the same way. It defines three headers, webhook-id, webhook-timestamp and webhook-signature, an HMAC-SHA256 signing scheme over the id, timestamp and body, a whsec_ secret format and a way to rotate secrets.",{"q":48,"a":49},"How do I verify a Standard Webhooks signature?","Build the string webhook-id, a dot, webhook-timestamp, a dot, then the exact raw body. Strip the whsec_ prefix from your secret and base64-decode the rest to get the key. Compute HMAC-SHA256, base64-encode it and compare it in constant time with each v1 entry in the webhook-signature header. Also reject timestamps more than a few minutes from the current time.",{"q":51,"a":52},"Who uses Standard Webhooks?","Svix started the spec, and a steering group with members from companies including Zapier, Twilio, ngrok and Supabase maintains it. OpenAI, Anthropic and the Webhook Relay Outbound Gateway sign their webhooks this way, and reference libraries exist for most languages.",{"q":54,"a":55},"Why are there two signatures in the webhook-signature header?","The header can carry several space-separated signatures so that secrets can be rotated without downtime. During a rotation the sender signs with both the old and the new secret, and the receiver accepts the request if any signature matches.",{"type":57,"children":58,"toc":1471},"root",[59,113,189,202,209,305,311,396,402,1215,1228,1234,1302,1350,1363,1369,1435,1441,1465],{"type":60,"tag":61,"props":62,"children":63},"element","p",{},[64,71,73,80,82,88,89,95,97,103,105,111],{"type":60,"tag":65,"props":66,"children":67},"strong",{},[68],{"type":69,"value":70},"text","Standard Webhooks is an open specification for sending webhooks the same way everywhere, so a receiver that can verify one compliant provider can verify them all.",{"type":69,"value":72}," It defines three request headers (",{"type":60,"tag":74,"props":75,"children":77},"code",{"className":76},[],[78],{"type":69,"value":79},"webhook-id",{"type":69,"value":81},", ",{"type":60,"tag":74,"props":83,"children":85},{"className":84},[],[86],{"type":69,"value":87},"webhook-timestamp",{"type":69,"value":81},{"type":60,"tag":74,"props":90,"children":92},{"className":91},[],[93],{"type":69,"value":94},"webhook-signature",{"type":69,"value":96},"), one HMAC-SHA256 signing scheme, a ",{"type":60,"tag":74,"props":98,"children":100},{"className":99},[],[101],{"type":69,"value":102},"whsec_",{"type":69,"value":104}," secret format and a way to rotate secrets. Svix started it, a steering group with members from Zapier, Twilio, ngrok, Supabase and others maintains it, and OpenAI, Anthropic and the Webhook Relay ",{"type":60,"tag":106,"props":107,"children":108},"a",{"href":42},[109],{"type":69,"value":110},"Outbound Gateway",{"type":69,"value":112}," sign this way.",{"type":60,"tag":114,"props":115,"children":119},"pre",{"className":116,"code":117,"language":118,"meta":7,"style":7},"language-http shiki shiki-themes github-dark","POST /webhooks HTTP/1.1\nContent-Type: application/json\nwebhook-id: msg_2xKq8f3Ld9\nwebhook-timestamp: 1793180400\nwebhook-signature: v1,yWMOJWJfxba5Z0vHXtZJBR94qbHjqijD6YSPO6RJ6jY=\n\n{\"type\":\"invoice.paid\",\"timestamp\":\"2026-10-28T09:00:00Z\",\"data\":{\"invoice_id\":\"inv_8842\",\"amount\":4900}}\n","http",[120],{"type":60,"tag":74,"props":121,"children":122},{"__ignoreMap":7},[123,134,143,152,161,170,180],{"type":60,"tag":124,"props":125,"children":128},"span",{"class":126,"line":127},"line",1,[129],{"type":60,"tag":124,"props":130,"children":131},{},[132],{"type":69,"value":133},"POST /webhooks HTTP/1.1\n",{"type":60,"tag":124,"props":135,"children":137},{"class":126,"line":136},2,[138],{"type":60,"tag":124,"props":139,"children":140},{},[141],{"type":69,"value":142},"Content-Type: application/json\n",{"type":60,"tag":124,"props":144,"children":146},{"class":126,"line":145},3,[147],{"type":60,"tag":124,"props":148,"children":149},{},[150],{"type":69,"value":151},"webhook-id: msg_2xKq8f3Ld9\n",{"type":60,"tag":124,"props":153,"children":155},{"class":126,"line":154},4,[156],{"type":60,"tag":124,"props":157,"children":158},{},[159],{"type":69,"value":160},"webhook-timestamp: 1793180400\n",{"type":60,"tag":124,"props":162,"children":164},{"class":126,"line":163},5,[165],{"type":60,"tag":124,"props":166,"children":167},{},[168],{"type":69,"value":169},"webhook-signature: v1,yWMOJWJfxba5Z0vHXtZJBR94qbHjqijD6YSPO6RJ6jY=\n",{"type":60,"tag":124,"props":171,"children":173},{"class":126,"line":172},6,[174],{"type":60,"tag":124,"props":175,"children":177},{"emptyLinePlaceholder":176},true,[178],{"type":69,"value":179},"\n",{"type":60,"tag":124,"props":181,"children":183},{"class":126,"line":182},7,[184],{"type":60,"tag":124,"props":185,"children":186},{},[187],{"type":69,"value":188},"{\"type\":\"invoice.paid\",\"timestamp\":\"2026-10-28T09:00:00Z\",\"data\":{\"invoice_id\":\"inv_8842\",\"amount\":4900}}\n",{"type":60,"tag":61,"props":190,"children":191},{},[192,194,200],{"type":69,"value":193},"That signature is the real HMAC of this example with the secret ",{"type":60,"tag":74,"props":195,"children":197},{"className":196},[],[198],{"type":69,"value":199},"whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw",{"type":69,"value":201},".",{"type":60,"tag":203,"props":204,"children":206},"h2",{"id":205},"the-three-headers",[207],{"type":69,"value":208},"The three headers",{"type":60,"tag":210,"props":211,"children":212},"table",{},[213,232],{"type":60,"tag":214,"props":215,"children":216},"thead",{},[217],{"type":60,"tag":218,"props":219,"children":220},"tr",{},[221,227],{"type":60,"tag":222,"props":223,"children":224},"th",{},[225],{"type":69,"value":226},"Header",{"type":60,"tag":222,"props":228,"children":229},{},[230],{"type":69,"value":231},"Meaning",{"type":60,"tag":233,"props":234,"children":235},"tbody",{},[236,259,282],{"type":60,"tag":218,"props":237,"children":238},{},[239,248],{"type":60,"tag":240,"props":241,"children":242},"td",{},[243],{"type":60,"tag":74,"props":244,"children":246},{"className":245},[],[247],{"type":69,"value":79},{"type":60,"tag":240,"props":249,"children":250},{},[251,253,258],{"type":69,"value":252},"Unique message ID. It stays the same across retries, so use it to deduplicate. See ",{"type":60,"tag":106,"props":254,"children":255},{"href":36},[256],{"type":69,"value":257},"idempotency",{"type":69,"value":201},{"type":60,"tag":218,"props":260,"children":261},{},[262,270],{"type":60,"tag":240,"props":263,"children":264},{},[265],{"type":60,"tag":74,"props":266,"children":268},{"className":267},[],[269],{"type":69,"value":87},{"type":60,"tag":240,"props":271,"children":272},{},[273,275,280],{"type":69,"value":274},"Unix time in ",{"type":60,"tag":65,"props":276,"children":277},{},[278],{"type":69,"value":279},"seconds",{"type":69,"value":281}," when this attempt was sent. Receivers reject values too far from now.",{"type":60,"tag":218,"props":283,"children":284},{},[285,293],{"type":60,"tag":240,"props":286,"children":287},{},[288],{"type":60,"tag":74,"props":289,"children":291},{"className":290},[],[292],{"type":69,"value":94},{"type":60,"tag":240,"props":294,"children":295},{},[296,298,304],{"type":69,"value":297},"One or more space-separated signatures, each ",{"type":60,"tag":74,"props":299,"children":301},{"className":300},[],[302],{"type":69,"value":303},"v1,\u003Cbase64 HMAC-SHA256>",{"type":69,"value":201},{"type":60,"tag":203,"props":306,"children":308},{"id":307},"how-signing-works",[309],{"type":69,"value":310},"How signing works",{"type":60,"tag":312,"props":313,"children":314},"ol",{},[315,342,372],{"type":60,"tag":316,"props":317,"children":318},"li",{},[319,324,326,332,334,340],{"type":60,"tag":65,"props":320,"children":321},{},[322],{"type":69,"value":323},"Signed content:",{"type":69,"value":325}," ",{"type":60,"tag":74,"props":327,"children":329},{"className":328},[],[330],{"type":69,"value":331},"{webhook-id}.{webhook-timestamp}.{body}",{"type":69,"value":333},", where ",{"type":60,"tag":74,"props":335,"children":337},{"className":336},[],[338],{"type":69,"value":339},"body",{"type":69,"value":341}," is the exact raw bytes sent.",{"type":60,"tag":316,"props":343,"children":344},{},[345,350,352,357,359,364,366,370],{"type":60,"tag":65,"props":346,"children":347},{},[348],{"type":69,"value":349},"Key:",{"type":69,"value":351}," strip ",{"type":60,"tag":74,"props":353,"children":355},{"className":354},[],[356],{"type":69,"value":102},{"type":69,"value":358}," from the ",{"type":60,"tag":106,"props":360,"children":361},{"href":30},[362],{"type":69,"value":363},"secret",{"type":69,"value":365}," and base64-decode the rest. Those bytes are the ",{"type":60,"tag":106,"props":367,"children":368},{"href":33},[369],{"type":69,"value":32},{"type":69,"value":371}," key, not the text.",{"type":60,"tag":316,"props":373,"children":374},{},[375,380,381,387,389,395],{"type":60,"tag":65,"props":376,"children":377},{},[378],{"type":69,"value":379},"Signature:",{"type":69,"value":325},{"type":60,"tag":74,"props":382,"children":384},{"className":383},[],[385],{"type":69,"value":386},"base64(HMAC-SHA256(key, signed_content))",{"type":69,"value":388},", sent as ",{"type":60,"tag":74,"props":390,"children":392},{"className":391},[],[393],{"type":69,"value":394},"v1,\u003Csignature>",{"type":69,"value":201},{"type":60,"tag":203,"props":397,"children":399},{"id":398},"verifying-in-nodejs",[400],{"type":69,"value":401},"Verifying in Node.js",{"type":60,"tag":114,"props":403,"children":407},{"className":404,"code":405,"language":406,"meta":7,"style":7},"language-js shiki shiki-themes github-dark","import crypto from 'node:crypto'\n\nexport function verify(rawBody, headers, secret, toleranceSeconds = 300) {\n  const id = headers['webhook-id']\n  const ts = headers['webhook-timestamp']\n  const sigs = headers['webhook-signature']\n  if (!id || !ts || !sigs) return false\n  if (Math.abs(Date.now() / 1000 - Number(ts)) > toleranceSeconds) return false\n\n  const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64')\n  const expected = crypto.createHmac('sha256', key).update(`${id}.${ts}.`).update(rawBody).digest()\n\n  return sigs.split(' ').some((entry) => {\n    const [version, sig] = entry.split(',')\n    if (version !== 'v1' || !sig) return false\n    const given = Buffer.from(sig, 'base64')\n    return given.length === expected.length && crypto.timingSafeEqual(given, expected)\n  })\n}\n","js",[408],{"type":60,"tag":74,"props":409,"children":410},{"__ignoreMap":7},[411,437,444,516,548,577,606,667,743,751,833,932,940,1001,1061,1107,1145,1197,1206],{"type":60,"tag":124,"props":412,"children":413},{"class":126,"line":127},[414,420,426,431],{"type":60,"tag":124,"props":415,"children":417},{"style":416},"--shiki-default:#F97583",[418],{"type":69,"value":419},"import",{"type":60,"tag":124,"props":421,"children":423},{"style":422},"--shiki-default:#E1E4E8",[424],{"type":69,"value":425}," crypto ",{"type":60,"tag":124,"props":427,"children":428},{"style":416},[429],{"type":69,"value":430},"from",{"type":60,"tag":124,"props":432,"children":434},{"style":433},"--shiki-default:#9ECBFF",[435],{"type":69,"value":436}," 'node:crypto'\n",{"type":60,"tag":124,"props":438,"children":439},{"class":126,"line":136},[440],{"type":60,"tag":124,"props":441,"children":442},{"emptyLinePlaceholder":176},[443],{"type":69,"value":179},{"type":60,"tag":124,"props":445,"children":446},{"class":126,"line":145},[447,452,457,463,468,474,478,483,487,491,495,500,505,511],{"type":60,"tag":124,"props":448,"children":449},{"style":416},[450],{"type":69,"value":451},"export",{"type":60,"tag":124,"props":453,"children":454},{"style":416},[455],{"type":69,"value":456}," function",{"type":60,"tag":124,"props":458,"children":460},{"style":459},"--shiki-default:#B392F0",[461],{"type":69,"value":462}," verify",{"type":60,"tag":124,"props":464,"children":465},{"style":422},[466],{"type":69,"value":467},"(",{"type":60,"tag":124,"props":469,"children":471},{"style":470},"--shiki-default:#FFAB70",[472],{"type":69,"value":473},"rawBody",{"type":60,"tag":124,"props":475,"children":476},{"style":422},[477],{"type":69,"value":81},{"type":60,"tag":124,"props":479,"children":480},{"style":470},[481],{"type":69,"value":482},"headers",{"type":60,"tag":124,"props":484,"children":485},{"style":422},[486],{"type":69,"value":81},{"type":60,"tag":124,"props":488,"children":489},{"style":470},[490],{"type":69,"value":363},{"type":60,"tag":124,"props":492,"children":493},{"style":422},[494],{"type":69,"value":81},{"type":60,"tag":124,"props":496,"children":497},{"style":470},[498],{"type":69,"value":499},"toleranceSeconds",{"type":60,"tag":124,"props":501,"children":502},{"style":416},[503],{"type":69,"value":504}," =",{"type":60,"tag":124,"props":506,"children":508},{"style":507},"--shiki-default:#79B8FF",[509],{"type":69,"value":510}," 300",{"type":60,"tag":124,"props":512,"children":513},{"style":422},[514],{"type":69,"value":515},") {\n",{"type":60,"tag":124,"props":517,"children":518},{"class":126,"line":154},[519,524,529,533,538,543],{"type":60,"tag":124,"props":520,"children":521},{"style":416},[522],{"type":69,"value":523},"  const",{"type":60,"tag":124,"props":525,"children":526},{"style":507},[527],{"type":69,"value":528}," id",{"type":60,"tag":124,"props":530,"children":531},{"style":416},[532],{"type":69,"value":504},{"type":60,"tag":124,"props":534,"children":535},{"style":422},[536],{"type":69,"value":537}," headers[",{"type":60,"tag":124,"props":539,"children":540},{"style":433},[541],{"type":69,"value":542},"'webhook-id'",{"type":60,"tag":124,"props":544,"children":545},{"style":422},[546],{"type":69,"value":547},"]\n",{"type":60,"tag":124,"props":549,"children":550},{"class":126,"line":163},[551,555,560,564,568,573],{"type":60,"tag":124,"props":552,"children":553},{"style":416},[554],{"type":69,"value":523},{"type":60,"tag":124,"props":556,"children":557},{"style":507},[558],{"type":69,"value":559}," ts",{"type":60,"tag":124,"props":561,"children":562},{"style":416},[563],{"type":69,"value":504},{"type":60,"tag":124,"props":565,"children":566},{"style":422},[567],{"type":69,"value":537},{"type":60,"tag":124,"props":569,"children":570},{"style":433},[571],{"type":69,"value":572},"'webhook-timestamp'",{"type":60,"tag":124,"props":574,"children":575},{"style":422},[576],{"type":69,"value":547},{"type":60,"tag":124,"props":578,"children":579},{"class":126,"line":172},[580,584,589,593,597,602],{"type":60,"tag":124,"props":581,"children":582},{"style":416},[583],{"type":69,"value":523},{"type":60,"tag":124,"props":585,"children":586},{"style":507},[587],{"type":69,"value":588}," sigs",{"type":60,"tag":124,"props":590,"children":591},{"style":416},[592],{"type":69,"value":504},{"type":60,"tag":124,"props":594,"children":595},{"style":422},[596],{"type":69,"value":537},{"type":60,"tag":124,"props":598,"children":599},{"style":433},[600],{"type":69,"value":601},"'webhook-signature'",{"type":60,"tag":124,"props":603,"children":604},{"style":422},[605],{"type":69,"value":547},{"type":60,"tag":124,"props":607,"children":608},{"class":126,"line":182},[609,614,619,624,629,634,639,644,648,652,657,662],{"type":60,"tag":124,"props":610,"children":611},{"style":416},[612],{"type":69,"value":613},"  if",{"type":60,"tag":124,"props":615,"children":616},{"style":422},[617],{"type":69,"value":618}," (",{"type":60,"tag":124,"props":620,"children":621},{"style":416},[622],{"type":69,"value":623},"!",{"type":60,"tag":124,"props":625,"children":626},{"style":422},[627],{"type":69,"value":628},"id ",{"type":60,"tag":124,"props":630,"children":631},{"style":416},[632],{"type":69,"value":633},"||",{"type":60,"tag":124,"props":635,"children":636},{"style":416},[637],{"type":69,"value":638}," !",{"type":60,"tag":124,"props":640,"children":641},{"style":422},[642],{"type":69,"value":643},"ts ",{"type":60,"tag":124,"props":645,"children":646},{"style":416},[647],{"type":69,"value":633},{"type":60,"tag":124,"props":649,"children":650},{"style":416},[651],{"type":69,"value":638},{"type":60,"tag":124,"props":653,"children":654},{"style":422},[655],{"type":69,"value":656},"sigs) ",{"type":60,"tag":124,"props":658,"children":659},{"style":416},[660],{"type":69,"value":661},"return",{"type":60,"tag":124,"props":663,"children":664},{"style":507},[665],{"type":69,"value":666}," false\n",{"type":60,"tag":124,"props":668,"children":670},{"class":126,"line":669},8,[671,675,680,685,690,695,700,705,710,715,720,725,730,735,739],{"type":60,"tag":124,"props":672,"children":673},{"style":416},[674],{"type":69,"value":613},{"type":60,"tag":124,"props":676,"children":677},{"style":422},[678],{"type":69,"value":679}," (Math.",{"type":60,"tag":124,"props":681,"children":682},{"style":459},[683],{"type":69,"value":684},"abs",{"type":60,"tag":124,"props":686,"children":687},{"style":422},[688],{"type":69,"value":689},"(Date.",{"type":60,"tag":124,"props":691,"children":692},{"style":459},[693],{"type":69,"value":694},"now",{"type":60,"tag":124,"props":696,"children":697},{"style":422},[698],{"type":69,"value":699},"() ",{"type":60,"tag":124,"props":701,"children":702},{"style":416},[703],{"type":69,"value":704},"/",{"type":60,"tag":124,"props":706,"children":707},{"style":507},[708],{"type":69,"value":709}," 1000",{"type":60,"tag":124,"props":711,"children":712},{"style":416},[713],{"type":69,"value":714}," -",{"type":60,"tag":124,"props":716,"children":717},{"style":459},[718],{"type":69,"value":719}," Number",{"type":60,"tag":124,"props":721,"children":722},{"style":422},[723],{"type":69,"value":724},"(ts)) ",{"type":60,"tag":124,"props":726,"children":727},{"style":416},[728],{"type":69,"value":729},">",{"type":60,"tag":124,"props":731,"children":732},{"style":422},[733],{"type":69,"value":734}," toleranceSeconds) ",{"type":60,"tag":124,"props":736,"children":737},{"style":416},[738],{"type":69,"value":661},{"type":60,"tag":124,"props":740,"children":741},{"style":507},[742],{"type":69,"value":666},{"type":60,"tag":124,"props":744,"children":746},{"class":126,"line":745},9,[747],{"type":60,"tag":124,"props":748,"children":749},{"emptyLinePlaceholder":176},[750],{"type":69,"value":179},{"type":60,"tag":124,"props":752,"children":754},{"class":126,"line":753},10,[755,759,764,768,773,777,782,787,791,795,800,805,809,813,818,823,828],{"type":60,"tag":124,"props":756,"children":757},{"style":416},[758],{"type":69,"value":523},{"type":60,"tag":124,"props":760,"children":761},{"style":507},[762],{"type":69,"value":763}," key",{"type":60,"tag":124,"props":765,"children":766},{"style":416},[767],{"type":69,"value":504},{"type":60,"tag":124,"props":769,"children":770},{"style":422},[771],{"type":69,"value":772}," Buffer.",{"type":60,"tag":124,"props":774,"children":775},{"style":459},[776],{"type":69,"value":430},{"type":60,"tag":124,"props":778,"children":779},{"style":422},[780],{"type":69,"value":781},"(secret.",{"type":60,"tag":124,"props":783,"children":784},{"style":459},[785],{"type":69,"value":786},"replace",{"type":60,"tag":124,"props":788,"children":789},{"style":422},[790],{"type":69,"value":467},{"type":60,"tag":124,"props":792,"children":793},{"style":433},[794],{"type":69,"value":704},{"type":60,"tag":124,"props":796,"children":797},{"style":416},[798],{"type":69,"value":799},"^",{"type":60,"tag":124,"props":801,"children":803},{"style":802},"--shiki-default:#DBEDFF",[804],{"type":69,"value":102},{"type":60,"tag":124,"props":806,"children":807},{"style":433},[808],{"type":69,"value":704},{"type":60,"tag":124,"props":810,"children":811},{"style":422},[812],{"type":69,"value":81},{"type":60,"tag":124,"props":814,"children":815},{"style":433},[816],{"type":69,"value":817},"''",{"type":60,"tag":124,"props":819,"children":820},{"style":422},[821],{"type":69,"value":822},"), ",{"type":60,"tag":124,"props":824,"children":825},{"style":433},[826],{"type":69,"value":827},"'base64'",{"type":60,"tag":124,"props":829,"children":830},{"style":422},[831],{"type":69,"value":832},")\n",{"type":60,"tag":124,"props":834,"children":836},{"class":126,"line":835},11,[837,841,846,850,855,860,864,869,874,879,883,888,893,898,903,908,913,917,922,927],{"type":60,"tag":124,"props":838,"children":839},{"style":416},[840],{"type":69,"value":523},{"type":60,"tag":124,"props":842,"children":843},{"style":507},[844],{"type":69,"value":845}," expected",{"type":60,"tag":124,"props":847,"children":848},{"style":416},[849],{"type":69,"value":504},{"type":60,"tag":124,"props":851,"children":852},{"style":422},[853],{"type":69,"value":854}," crypto.",{"type":60,"tag":124,"props":856,"children":857},{"style":459},[858],{"type":69,"value":859},"createHmac",{"type":60,"tag":124,"props":861,"children":862},{"style":422},[863],{"type":69,"value":467},{"type":60,"tag":124,"props":865,"children":866},{"style":433},[867],{"type":69,"value":868},"'sha256'",{"type":60,"tag":124,"props":870,"children":871},{"style":422},[872],{"type":69,"value":873},", key).",{"type":60,"tag":124,"props":875,"children":876},{"style":459},[877],{"type":69,"value":878},"update",{"type":60,"tag":124,"props":880,"children":881},{"style":422},[882],{"type":69,"value":467},{"type":60,"tag":124,"props":884,"children":885},{"style":433},[886],{"type":69,"value":887},"`${",{"type":60,"tag":124,"props":889,"children":890},{"style":422},[891],{"type":69,"value":892},"id",{"type":60,"tag":124,"props":894,"children":895},{"style":433},[896],{"type":69,"value":897},"}.${",{"type":60,"tag":124,"props":899,"children":900},{"style":422},[901],{"type":69,"value":902},"ts",{"type":60,"tag":124,"props":904,"children":905},{"style":433},[906],{"type":69,"value":907},"}.`",{"type":60,"tag":124,"props":909,"children":910},{"style":422},[911],{"type":69,"value":912},").",{"type":60,"tag":124,"props":914,"children":915},{"style":459},[916],{"type":69,"value":878},{"type":60,"tag":124,"props":918,"children":919},{"style":422},[920],{"type":69,"value":921},"(rawBody).",{"type":60,"tag":124,"props":923,"children":924},{"style":459},[925],{"type":69,"value":926},"digest",{"type":60,"tag":124,"props":928,"children":929},{"style":422},[930],{"type":69,"value":931},"()\n",{"type":60,"tag":124,"props":933,"children":935},{"class":126,"line":934},12,[936],{"type":60,"tag":124,"props":937,"children":938},{"emptyLinePlaceholder":176},[939],{"type":69,"value":179},{"type":60,"tag":124,"props":941,"children":942},{"class":126,"line":14},[943,948,953,958,962,967,971,976,981,986,991,996],{"type":60,"tag":124,"props":944,"children":945},{"style":416},[946],{"type":69,"value":947},"  return",{"type":60,"tag":124,"props":949,"children":950},{"style":422},[951],{"type":69,"value":952}," sigs.",{"type":60,"tag":124,"props":954,"children":955},{"style":459},[956],{"type":69,"value":957},"split",{"type":60,"tag":124,"props":959,"children":960},{"style":422},[961],{"type":69,"value":467},{"type":60,"tag":124,"props":963,"children":964},{"style":433},[965],{"type":69,"value":966},"' '",{"type":60,"tag":124,"props":968,"children":969},{"style":422},[970],{"type":69,"value":912},{"type":60,"tag":124,"props":972,"children":973},{"style":459},[974],{"type":69,"value":975},"some",{"type":60,"tag":124,"props":977,"children":978},{"style":422},[979],{"type":69,"value":980},"((",{"type":60,"tag":124,"props":982,"children":983},{"style":470},[984],{"type":69,"value":985},"entry",{"type":60,"tag":124,"props":987,"children":988},{"style":422},[989],{"type":69,"value":990},") ",{"type":60,"tag":124,"props":992,"children":993},{"style":416},[994],{"type":69,"value":995},"=>",{"type":60,"tag":124,"props":997,"children":998},{"style":422},[999],{"type":69,"value":1000}," {\n",{"type":60,"tag":124,"props":1002,"children":1004},{"class":126,"line":1003},14,[1005,1010,1015,1020,1024,1029,1034,1039,1044,1048,1052,1057],{"type":60,"tag":124,"props":1006,"children":1007},{"style":416},[1008],{"type":69,"value":1009},"    const",{"type":60,"tag":124,"props":1011,"children":1012},{"style":422},[1013],{"type":69,"value":1014}," [",{"type":60,"tag":124,"props":1016,"children":1017},{"style":507},[1018],{"type":69,"value":1019},"version",{"type":60,"tag":124,"props":1021,"children":1022},{"style":422},[1023],{"type":69,"value":81},{"type":60,"tag":124,"props":1025,"children":1026},{"style":507},[1027],{"type":69,"value":1028},"sig",{"type":60,"tag":124,"props":1030,"children":1031},{"style":422},[1032],{"type":69,"value":1033},"] ",{"type":60,"tag":124,"props":1035,"children":1036},{"style":416},[1037],{"type":69,"value":1038},"=",{"type":60,"tag":124,"props":1040,"children":1041},{"style":422},[1042],{"type":69,"value":1043}," entry.",{"type":60,"tag":124,"props":1045,"children":1046},{"style":459},[1047],{"type":69,"value":957},{"type":60,"tag":124,"props":1049,"children":1050},{"style":422},[1051],{"type":69,"value":467},{"type":60,"tag":124,"props":1053,"children":1054},{"style":433},[1055],{"type":69,"value":1056},"','",{"type":60,"tag":124,"props":1058,"children":1059},{"style":422},[1060],{"type":69,"value":832},{"type":60,"tag":124,"props":1062,"children":1064},{"class":126,"line":1063},15,[1065,1070,1075,1080,1085,1090,1094,1099,1103],{"type":60,"tag":124,"props":1066,"children":1067},{"style":416},[1068],{"type":69,"value":1069},"    if",{"type":60,"tag":124,"props":1071,"children":1072},{"style":422},[1073],{"type":69,"value":1074}," (version ",{"type":60,"tag":124,"props":1076,"children":1077},{"style":416},[1078],{"type":69,"value":1079},"!==",{"type":60,"tag":124,"props":1081,"children":1082},{"style":433},[1083],{"type":69,"value":1084}," 'v1'",{"type":60,"tag":124,"props":1086,"children":1087},{"style":416},[1088],{"type":69,"value":1089}," ||",{"type":60,"tag":124,"props":1091,"children":1092},{"style":416},[1093],{"type":69,"value":638},{"type":60,"tag":124,"props":1095,"children":1096},{"style":422},[1097],{"type":69,"value":1098},"sig) ",{"type":60,"tag":124,"props":1100,"children":1101},{"style":416},[1102],{"type":69,"value":661},{"type":60,"tag":124,"props":1104,"children":1105},{"style":507},[1106],{"type":69,"value":666},{"type":60,"tag":124,"props":1108,"children":1110},{"class":126,"line":1109},16,[1111,1115,1120,1124,1128,1132,1137,1141],{"type":60,"tag":124,"props":1112,"children":1113},{"style":416},[1114],{"type":69,"value":1009},{"type":60,"tag":124,"props":1116,"children":1117},{"style":507},[1118],{"type":69,"value":1119}," given",{"type":60,"tag":124,"props":1121,"children":1122},{"style":416},[1123],{"type":69,"value":504},{"type":60,"tag":124,"props":1125,"children":1126},{"style":422},[1127],{"type":69,"value":772},{"type":60,"tag":124,"props":1129,"children":1130},{"style":459},[1131],{"type":69,"value":430},{"type":60,"tag":124,"props":1133,"children":1134},{"style":422},[1135],{"type":69,"value":1136},"(sig, ",{"type":60,"tag":124,"props":1138,"children":1139},{"style":433},[1140],{"type":69,"value":827},{"type":60,"tag":124,"props":1142,"children":1143},{"style":422},[1144],{"type":69,"value":832},{"type":60,"tag":124,"props":1146,"children":1148},{"class":126,"line":1147},17,[1149,1154,1159,1164,1169,1174,1178,1183,1187,1192],{"type":60,"tag":124,"props":1150,"children":1151},{"style":416},[1152],{"type":69,"value":1153},"    return",{"type":60,"tag":124,"props":1155,"children":1156},{"style":422},[1157],{"type":69,"value":1158}," given.",{"type":60,"tag":124,"props":1160,"children":1161},{"style":507},[1162],{"type":69,"value":1163},"length",{"type":60,"tag":124,"props":1165,"children":1166},{"style":416},[1167],{"type":69,"value":1168}," ===",{"type":60,"tag":124,"props":1170,"children":1171},{"style":422},[1172],{"type":69,"value":1173}," expected.",{"type":60,"tag":124,"props":1175,"children":1176},{"style":507},[1177],{"type":69,"value":1163},{"type":60,"tag":124,"props":1179,"children":1180},{"style":416},[1181],{"type":69,"value":1182}," &&",{"type":60,"tag":124,"props":1184,"children":1185},{"style":422},[1186],{"type":69,"value":854},{"type":60,"tag":124,"props":1188,"children":1189},{"style":459},[1190],{"type":69,"value":1191},"timingSafeEqual",{"type":60,"tag":124,"props":1193,"children":1194},{"style":422},[1195],{"type":69,"value":1196},"(given, expected)\n",{"type":60,"tag":124,"props":1198,"children":1200},{"class":126,"line":1199},18,[1201],{"type":60,"tag":124,"props":1202,"children":1203},{"style":422},[1204],{"type":69,"value":1205},"  })\n",{"type":60,"tag":124,"props":1207,"children":1209},{"class":126,"line":1208},19,[1210],{"type":60,"tag":124,"props":1211,"children":1212},{"style":422},[1213],{"type":69,"value":1214},"}\n",{"type":60,"tag":61,"props":1216,"children":1217},{},[1218,1220,1226],{"type":69,"value":1219},"In production, the official ",{"type":60,"tag":74,"props":1221,"children":1223},{"className":1222},[],[1224],{"type":69,"value":1225},"standardwebhooks",{"type":69,"value":1227}," libraries (and Svix's libraries, which accept the same unbranded headers) do this for you.",{"type":60,"tag":203,"props":1229,"children":1231},{"id":1230},"why-it-exists",[1232],{"type":69,"value":1233},"Why it exists",{"type":60,"tag":61,"props":1235,"children":1236},{},[1237,1239,1245,1247,1253,1255,1261,1263,1269,1271,1277,1279,1285,1287,1293,1295,1300],{"type":69,"value":1238},"Before the spec, every provider invented its own scheme: ",{"type":60,"tag":74,"props":1240,"children":1242},{"className":1241},[],[1243],{"type":69,"value":1244},"Stripe-Signature",{"type":69,"value":1246}," with ",{"type":60,"tag":74,"props":1248,"children":1250},{"className":1249},[],[1251],{"type":69,"value":1252},"t=",{"type":69,"value":1254}," and ",{"type":60,"tag":74,"props":1256,"children":1258},{"className":1257},[],[1259],{"type":69,"value":1260},"v1=",{"type":69,"value":1262}," fields over ",{"type":60,"tag":74,"props":1264,"children":1266},{"className":1265},[],[1267],{"type":69,"value":1268},"{t}.{body}",{"type":69,"value":1270}," in hex, GitHub's ",{"type":60,"tag":74,"props":1272,"children":1274},{"className":1273},[],[1275],{"type":69,"value":1276},"X-Hub-Signature-256",{"type":69,"value":1278}," over the body in hex, Shopify's base64 ",{"type":60,"tag":74,"props":1280,"children":1282},{"className":1281},[],[1283],{"type":69,"value":1284},"X-Shopify-Hmac-Sha256",{"type":69,"value":1286},", Slack's ",{"type":60,"tag":74,"props":1288,"children":1290},{"className":1289},[],[1291],{"type":69,"value":1292},"v0:",{"type":69,"value":1294}," prefix. Each needs its own verifier code (see ",{"type":60,"tag":106,"props":1296,"children":1297},{"href":27},[1298],{"type":69,"value":1299},"webhook signature",{"type":69,"value":1301}," for the comparison). Standard Webhooks fixes the parts that matter for security in one place:",{"type":60,"tag":1303,"props":1304,"children":1305},"ul",{},[1306,1323,1333],{"type":60,"tag":316,"props":1307,"children":1308},{},[1309,1314,1316,1322],{"type":60,"tag":65,"props":1310,"children":1311},{},[1312],{"type":69,"value":1313},"The ID and timestamp are signed",{"type":69,"value":1315},", so an attacker can't change them or resend a captured request after the tolerance window. See ",{"type":60,"tag":106,"props":1317,"children":1319},{"href":1318},"/glossary/webhook-replay/",[1320],{"type":69,"value":1321},"webhook replay",{"type":69,"value":201},{"type":60,"tag":316,"props":1324,"children":1325},{},[1326,1331],{"type":60,"tag":65,"props":1327,"children":1328},{},[1329],{"type":69,"value":1330},"Multiple signatures",{"type":69,"value":1332}," make secret rotation possible without downtime.",{"type":60,"tag":316,"props":1334,"children":1335},{},[1336,1341,1343,1349],{"type":60,"tag":65,"props":1337,"children":1338},{},[1339],{"type":69,"value":1340},"A stable message ID",{"type":69,"value":1342}," across retries makes deduplication straightforward for ",{"type":60,"tag":106,"props":1344,"children":1346},{"href":1345},"/glossary/at-least-once-delivery/",[1347],{"type":69,"value":1348},"at-least-once delivery",{"type":69,"value":201},{"type":60,"tag":61,"props":1351,"children":1352},{},[1353,1355,1361],{"type":69,"value":1354},"The spec also defines an asymmetric ",{"type":60,"tag":74,"props":1356,"children":1358},{"className":1357},[],[1359],{"type":69,"value":1360},"v1a",{"type":69,"value":1362}," signature scheme (Ed25519) for senders that don't want receivers to hold a signing key, and recommends payload, retry and endpoint-disabling conventions.",{"type":60,"tag":203,"props":1364,"children":1366},{"id":1365},"common-mistakes",[1367],{"type":69,"value":1368},"Common mistakes",{"type":60,"tag":1303,"props":1370,"children":1371},{},[1372,1389,1405,1415,1425],{"type":60,"tag":316,"props":1373,"children":1374},{},[1375,1387],{"type":60,"tag":65,"props":1376,"children":1377},{},[1378,1380,1385],{"type":69,"value":1379},"Using the ",{"type":60,"tag":74,"props":1381,"children":1383},{"className":1382},[],[1384],{"type":69,"value":102},{"type":69,"value":1386}," text as the key.",{"type":69,"value":1388}," Decode the base64 after the prefix.",{"type":60,"tag":316,"props":1390,"children":1391},{},[1392,1397,1398,1403],{"type":60,"tag":65,"props":1393,"children":1394},{},[1395],{"type":69,"value":1396},"Millisecond timestamps.",{"type":69,"value":325},{"type":60,"tag":74,"props":1399,"children":1401},{"className":1400},[],[1402],{"type":69,"value":87},{"type":69,"value":1404}," is seconds, a 10-digit number. A 13-digit value falls outside every tolerance window.",{"type":60,"tag":316,"props":1406,"children":1407},{},[1408,1413],{"type":60,"tag":65,"props":1409,"children":1410},{},[1411],{"type":69,"value":1412},"Parsing before verifying.",{"type":69,"value":1414}," Re-serialized JSON has different bytes. Verify the raw body.",{"type":60,"tag":316,"props":1416,"children":1417},{},[1418,1423],{"type":60,"tag":65,"props":1419,"children":1420},{},[1421],{"type":69,"value":1422},"Checking only the first signature.",{"type":69,"value":1424}," During rotation there are two. Accept any match.",{"type":60,"tag":316,"props":1426,"children":1427},{},[1428,1433],{"type":60,"tag":65,"props":1429,"children":1430},{},[1431],{"type":69,"value":1432},"Hex instead of base64.",{"type":69,"value":1434}," Standard Webhooks signatures are base64.",{"type":60,"tag":203,"props":1436,"children":1438},{"id":1437},"standard-webhooks-in-webhook-relay",[1439],{"type":69,"value":1440},"Standard Webhooks in Webhook Relay",{"type":60,"tag":61,"props":1442,"children":1443},{},[1444,1446,1450,1452,1457,1459,1464],{"type":69,"value":1445},"The Webhook Relay ",{"type":60,"tag":106,"props":1447,"children":1448},{"href":42},[1449],{"type":69,"value":110},{"type":69,"value":1451},", for sending webhooks to your own customers, signs every delivery with Standard Webhooks headers. Each retry gets a new timestamp and signature but keeps the original ",{"type":60,"tag":74,"props":1453,"children":1455},{"className":1454},[],[1456],{"type":69,"value":79},{"type":69,"value":1458},", and for 24 hours after a secret rotation each delivery carries signatures from both the new and the previous secret. Receiver code for Node.js and Go is in ",{"type":60,"tag":106,"props":1460,"children":1461},{"href":39},[1462],{"type":69,"value":1463},"receiving Outbound Gateway webhooks",{"type":69,"value":201},{"type":60,"tag":1466,"props":1467,"children":1468},"style",{},[1469],{"type":69,"value":1470},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":145,"depth":145,"links":1472},[1473,1474,1475,1476,1477,1478],{"id":205,"depth":136,"text":208},{"id":307,"depth":136,"text":310},{"id":398,"depth":136,"text":401},{"id":1230,"depth":136,"text":1233},{"id":1365,"depth":136,"text":1368},{"id":1437,"depth":136,"text":1440},"markdown","content:glossary:standard-webhooks.md","content","glossary/standard-webhooks.md","glossary/standard-webhooks","md",{"loc":4},1793199805335]