[{"data":1,"prerenderedAt":430},["ShallowReactive",2],{"navigation":3,"page:\u002Fidempotency":45},{"title":4,"links":5},"API reference",[6,9,12,15,18,21,24,27,30,33,36,39,42],{"title":7,"href":8},"Accounts","\u002Freference\u002Faccounts",{"title":10,"href":11},"Connect","\u002Freference\u002Fconnect",{"title":13,"href":14},"Calendars","\u002Freference\u002Fcalendars",{"title":16,"href":17},"Events","\u002Freference\u002Fevents",{"title":19,"href":20},"Availability","\u002Freference\u002Favailability",{"title":22,"href":23},"Meetings","\u002Freference\u002Fmeetings",{"title":25,"href":26},"Invitations","\u002Freference\u002Finvitations",{"title":28,"href":29},"Webhooks","\u002Freference\u002Fwebhooks",{"title":31,"href":32},"Metadata","\u002Freference\u002Fmetadata",{"title":34,"href":35},"Keys","\u002Freference\u002Fkeys",{"title":37,"href":38},"Sandbox","\u002Freference\u002Fsandbox",{"title":40,"href":41},"Usage","\u002Freference\u002Fusage",{"title":43,"href":44},"Logs","\u002Freference\u002Flogs",{"kind":46,"title":47,"description":48,"sections":49,"doc":59},"guide","Retries and idempotency","Retry any request safely with an Idempotency-Key, learn which errors are worth retrying, and avoid creating an event twice.",[50,53,56],{"id":51,"title":52},"how-it-works","How it works",{"id":54,"title":55},"what-to-retry","What to retry",{"id":57,"title":58},"prevent-conflicting-updates","Prevent conflicting updates",{"id":60,"title":47,"body":61,"description":48,"extension":420,"hero":421,"meta":422,"navigation":423,"path":424,"rawbody":425,"sections":426,"seo":427,"stem":428,"v1":426,"__hash__":429},"docs\u002Fidempotency.md",{"type":62,"value":63,"toc":415},"minimark",[64,68,80,159,181,223,226,284,286,299,314,396,399,401,411],[65,66,47],"h1",{"id":67},"retries-and-idempotency",[69,70,71],"lead",{},[72,73,74,75,79],"p",{},"If a network error occurs, you might not receive a response. For example, if a create request times out, you can't tell whether the resource was created. To retry safely, send an ",[76,77,78],"code",{},"Idempotency-Key"," header and retry with the same key. The API returns the original response and doesn't create anything twice.",[81,82,87],"pre",{"className":83,"code":84,"language":85,"meta":86,"style":86},"language-bash shiki shiki-themes css-variables","curl -X POST https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fevents \\\n  -H \"x-api-key: $PYAS_API_KEY\" \\\n  -H \"Idempotency-Key: booking-81723\" \\\n  -H \"content-type: application\u002Fjson\" \\\n  -d '{ \"accountId\": \"'$ACCOUNT_ID'\", \"title\": \"Intro call\", \"startsAt\": \"2026-10-21T15:00:00Z\", \"endsAt\": \"2026-10-21T15:30:00Z\" }'\n","bash","",[76,88,89,112,124,134,144],{"__ignoreMap":86},[90,91,94,98,102,105,108],"span",{"class":92,"line":93},"line",1,[90,95,97],{"class":96},"s1IGz","curl",[90,99,101],{"class":100},"sTB4W"," -X",[90,103,104],{"class":100}," POST",[90,106,107],{"class":100}," https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fevents",[90,109,111],{"class":110},"sqSWz"," \\\n",[90,113,115,118,122],{"class":92,"line":114},2,[90,116,117],{"class":100},"  -H",[90,119,121],{"class":120},"sk1Au"," \"x-api-key: $PYAS_API_KEY\"",[90,123,111],{"class":110},[90,125,127,129,132],{"class":92,"line":126},3,[90,128,117],{"class":100},[90,130,131],{"class":120}," \"Idempotency-Key: booking-81723\"",[90,133,111],{"class":110},[90,135,137,139,142],{"class":92,"line":136},4,[90,138,117],{"class":100},[90,140,141],{"class":120}," \"content-type: application\u002Fjson\"",[90,143,111],{"class":110},[90,145,147,150,153,156],{"class":92,"line":146},5,[90,148,149],{"class":100},"  -d",[90,151,152],{"class":120}," '{ \"accountId\": \"'",[90,154,155],{"class":110},"$ACCOUNT_ID",[90,157,158],{"class":120},"'\", \"title\": \"Intro call\", \"startsAt\": \"2026-10-21T15:00:00Z\", \"endsAt\": \"2026-10-21T15:30:00Z\" }'\n",[72,160,161,162,165,166,165,169,172,173,176,177,180],{},"The SDK does this for you on every ",[76,163,164],{},"POST",", ",[76,167,168],{},"PATCH",[76,170,171],{},"PUT"," and ",[76,174,175],{},"DELETE"," request. It generates a key and reuses it when it retries the request. To tie retries to your own records, pass your own key with the ",[76,178,179],{},"idempotencyKey"," option:",[81,182,186],{"className":183,"code":184,"language":185,"meta":86,"style":86},"language-js shiki shiki-themes css-variables","pyas.events.create(body, { idempotencyKey: 'booking-81723' })\n","js",[76,187,188],{"__ignoreMap":86},[90,189,190,194,197,200,203,206,210,213,217,220],{"class":92,"line":93},[90,191,193],{"class":192},"sXULy","pyas",[90,195,196],{"class":96},".",[90,198,199],{"class":192},"events",[90,201,202],{"class":96},".create",[90,204,205],{"class":110},"(body",[90,207,209],{"class":208},"sEEt9",",",[90,211,212],{"class":110}," { idempotencyKey",[90,214,216],{"class":215},"sag7P",":",[90,218,219],{"class":120}," 'booking-81723'",[90,221,222],{"class":110}," })\n",[224,225,52],"h2",{"id":51},[227,228,229,246,253,264,274,281],"ul",{},[230,231,232,233,165,235,165,237,239,240,242,243,245],"li",{},"Any ",[76,234,164],{},[76,236,168],{},[76,238,171],{}," or ",[76,241,175],{}," request accepts an ",[76,244,78],{}," header. A key can be up to 255 printable characters and must be unique for each operation you intend to perform. For example, you can use a booking id or a random UUID.",[230,247,248,249,252],{},"The API stores the response for 24 hours. If you send the same key with the same request, the API returns the stored response with an ",[76,250,251],{},"Idempotent-Replayed: true"," header.",[230,254,255,256,259,260,263],{},"If you send the same key with a different request, the API rejects the request with ",[76,257,258],{},"VALIDATION_IDEMPOTENCY_CONFLICT"," (",[76,261,262],{},"422 Unprocessable Entity","), because each key identifies a single operation.",[230,265,266,267,259,270,273],{},"If you send the same key while the first request is still in progress, the API rejects the request with ",[76,268,269],{},"RESOURCE_CONFLICT",[76,271,272],{},"409 Conflict","). Wait a moment, then retry.",[230,275,276,277,280],{},"The API doesn't store responses for failures that are worth retrying (5xx and ",[76,278,279],{},"429 Too Many Requests","), so a retry runs the request again.",[230,282,283],{},"Keys are scoped to your app, so a key sent by another app never conflicts with yours.",[224,285,55],{"id":54},[72,287,288,289,294,295,298],{},"Every error code indicates whether a retry can succeed. The ",[290,291,293],"a",{"href":292},"\u002Ferrors","error codes"," page lists the ",[76,296,297],{},"retryable"," value for each code. You can also get these values from the API:",[81,300,304],{"className":301,"code":302,"language":303,"meta":86,"style":86},"language-http shiki shiki-themes css-variables","GET \u002Fv2\u002Ferrors\n","http",[76,305,306],{"__ignoreMap":86},[90,307,308,311],{"class":92,"line":93},[90,309,310],{"class":215},"GET",[90,312,313],{"class":110}," \u002Fv2\u002Ferrors\n",[315,316,317,330],"table",{},[318,319,320],"thead",{},[321,322,323,327],"tr",{},[324,325,326],"th",{},"Response",[324,328,329],{},"Retry?",[331,332,333,352,369,380,388],"tbody",{},[321,334,335,345],{},[336,337,338,339,165,342],"td",{},"429 ",[76,340,341],{},"RATE_LIMIT_EXCEEDED",[76,343,344],{},"PROVIDER_RATE_LIMITED",[336,346,347,348,351],{},"Yes, after ",[76,349,350],{},"Retry-After"," seconds",[321,353,354,366],{},[336,355,356,357,165,360,165,363],{},"5xx, ",[76,358,359],{},"PROVIDER_TIMEOUT",[76,361,362],{},"PROVIDER_UNAVAILABLE",[76,364,365],{},"CONNECTION_TOKEN_EXPIRED",[336,367,368],{},"Yes, with backoff. Include an idempotency key for writes.",[321,370,371,377],{},[336,372,373,376],{},[76,374,375],{},"PROVIDER_UNKNOWN_ERROR"," (502)",[336,378,379],{},"Once. If it fails again, contact support with the trace id.",[321,381,382,385],{},[336,383,384],{},"No response",[336,386,387],{},"Yes, with the same idempotency key",[321,389,390,393],{},[336,391,392],{},"Any other 4xx",[336,394,395],{},"No. Change the request first. The error's hint explains how.",[72,397,398],{},"Increase the wait between attempts (1, 2, 4, 8 seconds, with some random jitter), and stop after a few attempts.",[224,400,58],{"id":57},[72,402,403,404,407,408,196],{},"Idempotency protects you from duplicates caused by your own retries. To avoid overwriting an update that someone else made, send the event's version in the ",[76,405,406],{},"If-Match"," header. See ",[290,409,58],{"href":410},"\u002Fevents#prevent-conflicting-updates",[412,413,414],"style",{},"html pre.shiki code .s1IGz, html code.shiki .s1IGz{--shiki-default:var(--shiki-token-function)}html pre.shiki code .sTB4W, html code.shiki .sTB4W{--shiki-default:var(--shiki-token-string)}html pre.shiki code .sqSWz, html code.shiki .sqSWz{--shiki-default:var(--shiki-foreground)}html pre.shiki code .sk1Au, html code.shiki .sk1Au{--shiki-default:var(--shiki-token-string-expression)}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);}html pre.shiki code .sXULy, html code.shiki .sXULy{--shiki-default:var(--shiki-token-constant)}html pre.shiki code .sEEt9, html code.shiki .sEEt9{--shiki-default:var(--shiki-token-punctuation)}html pre.shiki code .sag7P, html code.shiki .sag7P{--shiki-default:var(--shiki-token-keyword)}",{"title":86,"searchDepth":126,"depth":126,"links":416},[417,418,419],{"id":51,"depth":114,"text":52},{"id":54,"depth":114,"text":55},{"id":57,"depth":114,"text":58},"md",false,{},true,"\u002Fidempotency","---\ntitle: Retries and idempotency\ndescription: Retry any request safely with an Idempotency-Key, learn which errors are worth retrying, and avoid creating an event twice.\n---\n\n# Retries and idempotency\n\n::lead\nIf a network error occurs, you might not receive a response. For example, if a create request times out, you can't tell whether the resource was created. To retry safely, send an `Idempotency-Key` header and retry with the same key. The API returns the original response and doesn't create anything twice.\n::\n\n```bash\ncurl -X POST https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fevents \\\n  -H \"x-api-key: $PYAS_API_KEY\" \\\n  -H \"Idempotency-Key: booking-81723\" \\\n  -H \"content-type: application\u002Fjson\" \\\n  -d '{ \"accountId\": \"'$ACCOUNT_ID'\", \"title\": \"Intro call\", \"startsAt\": \"2026-10-21T15:00:00Z\", \"endsAt\": \"2026-10-21T15:30:00Z\" }'\n```\n\nThe SDK does this for you on every `POST`, `PATCH`, `PUT` and `DELETE` request. It generates a key and reuses it when it retries the request. To tie retries to your own records, pass your own key with the `idempotencyKey` option:\n\n```js\npyas.events.create(body, { idempotencyKey: 'booking-81723' })\n```\n\n## How it works\n\n- Any `POST`, `PATCH`, `PUT` or `DELETE` request accepts an `Idempotency-Key` header. A key can be up to 255 printable characters and must be unique for each operation you intend to perform. For example, you can use a booking id or a random UUID.\n- The API stores the response for 24 hours. If you send the same key with the same request, the API returns the stored response with an `Idempotent-Replayed: true` header.\n- If you send the same key with a different request, the API rejects the request with `VALIDATION_IDEMPOTENCY_CONFLICT` (`422 Unprocessable Entity`), because each key identifies a single operation.\n- If you send the same key while the first request is still in progress, the API rejects the request with `RESOURCE_CONFLICT` (`409 Conflict`). Wait a moment, then retry.\n- The API doesn't store responses for failures that are worth retrying (5xx and `429 Too Many Requests`), so a retry runs the request again.\n- Keys are scoped to your app, so a key sent by another app never conflicts with yours.\n\n## What to retry\n\nEvery error code indicates whether a retry can succeed. The [error codes](\u002Ferrors) page lists the `retryable` value for each code. You can also get these values from the API:\n\n```http\nGET \u002Fv2\u002Ferrors\n```\n\n| Response | Retry? |\n|---|---|\n| 429 `RATE_LIMIT_EXCEEDED`, `PROVIDER_RATE_LIMITED` | Yes, after `Retry-After` seconds |\n| 5xx, `PROVIDER_TIMEOUT`, `PROVIDER_UNAVAILABLE`, `CONNECTION_TOKEN_EXPIRED` | Yes, with backoff. Include an idempotency key for writes. |\n| `PROVIDER_UNKNOWN_ERROR` (502) | Once. If it fails again, contact support with the trace id. |\n| No response | Yes, with the same idempotency key |\n| Any other 4xx | No. Change the request first. The error's hint explains how. |\n\nIncrease the wait between attempts (1, 2, 4, 8 seconds, with some random jitter), and stop after a few attempts.\n\n## Prevent conflicting updates\n\nIdempotency protects you from duplicates caused by your own retries. To avoid overwriting an update that someone else made, send the event's version in the `If-Match` header. See [Prevent conflicting updates](\u002Fevents#prevent-conflicting-updates).\n",null,{"title":47,"description":48},"idempotency","IkNu15U3PeaX6p9IzLLbEdSkiQ_Ypyg45tc0RuqCFpc",1794697271358]