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.
{ "id": "whe_3k8m2q9x7c4v1b6n", "url": "https://hooks.example.com/dotwick", "secret": null, "secretAlreadyShown": true}Bezpieczne ponawianie
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żka | Zakres (scope) | Cel |
|---|---|---|---|
| POST | /v1/codes | codes:write | Utwórz kodidempotentne |
| POST | /v1/codes/move | codes:write | Przenieś kody do folderuidempotentne |
| POST | /v1/codes/move-domain | codes:write | Przenieś kody do innej domenyidempotentne |
| POST | /v1/codes/tags | codes:write | Dodaj lub usuń tagi w wielu kodachidempotentne |
| POST | /v1/codes/{id}/duplicate | codes:write | Zduplikuj kodidempotentne |
| POST | /v1/codes/bulk | codes:write | Utwórz do 500 kodów w jednym zadaniuidempotentne |
| POST | /v1/bulk-imports/{id}/cancel | codes:write | Anuluj zadanie w kolejce lub w tokuidempotentne |
| POST | /v1/bulk-imports/{id}/retry | codes:write | Ponów nieudane wiersze zadaniaidempotentne |
| POST | /v1/analytics/exports | analytics:read | Skany jako CSV, przygotowywane w tleidempotentne |
| POST | /v1/reports | analytics:read | Wygeneruj raport statystyk w PDF lub CSVidempotentne |
| POST | /v1/exports | codes:read | Wyeksportuj wiele kodów jako jeden ZIPidempotentne |
| POST | /v1/tags | tags:write | Utwórz tagidempotentne |
| POST | /v1/folders | folders:write | Utwórz folderidempotentne |
| POST | /v1/templates | templates:write | Utwórz szablonidempotentne |
| POST | /v1/webhooks | webhooks:write | Zarejestruj endpoint i typy zdarzeńidempotentne |
| POST | /v1/webhooks/{id}/test | webhooks:write | Wyślij zdarzenie webhook.testidempotentne |
| POST | /v1/webhooks/{id}/deliveries/{deliveryId}/redeliver | webhooks:write | Wyślij dostarczenie ponownieidempotentne |
| POST | /v1/webhooks/{id}/recover | webhooks:write | Wyślij ponownie nieudane dostarczeniaidempotentne |
| POST | /v1/hooks | webhooks:write | Zapisz hook na jedno zdarzenieidempotentne |
| POST | /v1/cards | cards:write | Utwórz wizytówkęidempotentne |
| POST | /v1/cards/{id}/deactivate | cards:write | Dezaktywuj wizytówkęidempotentne |
| POST | /v1/conversions | conversions:write | Zapisz konwersjęidempotentne |
| POST | /v1/nfc/tags | codes:write | Zarejestruj tag NFC w kodzieidempotentne |