Zum Inhalt springen
Dokumentation
Deutsch
App öffnen

Idempotenz

Einen Schreibzugriff mit X-Client-Mutation-Id wiederholen, ohne ein Duplikat zu erzeugen, und eine Änderung mit der optimistischen Sperre X-Base-Version schützen.

Ein Netzwerk kann zwischen dem Senden Ihres Requests und dem Empfang der Antwort ausfallen. Sie wissen dann nicht, ob die Rechnung angelegt wurde. Die Idempotenz beantwortet diese Frage: Sie senden denselben Request mit demselben Schlüssel erneut, und NovaFisko garantiert, dass er nur ein einziges Mal ausgeführt wird.

Der Schlüssel X-Client-Mutation-Id

Fügen Sie den Header X-Client-Mutation-Id jedem schreibenden Request (POST, PUT, PATCH, DELETE) hinzu, der sich auf ein Mandat bezieht, also unter companies/{company} liegt.

curl -X POST https://api.novafisko.com/v1/companies/k3Jd9fPq2LmX/entries/invoice \
  -H "Authorization: Bearer $NOVAFISKO_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Client-Mutation-Id: 0b0f7a52-63f4-4f1f-8a43-0d4f6b0b8a10" \
  -d '{
    "journal_id": 3,
    "third_party_id": 41,
    "entry_date": "2026-10-02",
    "reference": "F-2026-0918",
    "lines": [{"account": "604000", "amount": "1250.00", "vat_code": "A21"}]
  }'

Regeln

Regel Einzelheiten
Format Freie Zeichenfolge mit 1 bis 64 Zeichen. Eine UUID Version 4 eignet sich bestens
Geltungsbereich Der Schlüssel ist pro Mandat eindeutig
Speicherung Nur erfolgreiche Antworten (Status 2xx) werden gespeichert
Betroffene Requests POST, PUT, PATCH, DELETE unter companies/{company}
Ausnahme sync/push verwaltet seine eigene Idempotenz, Mutation für Mutation

Ein leerer Schlüssel oder einer mit mehr als 64 Zeichen wird ignoriert: Der Request wird normal ausgeführt, ohne Schutz.

Erster Versand

Der Request wird ausgeführt. Ist er erfolgreich, speichert NovaFisko den Status und den Body der Antwort.

HTTP/1.1 201 Created
X-Operation-Id: 5d0f6a7e-2f0b-4a35-9b56-7f1f3c1f4c22

Wiederholung

Wenn Sie denselben Schlüssel für dasselbe Mandat erneut senden, wird nichts ausgeführt. Sie erhalten den gespeicherten Body mit dem Status 200 und einem Header, der die Wiederholung anzeigt.

HTTP/1.1 200 OK
X-Idempotent-Replay: 1
Content-Type: application/json
Achtung

Eine Wiederholung antwortet immer mit 200, auch wenn der ursprüngliche Request mit 201 oder 204 geantwortet hatte. Betrachten Sie jeden Status 2xx als Erfolg und prüfen Sie X-Idempotent-Replay, wenn Sie die beiden Fälle unterscheiden müssen.

Was nicht gespeichert wird

Ein fehlgeschlagener Request (4xx, 5xx) wird nicht gespeichert. Sie können die Daten korrigieren und denselben Schlüssel erneut senden oder es nach einem Fehler 500 oder 503 unverändert noch einmal versuchen.

Der gespeicherte Body ist auf 500 KB begrenzt. Darüber hinaus liefert die Wiederholung 200 mit einem Body null: Der Vorgang hat stattgefunden, lesen Sie die Ressource erneut, um ihren Zustand zu erhalten.

Den Schlüssel richtig wählen

  • Erzeugen Sie den Schlüssel vor dem ersten Versand und speichern Sie ihn zusammen mit dem Vorgang in Ihrem System.
  • Ein Schlüssel bezeichnet eine Absicht, keinen Versuch: Alle Versuche desselben Vorgangs teilen sich denselben Schlüssel.
  • Verwenden Sie einen Schlüssel niemals für einen anderen Vorgang wieder. Der Server vergleicht die Bodys nicht: Er würde das Ergebnis des ersten zurückgeben.

Ein deterministischer Schlüssel funktioniert sehr gut, wenn Ihr System bereits eine stabile Kennung besitzt:

// One key per business operation, stable across retries
$key = substr(hash('sha256', 'erp-atelier:purchase-invoice:'.$invoice->id), 0, 64);

Beispiel einer Wiederholungsschleife

import { randomUUID } from "node:crypto";

async function postOnce(url, body, token) {
  const mutationId = randomUUID(); // generated once, reused on every retry
  for (let attempt = 0; attempt < 5; attempt++) {
    try {
      const response = await fetch(url, {
        method: "POST",
        headers: {
          Authorization: `Bearer ${token}`,
          "Content-Type": "application/json",
          Accept: "application/json",
          "X-Client-Mutation-Id": mutationId,
        },
        body: JSON.stringify(body),
      });
      if (response.status === 429 || response.status >= 500) {
        const wait = Number(response.headers.get("Retry-After") ?? 2 ** attempt);
        await new Promise((resolve) => setTimeout(resolve, wait * 1000));
        continue;
      }
      return {
        replayed: response.headers.get("X-Idempotent-Replay") === "1",
        status: response.status,
        data: await response.json(),
      };
    } catch (networkError) {
      await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 1000));
    }
  }
  throw new Error("NovaFisko API unreachable");
}

Optimistische Sperre mit X-Base-Version

Die Idempotenz schützt vor Ihren eigenen Duplikaten. Die optimistische Sperre schützt vor den Änderungen der anderen: Sie verhindert, dass eine Änderung überschrieben wird, die zwischen Ihrem Lese- und Ihrem Schreibzugriff erfolgt ist.

Jede vom Verlauf erfasste Ressource stellt eine Ganzzahl version bereit. Senden Sie den gelesenen Wert im Header X-Base-Version eines PATCH, eines PUT oder eines DELETE.

curl -X PATCH https://api.novafisko.com/v1/companies/k3Jd9fPq2LmX/third-parties/41 \
  -H "Authorization: Bearer $NOVAFISKO_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Base-Version: 3" \
  -d '{"email": "compta@brasserie-collines.example"}'

Ist die Version des Servers weiterhin 3, wird die Änderung angewendet. Hat jemand den Geschäftspartner inzwischen geändert, bleibt alles unberührt und die API antwortet mit 409:

{
  "message": "Cet enregistrement a été modifié entre-temps.",
  "code": "conflict",
  "server_version": 5,
  "server_data": {
    "id": 41,
    "type": "supplier",
    "name": "Brasserie des Collines SA",
    "email": "facturation@brasserie-collines.example",
    "version": 5
  }
}

So lösen Sie den Konflikt: Vergleichen Sie server_data mit Ihren Werten, entscheiden Sie, was erhalten bleiben soll, und senden Sie den Request anschließend mit X-Base-Version: 5 erneut.

Punkt Verhalten
Aktivierung Nur wenn der Header vorhanden und numerisch ist
Methoden PATCH, PUT, DELETE. Bei POST ignoriert
Vergleich Konflikt, wenn die Version des Servers strikt größer ist als Ihre
Betroffene Ressource Der durch die URL bezeichnete Datensatz (niemals das Mandat selbst)
Hinweis

Die Versionsprüfung erfolgt vor der idempotenten Wiederholung. Kombinieren Sie beide Header unbesorgt: X-Base-Version schützt die Daten, X-Client-Mutation-Id schützt den Versuch.

Und bei Offline-Stapeln

POST sync/push wendet dieselben Grundsätze auf einen Stapel von Mutationen an: Jede Mutation trägt ihre client_mutation_id und, wenn Sie es wünschen, ihre base_version. Eine bereits verarbeitete Mutation kommt mit dem Status duplicate und ihrem ursprünglichen Ergebnis zurück. Konflikte werden dort Feld für Feld zusammengeführt, statt abgelehnt zu werden. Siehe Fortlaufend synchronisieren.

Siehe auch