Idempotentie
Hoe het werkt
Kies een unieke sleutel per bewerking (een UUID, of je eigen id zoals een bestelnummer) en stuur hem mee als Idempotency-Key. 24 uur lang geeft dezelfde sleutel op dezelfde route opnieuw het eerste resultaat terug, met Idempotent-Replayed: true, in plaats van het werk opnieuw te doen.
- Dezelfde sleutel met een andere body antwoordt 422 idempotency_conflict.
- Dezelfde sleutel terwijl het eerste verzoek nog loopt antwoordt 409 conflict: wacht en probeer opnieuw.
- Een mislukt verzoek wordt niet opgeslagen, dus opnieuw proberen met dezelfde sleutel doet het werk.
- Sleutels gelden voor de werkruimte, de aanroeper, de route en zijn pad. De aanroeper is de API-sleutel (of, in de app, het ingelogde lid): twee API-sleutels die dezelfde Idempotency-Key sturen, spelen elkaars antwoorden nooit af, en een geroteerde API-sleutel is een nieuwe aanroeper. Dezelfde sleutel op de routes van twee verschillende codes botst ook niet.
- PATCH en DELETE nemen geen sleutel aan: herhalen laat dezelfde toestand achter.
Herhalingen bevatten nooit een geheim
Het opgeslagen antwoord bevat nooit een geheim. Een POST die er een teruggeeft (een webhook-endpoint registreren, en een API-sleutel maken of roteren in de app) draagt het geheim alleen in het eerste antwoord, met secretAlreadyShown: false. Een herhaling binnen de 24 uur antwoordt hetzelfde object met secret: null en secretAlreadyShown: true.
Is het eerste antwoord verloren gegaan, dan bestaat het object nog (zijn id staat in de herhaling). Roteer het geheim van het endpoint in de app (of verwijder het endpoint en registreer het opnieuw), of roteer de API-sleutel, om een nieuw geheim te krijgen.
{ "id": "whe_3k8m2q9x7c4v1b6n", "url": "https://hooks.example.com/dotwick", "secret": null, "secretAlreadyShown": true}Veilig opnieuw proberen
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));}Routes die een sleutel aannemen
Elke POST behalve de multipart-upload POST /v1/assets.
| Methode | Pad | Scope | Doel |
|---|---|---|---|
| POST | /v1/codes | codes:write | Een code aanmakenidempotent |
| POST | /v1/codes/move | codes:write | Codes naar een map verplaatsenidempotent |
| POST | /v1/codes/move-domain | codes:write | Codes naar een ander domein verplaatsenidempotent |
| POST | /v1/codes/tags | codes:write | Tags toevoegen of verwijderen bij veel codesidempotent |
| POST | /v1/codes/{id}/duplicate | codes:write | Een code duplicerenidempotent |
| POST | /v1/codes/bulk | codes:write | Tot 500 codes aanmaken als één runidempotent |
| POST | /v1/bulk-imports/{id}/cancel | codes:write | Een run in de wachtrij of lopende run annulerenidempotent |
| POST | /v1/bulk-imports/{id}/retry | codes:write | De mislukte rijen van een run opnieuw proberenidempotent |
| POST | /v1/analytics/exports | analytics:read | Scans als CSV, op de achtergrond voorbereididempotent |
| POST | /v1/reports | analytics:read | Een PDF- of CSV-analyserapport genererenidempotent |
| POST | /v1/exports | codes:read | Veel codes exporteren als één ZIPidempotent |
| POST | /v1/tags | tags:write | Een tag aanmakenidempotent |
| POST | /v1/folders | folders:write | Een map aanmakenidempotent |
| POST | /v1/templates | templates:write | Een sjabloon aanmakenidempotent |
| POST | /v1/webhooks | webhooks:write | Een endpoint en eventtypen registrerenidempotent |
| POST | /v1/webhooks/{id}/test | webhooks:write | Een webhook.test-event sturenidempotent |
| POST | /v1/webhooks/{id}/deliveries/{deliveryId}/redeliver | webhooks:write | Een levering opnieuw sturenidempotent |
| POST | /v1/webhooks/{id}/recover | webhooks:write | Mislukte leveringen opnieuw sturenidempotent |
| POST | /v1/hooks | webhooks:write | Een hook abonneren op één eventidempotent |
| POST | /v1/cards | cards:write | Een visitekaartje aanmakenidempotent |
| POST | /v1/cards/{id}/deactivate | cards:write | Een visitekaartje deactiverenidempotent |
| POST | /v1/conversions | conversions:write | Een conversie registrerenidempotent |
| POST | /v1/nfc/tags | codes:write | Een NFC-tag registreren op een codeidempotent |