Przejdź do treści

Idempotentność

Jak to działa

Wybierz unikalny klucz na operację (UUID albo własny identyfikator, np. numer zamówienia) i wyślij go jako Idempotency-Key. Przez 24 godz. ten sam klucz na tej samej trasie ponownie zwraca pierwszy wynik, z Idempotent-Replayed: true, zamiast wykonywać pracę od nowa.

  • Ten sam klucz z inną treścią odpowiada 422 idempotency_conflict.
  • Ten sam klucz, gdy pierwsze żądanie jeszcze trwa, odpowiada 409 conflict: poczekaj i spróbuj ponownie.
  • Żądanie, które się nie powiodło, nie jest zapisywane, więc ponowienie z tym samym kluczem wykonuje pracę.
  • Klucze są ograniczone do obszaru roboczego, wywołującego, trasy i jej ścieżki. Wywołującym jest klucz API (lub, w aplikacji, zalogowany członek): dwa klucze API wysyłające ten sam Idempotency-Key nigdy nie odtwarzają sobie nawzajem odpowiedzi, a obrócony klucz API to nowy wywołujący. Ten sam klucz na trasach dwóch różnych kodów też nie koliduje.
  • PATCH i DELETE nie przyjmują klucza: ich powtórzenie zostawia ten sam stan.

Powtórzenia nigdy nie niosą sekretu

Zapisana odpowiedź nigdy nie zawiera sekretu. POST, który go zwraca (rejestracja endpointu webhooka oraz tworzenie lub rotacja klucza API w aplikacji), niesie sekret tylko w pierwszej odpowiedzi, z secretAlreadyShown: false. Powtórzenie w ciągu 24 godz. odpowiada tym samym obiektem z secret: null i secretAlreadyShown: true.

Jeśli pierwsza odpowiedź zaginęła, obiekt nadal istnieje (jego identyfikator jest w powtórzeniu). Obróć sekret endpointu w aplikacji (albo usuń endpoint i zarejestruj go ponownie) lub obróć klucz API, aby dostać nowy sekret.

Powtórzenie POST /v1/webhooks (fragment)
{  "id": "whe_3k8m2q9x7c4v1b6n",  "url": "https://hooks.example.com/dotwick",  "secret": null,  "secretAlreadyShown": true}

Bezpieczne ponawianie

przykładPOST /v1/codes
import { randomUUID } from "node:crypto";// one key per logical operation, reused on every retry of itconst idempotencyKey = randomUUID();for (let attempt = 1; attempt <= 5; attempt++) {  const res = await fetch("https://api.dotwick.com/v1/codes", {    method: "POST",    headers: {      Authorization: `Bearer ${process.env.DOTWICK_API_KEY}`,      "Content-Type": "application/json",      "Idempotency-Key": idempotencyKey,    },    body: JSON.stringify({ name: "Spring menu", content: { type: "url", url: "https://example.com/menu" } }),  });  if (res.ok) break; // a replay answers the first result with Idempotent-Replayed: true  if (res.status !== 429 && res.status < 500) throw new Error((await res.json()).error.message);  const wait = Number(res.headers.get("Retry-After") ?? 2 ** attempt);  await new Promise((r) => setTimeout(r, wait * 1000));}

Trasy przyjmujące klucz

Każdy POST oprócz wgrywania multipart POST /v1/assets.

MetodaŚcieżkaZakres (scope)Cel
POST/v1/codescodes:writeUtwórz kodidempotentne
POST/v1/codes/movecodes:writePrzenieś kody do folderuidempotentne
POST/v1/codes/move-domaincodes:writePrzenieś kody do innej domenyidempotentne
POST/v1/codes/tagscodes:writeDodaj lub usuń tagi w wielu kodachidempotentne
POST/v1/codes/{id}/duplicatecodes:writeZduplikuj kodidempotentne
POST/v1/codes/bulkcodes:writeUtwórz do 500 kodów w jednym zadaniuidempotentne
POST/v1/bulk-imports/{id}/cancelcodes:writeAnuluj zadanie w kolejce lub w tokuidempotentne
POST/v1/bulk-imports/{id}/retrycodes:writePonów nieudane wiersze zadaniaidempotentne
POST/v1/analytics/exportsanalytics:readSkany jako CSV, przygotowywane w tleidempotentne
POST/v1/reportsanalytics:readWygeneruj raport statystyk w PDF lub CSVidempotentne
POST/v1/exportscodes:readWyeksportuj wiele kodów jako jeden ZIPidempotentne
POST/v1/tagstags:writeUtwórz tagidempotentne
POST/v1/foldersfolders:writeUtwórz folderidempotentne
POST/v1/templatestemplates:writeUtwórz szablonidempotentne
POST/v1/webhookswebhooks:writeZarejestruj endpoint i typy zdarzeńidempotentne
POST/v1/webhooks/{id}/testwebhooks:writeWyślij zdarzenie webhook.testidempotentne
POST/v1/webhooks/{id}/deliveries/{deliveryId}/redeliverwebhooks:writeWyślij dostarczenie ponownieidempotentne
POST/v1/webhooks/{id}/recoverwebhooks:writeWyślij ponownie nieudane dostarczeniaidempotentne
POST/v1/hookswebhooks:writeZapisz hook na jedno zdarzenieidempotentne
POST/v1/cardscards:writeUtwórz wizytówkęidempotentne
POST/v1/cards/{id}/deactivatecards:writeDezaktywuj wizytówkęidempotentne
POST/v1/conversionsconversions:writeZapisz konwersjęidempotentne
POST/v1/nfc/tagscodes:writeZarejestruj tag NFC w kodzieidempotentne