Nordlet

← Documentatie / Woordenlijst

Wat is idempotentie? Waarom boekhoud- en betalings-API's dit nodig hebben

Hoe een herhaald verzoek een dubbele factuur creëert, wat een idempotentiesleutel garandeert, welke foutscenario's deze moet opvangen en hoe je dit correct implementeert.

Een bewerking is idempotent als het tweemaal uitvoeren ervan hetzelfde effect heeft als eenmalig uitvoeren. Het uitlezen van een record is van nature idempotent. Het aanmaken van een factuur is dat niet: roep dit tweemaal aan en je hebt twee facturen, twee vorderingen en een klant die vraagt waarom er dubbel is gefactureerd.

Dat verschil is cruciaal omdat netwerken op een specifieke, vervelende manier falen: het verzoek slaagt, maar het antwoord gaat verloren. De klant ziet een time-out en kan geen onderscheid maken tussen "nooit aangekomen" en "aangekomen, verwerkt, antwoord verloren". De enige veilige optie lijkt te zijn om het verzoek opnieuw te proberen, en juist die herhaling creëert het duplicaat.

Waarom de boekhouding dit erger maakt

In de meeste systemen is een duplicaat hoogstens hinderlijk. In een grootboek is het een opeenstapeling van problemen:

  • Een dubbele factuur verbruikt een nummer uit een gapless reeks, dus verwijderen is geen optie. Dankzij onveranderlijkheid vereist een correctie een creditnota, en dat is een document dat je klant te zien krijgt.
  • Het duplicaat boekt journaalposten, waardoor omzet, btw en debiteuren te hoog worden weergegeven totdat iemand ingrijpt.
  • Het komt in de btw-aangifte terecht en, in Litouwen, in de i.SAF-registers.
  • Als het een betaling triggerde, is er daadwerkelijk geld overgemaakt.

"Gewoon controleren op duplicaten" werkt evenmin, omdat twee volkomen losstaande facturen voor dezelfde klant met hetzelfde bedrag op dezelfde dag volkomen normaal zijn. Het systeem kan de intentie niet afleiden uit de inhoud: de klant moet die expliciet meegeven.

Hoe idempotentiesleutels werken

De klant genereert een unieke sleutel per logische bewerking en stuurt deze mee met het verzoek:

POST /v1/sales/invoices/create
Idempotency-Key: 8f14e45f-ea1b-4c2b-9f3a-2d7c1e0b5a91

De server garandeert vervolgens het volgende:

  1. Eerste verzoek met deze sleutel - verwerk het normaal en sla het resultaat op gekoppeld aan de sleutel.
  2. Herhaling met dezelfde sleutel en dezelfde payload - voer de bewerking niet opnieuw uit; retourneer het opgeslagen antwoord.
  3. Dezelfde sleutel, andere payload - wijs het verzoek af. De sleutel is een claim dat het om dezelfde bewerking gaat. Een andere body betekent dat dit niet zo is, en het stilletjes retourneren van het oude resultaat is erger dan een foutmelding.
  4. Herhaling terwijl de oorspronkelijke bewerking nog loopt - wijs het verzoek af met een foutmelding die aangeeft dat een nieuwe poging zinvol is, in plaats van de bewerking gelijktijdig uit te voeren.

Punt 4 is precies wat naieve implementaties over het hoofd zien. Een klantapplicatie die na 5 seconden een time-out krijgt en na 6 seconden opnieuw probeert, botst met zijn eigen verzoek dat nog in behandeling is. Zonder beveiliging tegen gelijktijdige uitvoering gaan beide verzoeken door en krijg je alsnog het duplicaat dat dit mechanisme juist moest voorkomen.

Sleutels correct genereren

De sleutel moet de bewerking identificeren, niet de poging:

  • Goed: een UUID die eenmalig wordt gegenereerd wanneer de gebruiker op "factuur uitreiken" klikt, en die wordt hergebruikt voor elke herhaling van die klik.
  • Goed: een deterministische sleutel afgeleid van je eigen domein: order-1041-invoice.
  • Fout: een nieuwe UUID per HTTP-poging. Elke herhaling krijgt een nieuwe sleutel, waardoor elke herhaling wordt gezien als een nieuwe bewerking en je in feite niets hebt geïmplementeerd.
  • Fout: een hash van de payload. Twee legitiem identieke bewerkingen botsen dan met elkaar, waarbij de tweede stilletjes wordt genegeerd.

Sleutels hebben een bewaartermijn nodig die lang genoeg is om realistische vensters voor herhalingen en uitval van de klant op te vangen, maar niet voor eeuwig.

Wat het niet oplost

Idempotentie beschermt tegen dubbele uitvoering van hetzelfde verzoek. Het biedt geen bescherming tegen:

  • Twee verschillende gebruikers die vanuit verschillende sessies dezelfde factuur aanmaken. Dat is een bedrijfsregelprobleem dat wordt opgelost door uniekheidsbeperkingen (uniciteitsrestricties), zoals één factuur per bestelling.
  • Logische duplicaten met verschillende sleutels.
  • Non-determinisme binnen de bewerking: als de handler een tijdstempel of een nummer genereert, legt het opgeslagen antwoord het eerste resultaat vast. Dat is het gewenste gedrag, maar het is goed om je hiervan bewust te zijn.

Het vormt een aanvulling op databasebeperkingen in plaats de vervanging ervan. Het grootboek van Nordlet gebruikt beide: idempotentiesleutels stoppen herhaalde verzoeken, terwijl unieke indexen dubbele afwikkelingsbatches per uitbetalings-ID, dubbele inkoopfacturen per leverancier en documentnummer, en dubbele afschrijvingsrondes per activum en periode voorkomen.

Hoe Nordlet dit implementeert

Stuur een Idempotency-Key header mee bij elk verzoek dat de status wijzigt: een niet-lege tekenreeks van maximaal 255 tekens.

Het eerste verzoek met een bepaalde sleutel wordt uitgevoerd en de uitkomst wordt opgeslagen. Een herhaling met dezelfde sleutel en dezelfde payload retourneert het opgeslagen antwoord zonder het opnieuw uit te voeren, zodat de factuur exact eenmalig wordt aangemaakt, ongeacht hoe vaak de aanroep wordt herhaald.

Twee foutscenario's leveren expliciete meldingen op in plaats van stilzwijgend gedrag:

  • Dezelfde sleutel, andere payload → afgewezen: "Idempotency-Key was already used with a different request payload." Dit vangt het gevaarlijke scenario op waarin een klant ten onrechte een sleutel hergebruikt maar het bedrag wijzigt.
  • Dezelfde sleutel, origineel nog in behandeling → afgewezen met het verzoek het later opnieuw te proberen: "A request with this Idempotency-Key is still being processed, retry shortly." Dit voorkomt dubbele uitvoering op hetzelfde moment.

Sleutels zijn gekoppeld per bedrijfsentiteit (tenant), zodat twee tenants die dezelfde UUID genereren elkaar nooit in de wielen rijden.

De complementaire garantie werkt de andere kant op. Webhook-levering werkt op basis van ten minste eenmalige levering (at-least-once), wat betekent dat je endpoint af en toe hetzelfde event tweemaal ontvangt: na een time-out waarbij de bezorging feitelijk wel is geslaagd, of bij een herlevering. Elk event heeft een ID, en je handler moet verwerkte ID's registreren en herhalingen negeren. Idempotentie is een eigenschap die beide kanten van een integratie nodig hebben; de gids over API-conventies behandelt de verzoekzijde in detail.

FAQ

Wat is een idempotentiesleutel?

Een door de klant gegenereerde identificatie die wordt meegestuurd met een verzoek, waarmee de server een herhaling van dezelfde logische bewerking herkent. Het eerste verzoek wordt uitgevoerd; herhalingen met dezelfde sleutel retourneren het opgeslagen resultaat in plaats van de bewerking opnieuw uit te voeren.

Waarom hebben boekhoud-API's idempotentie nodig?

Omdat een dubbele schrijfactie een echt document creëert met echte gevolgen: een tweede factuur die een volgnummer verbruikt, in het grootboek wordt geboekt en in de btw-aangifte terechtkomt. In tegenstelling tot een dubbele leesactie kun je dit niet zomaar verwijderen; het moet worden gecorrigeerd met een creditnota.

Moet ik voor elke herhaling een nieuwe sleutel genereren?

Nee, dat haalt het hele mechanisme onderuit. Genereer één sleutel per logische bewerking en hergebruik deze voor elke poging van die bewerking. Een nieuwe sleutel per poging betekent dat elke poging wordt behandeld als een nieuwe bewerking.

Wat gebeurt er als ik een sleutel hergebruik met een andere payload?

Het verzoek wordt afgewezen. De sleutel bevestigt dat het om dezelfde bewerking gaat, dus een gewijzigde body betekent dat er iets mis is, hoogstwaarschijnlijk een bug in het hergebruik van sleutels aan de kant van de klant. Het stilletjes retourneren van het eerdere resultaat zou erger zijn.

Voorkomt idempotentie alle duplicaten?

Nee. Het voorkomt dubbele uitvoering van hetzelfde verzoek. Twee verschillende gebruikers die onafhankelijk van elkaar dezelfde factuur aanmaken, hebben twee verschillende verzoeken gedaan. Dat is een bedrijfsregelprobleem dat vraagt om uniekheidsbeperkingen, en iets wat een idempotentiesleutel niet kan voorzien.