Idempotenz
So funktioniert es
Wählen Sie pro Vorgang einen eindeutigen Schlüssel (eine UUID oder Ihre eigene ID wie eine Bestellnummer) und senden Sie ihn als Idempotency-Key. 24 Stunden lang beantwortet derselbe Schlüssel auf derselben Route erneut das erste Ergebnis, mit Idempotent-Replayed: true, statt die Arbeit noch einmal zu tun.
- Derselbe Schlüssel mit einem anderen Body wird mit 422 idempotency_conflict beantwortet.
- Derselbe Schlüssel, während die erste Anfrage noch läuft, wird mit 409 conflict beantwortet: Warten Sie und versuchen Sie es erneut.
- Eine fehlgeschlagene Anfrage wird nicht gespeichert, ein erneuter Versuch mit demselben Schlüssel führt die Arbeit also aus.
- Schlüssel gelten für Arbeitsbereich, Aufrufer, Route und ihren Pfad. Aufrufer ist der API-Schlüssel (oder in der App das angemeldete Mitglied): Zwei API-Schlüssel, die denselben Idempotency-Key senden, spielen sich nie gegenseitig Antworten zurück, und ein rotierter API-Schlüssel ist ein neuer Aufrufer. Derselbe Schlüssel auf den Routen zweier verschiedener Codes kollidiert ebenfalls nicht.
- PATCH und DELETE nehmen keinen Schlüssel: Sie zu wiederholen lässt denselben Zustand zurück.
Replays enthalten nie ein Secret
Die gespeicherte Antwort enthält nie ein Secret. Ein POST, der eines zurückgibt – einen Webhook-Endpunkt registrieren sowie einen API-Schlüssel in der App erstellen oder rotieren –, enthält das Secret nur in der ersten Antwort, mit secretAlreadyShown: false. Ein Replay innerhalb der 24 Stunden beantwortet dasselbe Objekt mit secret: null und secretAlreadyShown: true.
Ging die erste Antwort verloren, existiert das Objekt trotzdem (seine ID steht im Replay). Rotieren Sie das Secret des Endpunkts in der App (oder löschen Sie den Endpunkt und registrieren Sie ihn neu) oder rotieren Sie den API-Schlüssel, um ein neues Secret zu erhalten.
{ "id": "whe_3k8m2q9x7c4v1b6n", "url": "https://hooks.example.com/dotwick", "secret": null, "secretAlreadyShown": true}Sicher wiederholen
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));}Routen, die einen Schlüssel nehmen
Jeder POST außer dem Multipart-Upload POST /v1/assets.
| Methode | Pfad | Scope | Zweck |
|---|---|---|---|
| POST | /v1/codes | codes:write | Einen Code erstellenidempotent |
| POST | /v1/codes/move | codes:write | Codes in einen Ordner verschiebenidempotent |
| POST | /v1/codes/move-domain | codes:write | Codes auf eine andere Domain verschiebenidempotent |
| POST | /v1/codes/tags | codes:write | Tags bei mehreren Codes hinzufügen oder entfernenidempotent |
| POST | /v1/codes/{id}/duplicate | codes:write | Einen Code duplizierenidempotent |
| POST | /v1/codes/bulk | codes:write | Bis zu 500 Codes als einen Lauf erstellenidempotent |
| POST | /v1/bulk-imports/{id}/cancel | codes:write | Einen wartenden oder laufenden Lauf abbrechenidempotent |
| POST | /v1/bulk-imports/{id}/retry | codes:write | Die fehlgeschlagenen Zeilen eines Laufs wiederholenidempotent |
| POST | /v1/analytics/exports | analytics:read | Scans als CSV, im Hintergrund erstelltidempotent |
| POST | /v1/reports | analytics:read | Einen Statistikbericht als PDF oder CSV erstellenidempotent |
| POST | /v1/exports | codes:read | Viele Codes als ein ZIP exportierenidempotent |
| POST | /v1/tags | tags:write | Einen Tag erstellenidempotent |
| POST | /v1/folders | folders:write | Einen Ordner erstellenidempotent |
| POST | /v1/templates | templates:write | Eine Vorlage erstellenidempotent |
| POST | /v1/webhooks | webhooks:write | Einen Endpunkt und Ereignistypen registrierenidempotent |
| POST | /v1/webhooks/{id}/test | webhooks:write | Ein webhook.test-Ereignis sendenidempotent |
| POST | /v1/webhooks/{id}/deliveries/{deliveryId}/redeliver | webhooks:write | Eine Zustellung erneut sendenidempotent |
| POST | /v1/webhooks/{id}/recover | webhooks:write | Fehlgeschlagene Zustellungen erneut sendenidempotent |
| POST | /v1/hooks | webhooks:write | Einen Hook auf ein Ereignis abonnierenidempotent |
| POST | /v1/cards | cards:write | Eine Visitenkarte erstellenidempotent |
| POST | /v1/cards/{id}/deactivate | cards:write | Eine Visitenkarte deaktivierenidempotent |
| POST | /v1/conversions | conversions:write | Eine Conversion erfassenidempotent |
| POST | /v1/nfc/tags | codes:write | Einen NFC-Tag für einen Code registrierenidempotent |