[{"data":1,"prerenderedAt":1014},["ShallowReactive",2],{"content-query-TktSFl31bI":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":42,"body":58,"_type":1007,"_id":1008,"_source":1009,"_file":1010,"_stem":1011,"_extension":1012,"sitemap":1013},"/glossary/idempotency","glossary",false,"","What is idempotency? Idempotent webhooks and APIs","Idempotency means doing an operation twice has the same effect as doing it once. Why webhook handlers must be idempotent, idempotency keys, and code for deduplicating events.","reference","Idempotency","Doing it twice has the same effect as doing it once.","Delivery and reliability",22,"2026-10-28",{"eyebrow":17,"title":18,"text":19,"to":20,"label":21,"secondaryTo":22,"secondaryLabel":23},"Webhook Relay durable retries","Retry safely: durable delivery for idempotent handlers","Once your handler deduplicates on the event ID, retries become free insurance. Webhook Relay persists every webhook and retries failed deliveries with exponential backoff for up to 30 days, to public or private endpoints.","/features/durable-retries/","See durable retries","/blog/webhook-retries-and-idempotency/","Retries and idempotency guide",[25,28,31,34,37,40],{"title":26,"to":27},"At-least-once delivery","/glossary/at-least-once-delivery/",{"title":29,"to":30},"Webhook retries","/glossary/webhook-retries/",{"title":32,"to":33},"Webhook replay","/glossary/webhook-replay/",{"title":35,"to":36},"409 Conflict","/http-errors/409-conflict/",{"title":38,"to":39},"Standard Webhooks","/glossary/standard-webhooks/",{"title":41,"to":22},"Webhook retries and idempotency",[43,46,49,52,55],{"q":44,"a":45},"What is idempotency?","Idempotency is the property that performing an operation more than once has the same effect as performing it once. Setting a value to 5 is idempotent, adding 5 to a value is not. In APIs and webhooks it means a repeated request does not create a second order, charge or email.",{"q":47,"a":48},"Why do webhook handlers need to be idempotent?","Because webhooks are delivered at least once. Providers retry when your endpoint times out or returns an error, sometimes after you already processed the event, and you or your tools may replay events after an outage. Without idempotency, each duplicate repeats the side effect.",{"q":50,"a":51},"How do I make a webhook handler idempotent?","Use the event ID from the payload or a header such as webhook-id. Insert it into a table with a unique constraint inside the same transaction as the side effect. If the insert fails because the ID already exists, skip processing and return 200.",{"q":53,"a":54},"What is an idempotency key?","An idempotency key is a unique value a client sends with a request, often in an Idempotency-Key header, so the server can recognise a retry of the same request. The server stores the result for that key and returns it again instead of repeating the operation. Stripe's API is a well-known example.",{"q":56,"a":57},"Are HTTP methods idempotent?","By definition in the HTTP spec, GET, HEAD, PUT, DELETE and OPTIONS are idempotent, and POST and PATCH are not. Webhooks are almost always POST requests, so the receiver has to add idempotency itself.",{"type":59,"children":60,"toc":1000},"root",[61,92,140,145,152,165,212,217,223,265,617,622,671,676,682,694,719,749,775,781,935,941,982,994],{"type":62,"tag":63,"props":64,"children":65},"element","p",{},[66,73,75,82,84,90],{"type":62,"tag":67,"props":68,"children":69},"strong",{},[70],{"type":71,"value":72},"text","Idempotency is the property that doing an operation more than once has the same effect as doing it once.",{"type":71,"value":74}," ",{"type":62,"tag":76,"props":77,"children":79},"code",{"className":78},[],[80],{"type":71,"value":81},"SET balance = 100",{"type":71,"value":83}," is idempotent; ",{"type":62,"tag":76,"props":85,"children":87},{"className":86},[],[88],{"type":71,"value":89},"balance = balance + 100",{"type":71,"value":91}," is not. For webhooks and APIs it means a repeated request, from a retry, a replay or a duplicate delivery, doesn't create a second order, charge the card twice or send the email again.",{"type":62,"tag":93,"props":94,"children":98},"pre",{"className":95,"code":96,"language":97,"meta":7,"style":7},"language-sql shiki shiki-themes github-dark","CREATE TABLE processed_events (\n  event_id    text PRIMARY KEY,\n  received_at timestamptz NOT NULL DEFAULT now()\n);\n","sql",[99],{"type":62,"tag":76,"props":100,"children":101},{"__ignoreMap":7},[102,113,122,131],{"type":62,"tag":103,"props":104,"children":107},"span",{"class":105,"line":106},"line",1,[108],{"type":62,"tag":103,"props":109,"children":110},{},[111],{"type":71,"value":112},"CREATE TABLE processed_events (\n",{"type":62,"tag":103,"props":114,"children":116},{"class":105,"line":115},2,[117],{"type":62,"tag":103,"props":118,"children":119},{},[120],{"type":71,"value":121},"  event_id    text PRIMARY KEY,\n",{"type":62,"tag":103,"props":123,"children":125},{"class":105,"line":124},3,[126],{"type":62,"tag":103,"props":127,"children":128},{},[129],{"type":71,"value":130},"  received_at timestamptz NOT NULL DEFAULT now()\n",{"type":62,"tag":103,"props":132,"children":134},{"class":105,"line":133},4,[135],{"type":62,"tag":103,"props":136,"children":137},{},[138],{"type":71,"value":139},");\n",{"type":62,"tag":63,"props":141,"children":142},{},[143],{"type":71,"value":144},"That one table, keyed on the event ID, is the core of most idempotent webhook handlers.",{"type":62,"tag":146,"props":147,"children":149},"h2",{"id":148},"why-webhooks-need-idempotency",[150],{"type":71,"value":151},"Why webhooks need idempotency",{"type":62,"tag":63,"props":153,"children":154},{},[155,157,163],{"type":71,"value":156},"Webhooks are delivered ",{"type":62,"tag":158,"props":159,"children":160},"a",{"href":27},[161],{"type":71,"value":162},"at least once",{"type":71,"value":164},", not exactly once. Duplicates happen when:",{"type":62,"tag":166,"props":167,"children":168},"ul",{},[169,182,195,200],{"type":62,"tag":170,"props":171,"children":172},"li",{},[173,175,180],{"type":71,"value":174},"Your handler processed the event but responded too slowly, so the provider timed out and ",{"type":62,"tag":158,"props":176,"children":177},{"href":30},[178],{"type":71,"value":179},"retried",{"type":71,"value":181},".",{"type":62,"tag":170,"props":183,"children":184},{},[185,187,193],{"type":71,"value":186},"Your handler returned ",{"type":62,"tag":76,"props":188,"children":190},{"className":189},[],[191],{"type":71,"value":192},"500",{"type":71,"value":194}," after the side effect had already happened.",{"type":62,"tag":170,"props":196,"children":197},{},[198],{"type":71,"value":199},"The provider's own infrastructure delivered twice, which every large provider documents as possible.",{"type":62,"tag":170,"props":201,"children":202},{},[203,205,210],{"type":71,"value":204},"You or your tooling ",{"type":62,"tag":158,"props":206,"children":207},{"href":33},[208],{"type":71,"value":209},"replayed",{"type":71,"value":211}," events after an outage.",{"type":62,"tag":63,"props":213,"children":214},{},[215],{"type":71,"value":216},"If the handler isn't idempotent, each of these repeats whatever it did.",{"type":62,"tag":146,"props":218,"children":220},{"id":219},"making-a-webhook-handler-idempotent",[221],{"type":71,"value":222},"Making a webhook handler idempotent",{"type":62,"tag":63,"props":224,"children":225},{},[226,228,233,235,241,243,249,251,255,257,263],{"type":71,"value":227},"Key on the provider's ",{"type":62,"tag":67,"props":229,"children":230},{},[231],{"type":71,"value":232},"event ID",{"type":71,"value":234},", not on the payload contents. Stripe puts it in ",{"type":62,"tag":76,"props":236,"children":238},{"className":237},[],[239],{"type":71,"value":240},"id",{"type":71,"value":242},", GitHub sends ",{"type":62,"tag":76,"props":244,"children":246},{"className":245},[],[247],{"type":71,"value":248},"X-GitHub-Delivery",{"type":71,"value":250},", and ",{"type":62,"tag":158,"props":252,"children":253},{"href":39},[254],{"type":71,"value":38},{"type":71,"value":256}," providers send ",{"type":62,"tag":76,"props":258,"children":260},{"className":259},[],[261],{"type":71,"value":262},"webhook-id",{"type":71,"value":264},", which stays the same across retries.",{"type":62,"tag":93,"props":266,"children":270},{"className":267,"code":268,"language":269,"meta":7,"style":7},"language-js shiki shiki-themes github-dark","app.post('/webhooks', async (req, res) => {\n  const event = verifyAndParse(req)                 // signature check first\n  try {\n    await db.tx(async (tx) => {\n      await tx.query('INSERT INTO processed_events (event_id) VALUES ($1)', [event.id])\n      await fulfilOrder(tx, event.data)             // side effect in the same transaction\n    })\n  } catch (err) {\n    if (err.code !== '23505') throw err             // 23505 = unique violation: already processed\n  }\n  res.sendStatus(200)                               // duplicates are acknowledged, not reprocessed\n})\n","js",[271],{"type":62,"tag":76,"props":272,"children":273},{"__ignoreMap":7},[274,346,381,393,439,472,495,504,523,566,575,608],{"type":62,"tag":103,"props":275,"children":276},{"class":105,"line":106},[277,283,289,294,300,305,311,316,322,326,331,336,341],{"type":62,"tag":103,"props":278,"children":280},{"style":279},"--shiki-default:#E1E4E8",[281],{"type":71,"value":282},"app.",{"type":62,"tag":103,"props":284,"children":286},{"style":285},"--shiki-default:#B392F0",[287],{"type":71,"value":288},"post",{"type":62,"tag":103,"props":290,"children":291},{"style":279},[292],{"type":71,"value":293},"(",{"type":62,"tag":103,"props":295,"children":297},{"style":296},"--shiki-default:#9ECBFF",[298],{"type":71,"value":299},"'/webhooks'",{"type":62,"tag":103,"props":301,"children":302},{"style":279},[303],{"type":71,"value":304},", ",{"type":62,"tag":103,"props":306,"children":308},{"style":307},"--shiki-default:#F97583",[309],{"type":71,"value":310},"async",{"type":62,"tag":103,"props":312,"children":313},{"style":279},[314],{"type":71,"value":315}," (",{"type":62,"tag":103,"props":317,"children":319},{"style":318},"--shiki-default:#FFAB70",[320],{"type":71,"value":321},"req",{"type":62,"tag":103,"props":323,"children":324},{"style":279},[325],{"type":71,"value":304},{"type":62,"tag":103,"props":327,"children":328},{"style":318},[329],{"type":71,"value":330},"res",{"type":62,"tag":103,"props":332,"children":333},{"style":279},[334],{"type":71,"value":335},") ",{"type":62,"tag":103,"props":337,"children":338},{"style":307},[339],{"type":71,"value":340},"=>",{"type":62,"tag":103,"props":342,"children":343},{"style":279},[344],{"type":71,"value":345}," {\n",{"type":62,"tag":103,"props":347,"children":348},{"class":105,"line":115},[349,354,360,365,370,375],{"type":62,"tag":103,"props":350,"children":351},{"style":307},[352],{"type":71,"value":353},"  const",{"type":62,"tag":103,"props":355,"children":357},{"style":356},"--shiki-default:#79B8FF",[358],{"type":71,"value":359}," event",{"type":62,"tag":103,"props":361,"children":362},{"style":307},[363],{"type":71,"value":364}," =",{"type":62,"tag":103,"props":366,"children":367},{"style":285},[368],{"type":71,"value":369}," verifyAndParse",{"type":62,"tag":103,"props":371,"children":372},{"style":279},[373],{"type":71,"value":374},"(req)                 ",{"type":62,"tag":103,"props":376,"children":378},{"style":377},"--shiki-default:#6A737D",[379],{"type":71,"value":380},"// signature check first\n",{"type":62,"tag":103,"props":382,"children":383},{"class":105,"line":124},[384,389],{"type":62,"tag":103,"props":385,"children":386},{"style":307},[387],{"type":71,"value":388},"  try",{"type":62,"tag":103,"props":390,"children":391},{"style":279},[392],{"type":71,"value":345},{"type":62,"tag":103,"props":394,"children":395},{"class":105,"line":133},[396,401,406,411,415,419,423,427,431,435],{"type":62,"tag":103,"props":397,"children":398},{"style":307},[399],{"type":71,"value":400},"    await",{"type":62,"tag":103,"props":402,"children":403},{"style":279},[404],{"type":71,"value":405}," db.",{"type":62,"tag":103,"props":407,"children":408},{"style":285},[409],{"type":71,"value":410},"tx",{"type":62,"tag":103,"props":412,"children":413},{"style":279},[414],{"type":71,"value":293},{"type":62,"tag":103,"props":416,"children":417},{"style":307},[418],{"type":71,"value":310},{"type":62,"tag":103,"props":420,"children":421},{"style":279},[422],{"type":71,"value":315},{"type":62,"tag":103,"props":424,"children":425},{"style":318},[426],{"type":71,"value":410},{"type":62,"tag":103,"props":428,"children":429},{"style":279},[430],{"type":71,"value":335},{"type":62,"tag":103,"props":432,"children":433},{"style":307},[434],{"type":71,"value":340},{"type":62,"tag":103,"props":436,"children":437},{"style":279},[438],{"type":71,"value":345},{"type":62,"tag":103,"props":440,"children":442},{"class":105,"line":441},5,[443,448,453,458,462,467],{"type":62,"tag":103,"props":444,"children":445},{"style":307},[446],{"type":71,"value":447},"      await",{"type":62,"tag":103,"props":449,"children":450},{"style":279},[451],{"type":71,"value":452}," tx.",{"type":62,"tag":103,"props":454,"children":455},{"style":285},[456],{"type":71,"value":457},"query",{"type":62,"tag":103,"props":459,"children":460},{"style":279},[461],{"type":71,"value":293},{"type":62,"tag":103,"props":463,"children":464},{"style":296},[465],{"type":71,"value":466},"'INSERT INTO processed_events (event_id) VALUES ($1)'",{"type":62,"tag":103,"props":468,"children":469},{"style":279},[470],{"type":71,"value":471},", [event.id])\n",{"type":62,"tag":103,"props":473,"children":475},{"class":105,"line":474},6,[476,480,485,490],{"type":62,"tag":103,"props":477,"children":478},{"style":307},[479],{"type":71,"value":447},{"type":62,"tag":103,"props":481,"children":482},{"style":285},[483],{"type":71,"value":484}," fulfilOrder",{"type":62,"tag":103,"props":486,"children":487},{"style":279},[488],{"type":71,"value":489},"(tx, event.data)             ",{"type":62,"tag":103,"props":491,"children":492},{"style":377},[493],{"type":71,"value":494},"// side effect in the same transaction\n",{"type":62,"tag":103,"props":496,"children":498},{"class":105,"line":497},7,[499],{"type":62,"tag":103,"props":500,"children":501},{"style":279},[502],{"type":71,"value":503},"    })\n",{"type":62,"tag":103,"props":505,"children":507},{"class":105,"line":506},8,[508,513,518],{"type":62,"tag":103,"props":509,"children":510},{"style":279},[511],{"type":71,"value":512},"  } ",{"type":62,"tag":103,"props":514,"children":515},{"style":307},[516],{"type":71,"value":517},"catch",{"type":62,"tag":103,"props":519,"children":520},{"style":279},[521],{"type":71,"value":522}," (err) {\n",{"type":62,"tag":103,"props":524,"children":526},{"class":105,"line":525},9,[527,532,537,542,547,551,556,561],{"type":62,"tag":103,"props":528,"children":529},{"style":307},[530],{"type":71,"value":531},"    if",{"type":62,"tag":103,"props":533,"children":534},{"style":279},[535],{"type":71,"value":536}," (err.code ",{"type":62,"tag":103,"props":538,"children":539},{"style":307},[540],{"type":71,"value":541},"!==",{"type":62,"tag":103,"props":543,"children":544},{"style":296},[545],{"type":71,"value":546}," '23505'",{"type":62,"tag":103,"props":548,"children":549},{"style":279},[550],{"type":71,"value":335},{"type":62,"tag":103,"props":552,"children":553},{"style":307},[554],{"type":71,"value":555},"throw",{"type":62,"tag":103,"props":557,"children":558},{"style":279},[559],{"type":71,"value":560}," err             ",{"type":62,"tag":103,"props":562,"children":563},{"style":377},[564],{"type":71,"value":565},"// 23505 = unique violation: already processed\n",{"type":62,"tag":103,"props":567,"children":569},{"class":105,"line":568},10,[570],{"type":62,"tag":103,"props":571,"children":572},{"style":279},[573],{"type":71,"value":574},"  }\n",{"type":62,"tag":103,"props":576,"children":578},{"class":105,"line":577},11,[579,584,589,593,598,603],{"type":62,"tag":103,"props":580,"children":581},{"style":279},[582],{"type":71,"value":583},"  res.",{"type":62,"tag":103,"props":585,"children":586},{"style":285},[587],{"type":71,"value":588},"sendStatus",{"type":62,"tag":103,"props":590,"children":591},{"style":279},[592],{"type":71,"value":293},{"type":62,"tag":103,"props":594,"children":595},{"style":356},[596],{"type":71,"value":597},"200",{"type":62,"tag":103,"props":599,"children":600},{"style":279},[601],{"type":71,"value":602},")                               ",{"type":62,"tag":103,"props":604,"children":605},{"style":377},[606],{"type":71,"value":607},"// duplicates are acknowledged, not reprocessed\n",{"type":62,"tag":103,"props":609,"children":611},{"class":105,"line":610},12,[612],{"type":62,"tag":103,"props":613,"children":614},{"style":279},[615],{"type":71,"value":616},"})\n",{"type":62,"tag":63,"props":618,"children":619},{},[620],{"type":71,"value":621},"What makes this work:",{"type":62,"tag":623,"props":624,"children":625},"ol",{},[626,644,654],{"type":62,"tag":170,"props":627,"children":628},{},[629,634,636,642],{"type":62,"tag":67,"props":630,"children":631},{},[632],{"type":71,"value":633},"A unique constraint",{"type":71,"value":635},", not a \"check then insert\". Two copies of the same event arriving at once will both pass a ",{"type":62,"tag":76,"props":637,"children":639},{"className":638},[],[640],{"type":71,"value":641},"SELECT",{"type":71,"value":643}," check.",{"type":62,"tag":170,"props":645,"children":646},{},[647,652],{"type":62,"tag":67,"props":648,"children":649},{},[650],{"type":71,"value":651},"The same transaction",{"type":71,"value":653}," for the dedupe record and the side effect, so a crash can't record one without the other.",{"type":62,"tag":170,"props":655,"children":656},{},[657,669],{"type":62,"tag":67,"props":658,"children":659},{},[660,662,667],{"type":71,"value":661},"Return ",{"type":62,"tag":76,"props":663,"children":665},{"className":664},[],[666],{"type":71,"value":597},{"type":71,"value":668}," for duplicates.",{"type":71,"value":670}," Returning an error makes the provider retry a duplicate forever.",{"type":62,"tag":63,"props":672,"children":673},{},[674],{"type":71,"value":675},"When the side effect is outside your database, for example calling a payment API or sending an email, pass the event ID on as an idempotency key to that service if it supports one, or record the outcome before acknowledging.",{"type":62,"tag":146,"props":677,"children":679},{"id":678},"idempotency-keys-in-apis",[680],{"type":71,"value":681},"Idempotency keys in APIs",{"type":62,"tag":63,"props":683,"children":684},{},[685,687,692],{"type":71,"value":686},"The same idea from the client side: an ",{"type":62,"tag":67,"props":688,"children":689},{},[690],{"type":71,"value":691},"idempotency key",{"type":71,"value":693}," is a unique value the client sends with a request so the server can spot a retry.",{"type":62,"tag":93,"props":695,"children":699},{"className":696,"code":697,"language":698,"meta":7,"style":7},"language-http shiki shiki-themes github-dark","POST /v1/charges HTTP/1.1\nIdempotency-Key: 5f1c2c9a-6f3e-4c1d-9b0a-2b7e1f8c4d11\n","http",[700],{"type":62,"tag":76,"props":701,"children":702},{"__ignoreMap":7},[703,711],{"type":62,"tag":103,"props":704,"children":705},{"class":105,"line":106},[706],{"type":62,"tag":103,"props":707,"children":708},{},[709],{"type":71,"value":710},"POST /v1/charges HTTP/1.1\n",{"type":62,"tag":103,"props":712,"children":713},{"class":105,"line":115},[714],{"type":62,"tag":103,"props":715,"children":716},{},[717],{"type":71,"value":718},"Idempotency-Key: 5f1c2c9a-6f3e-4c1d-9b0a-2b7e1f8c4d11\n",{"type":62,"tag":63,"props":720,"children":721},{},[722,724,730,732,740,742,748],{"type":71,"value":723},"The server stores the response for that key and returns the same response for any repeat, rather than charging twice. Stripe's API works this way, and an IETF draft standardises the ",{"type":62,"tag":76,"props":725,"children":727},{"className":726},[],[728],{"type":71,"value":729},"Idempotency-Key",{"type":71,"value":731}," header. A repeat with the same key but a different body is usually rejected, sometimes with ",{"type":62,"tag":158,"props":733,"children":734},{"href":36},[735],{"type":62,"tag":76,"props":736,"children":738},{"className":737},[],[739],{"type":71,"value":35},{"type":71,"value":741}," or ",{"type":62,"tag":76,"props":743,"children":745},{"className":744},[],[746],{"type":71,"value":747},"422",{"type":71,"value":181},{"type":62,"tag":63,"props":750,"children":751},{},[752,754,760,762,767,769,774],{"type":71,"value":753},"The Webhook Relay ",{"type":62,"tag":158,"props":755,"children":757},{"href":756},"/outbound-webhooks/",[758],{"type":71,"value":759},"Outbound Gateway",{"type":71,"value":761}," works the same way when you publish a message: a repeat call with the same ",{"type":62,"tag":76,"props":763,"children":765},{"className":764},[],[766],{"type":71,"value":729},{"type":71,"value":768}," and content returns the original receipt and publishes nothing new, and reusing a key with different content is rejected with ",{"type":62,"tag":76,"props":770,"children":772},{"className":771},[],[773],{"type":71,"value":35},{"type":71,"value":181},{"type":62,"tag":146,"props":776,"children":778},{"id":777},"idempotent-http-methods",[779],{"type":71,"value":780},"Idempotent HTTP methods",{"type":62,"tag":782,"props":783,"children":784},"table",{},[785,809],{"type":62,"tag":786,"props":787,"children":788},"thead",{},[789],{"type":62,"tag":790,"props":791,"children":792},"tr",{},[793,799,804],{"type":62,"tag":794,"props":795,"children":796},"th",{},[797],{"type":71,"value":798},"Method",{"type":62,"tag":794,"props":800,"children":801},{},[802],{"type":71,"value":803},"Idempotent by spec",{"type":62,"tag":794,"props":805,"children":806},{},[807],{"type":71,"value":808},"Notes",{"type":62,"tag":810,"props":811,"children":812},"tbody",{},[813,850,871,892,914],{"type":62,"tag":790,"props":814,"children":815},{},[816,840,845],{"type":62,"tag":817,"props":818,"children":819},"td",{},[820,826,827,833,834],{"type":62,"tag":76,"props":821,"children":823},{"className":822},[],[824],{"type":71,"value":825},"GET",{"type":71,"value":304},{"type":62,"tag":76,"props":828,"children":830},{"className":829},[],[831],{"type":71,"value":832},"HEAD",{"type":71,"value":304},{"type":62,"tag":76,"props":835,"children":837},{"className":836},[],[838],{"type":71,"value":839},"OPTIONS",{"type":62,"tag":817,"props":841,"children":842},{},[843],{"type":71,"value":844},"Yes",{"type":62,"tag":817,"props":846,"children":847},{},[848],{"type":71,"value":849},"Also safe: no side effects",{"type":62,"tag":790,"props":851,"children":852},{},[853,862,866],{"type":62,"tag":817,"props":854,"children":855},{},[856],{"type":62,"tag":76,"props":857,"children":859},{"className":858},[],[860],{"type":71,"value":861},"PUT",{"type":62,"tag":817,"props":863,"children":864},{},[865],{"type":71,"value":844},{"type":62,"tag":817,"props":867,"children":868},{},[869],{"type":71,"value":870},"Replaces the resource with the same state",{"type":62,"tag":790,"props":872,"children":873},{},[874,883,887],{"type":62,"tag":817,"props":875,"children":876},{},[877],{"type":62,"tag":76,"props":878,"children":880},{"className":879},[],[881],{"type":71,"value":882},"DELETE",{"type":62,"tag":817,"props":884,"children":885},{},[886],{"type":71,"value":844},{"type":62,"tag":817,"props":888,"children":889},{},[890],{"type":71,"value":891},"Deleting twice leaves it deleted (the status code may differ)",{"type":62,"tag":790,"props":893,"children":894},{},[895,904,909],{"type":62,"tag":817,"props":896,"children":897},{},[898],{"type":62,"tag":76,"props":899,"children":901},{"className":900},[],[902],{"type":71,"value":903},"POST",{"type":62,"tag":817,"props":905,"children":906},{},[907],{"type":71,"value":908},"No",{"type":62,"tag":817,"props":910,"children":911},{},[912],{"type":71,"value":913},"Webhooks use this, so you add idempotency yourself",{"type":62,"tag":790,"props":915,"children":916},{},[917,926,930],{"type":62,"tag":817,"props":918,"children":919},{},[920],{"type":62,"tag":76,"props":921,"children":923},{"className":922},[],[924],{"type":71,"value":925},"PATCH",{"type":62,"tag":817,"props":927,"children":928},{},[929],{"type":71,"value":908},{"type":62,"tag":817,"props":931,"children":932},{},[933],{"type":71,"value":934},"Depends on the patch",{"type":62,"tag":146,"props":936,"children":938},{"id":937},"common-mistakes",[939],{"type":71,"value":940},"Common mistakes",{"type":62,"tag":166,"props":942,"children":943},{},[944,954,964],{"type":62,"tag":170,"props":945,"children":946},{},[947,952],{"type":62,"tag":67,"props":948,"children":949},{},[950],{"type":71,"value":951},"Deduplicating on a hash of the body.",{"type":71,"value":953}," Some providers change a field, such as an attempt counter or timestamp, on each retry. Use the event ID.",{"type":62,"tag":170,"props":955,"children":956},{},[957,962],{"type":62,"tag":67,"props":958,"children":959},{},[960],{"type":71,"value":961},"Expiring dedupe records too soon.",{"type":71,"value":963}," Keep IDs at least as long as the provider's retry and replay window, which can be days or weeks.",{"type":62,"tag":170,"props":965,"children":966},{},[967,972,974,980],{"type":62,"tag":67,"props":968,"children":969},{},[970],{"type":71,"value":971},"Assuming order.",{"type":71,"value":973}," Idempotency stops duplicates, not reordering. A retried ",{"type":62,"tag":76,"props":975,"children":977},{"className":976},[],[978],{"type":71,"value":979},"subscription.updated",{"type":71,"value":981}," can arrive after a newer one. Compare timestamps or fetch current state.",{"type":62,"tag":63,"props":983,"children":984},{},[985,987,992],{"type":71,"value":986},"With an idempotent handler, retries become harmless, which is what makes long retry windows like Webhook Relay's ",{"type":62,"tag":158,"props":988,"children":989},{"href":20},[990],{"type":71,"value":991},"durable retries",{"type":71,"value":993}," safe to turn on.",{"type":62,"tag":995,"props":996,"children":997},"style",{},[998],{"type":71,"value":999},"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":124,"depth":124,"links":1001},[1002,1003,1004,1005,1006],{"id":148,"depth":115,"text":151},{"id":219,"depth":115,"text":222},{"id":678,"depth":115,"text":681},{"id":777,"depth":115,"text":780},{"id":937,"depth":115,"text":940},"markdown","content:glossary:idempotency.md","content","glossary/idempotency.md","glossary/idempotency","md",{"loc":4},1793199805794]