Integrations
Twinfield, e-Boekhouden, SnelStart: 14 REST-quirks
Een Hilversums mediabureau van 22 koppen, drie boekhoud-API's, één stapel bonnetjes en veertien quirks die we hadden willen kennen voor we de eerste regel code schreven.

Dinsdag in mei, vierde verdieping van een Hilversums mediabureau, tweeëntwintig koppen op de loonlijst, één CFO met een Trello-kaart met de titel bonnetjes mei (urgent) die al sinds maart open stond. Ze had twee parttimers die bonnetjes scanden, drie administraties in Twinfield omdat de holding in 2024 twee productiebedrijven kocht, een SnelStart-licentie voor de verhuur-BV die niemand ooit had gemigreerd, en één freelance boekhouder die e-Boekhouden nooit meer wilde aanraken. Wij waren er om de agent te bouwen die een foto van een taxibonnetje pakt en die in het juiste grootboek zet, met de juiste BTW-code en de juiste kostenplaats, binnen negen seconden.
Dat werkte. Wat niet werkte, niet bij de eerste en niet bij de tweede poging, was de drie boekhoud-API's het eens laten worden over wat een getal is.
Dit is de cheatsheet van veertien REST-quirks die we tegenkwamen, ruwweg gerangschikt op hoeveel geld ze je stilletjes kunnen kosten voordat iemand het doorheeft. Twee kosten echte euro's. Vijf vreten elk een ontwikkeldag. De resterende zeven kosten alleen je waardigheid.
De twee die de cijfers veranderen
1. Twinfield rondt half-to-even op regeltotalen die op .x5 eindigen
De REST-laag van Twinfield (de nieuwe, niet de SOAP-host waar je misschien nog op zit) berekent BTW per regel op de server als je het zelf niet meestuurt. Eindigt het brutototaal van een regel op een halve cent, dan past hij bankiersafronding toe ("round half to even") in plaats van commerciële afronding. Op een regel van €12,345 bruto bij 21% rondt de netto af op €10,20, niet €10,21. Bij 800 bonnetjes per maand kwam de BTW-aangifte van de holding €4,30 lager uit dan de handmatig getypte som. De accountant merkte het op.
De fix is om BTW aan jouw kant uit te rekenen en de afgeronde bedragen expliciet per vatLine mee te sturen, in plaats van het de server te laten afleiden. Twinfield accepteert wat je stuurt zolang de regeltotalen kloppen tegen het documenttotaal.
// Don't trust the server to round for you.
// Commercial (half-up) rounding to whole cents.
function computeLine(grossCents: number, rate: number) {
const netto = Math.round(grossCents / (1 + rate));
const btw = grossCents - netto;
return { netto, btw };
}
// Then POST netto AND btw explicitly, do not let Twinfield re-derive.
const { netto, btw } = computeLine(1235, 0.21);
await twinfield.postInvoiceLine({ netto, btw, vatCode: 'VH' });
2. SnelStart geeft 200 OK terwijl kostenplaats verloren gaat bij multi-administratie POSTs
Deze ging op dag drie naar staging en we zagen het pas toen een van de parttimers terloops vroeg waarom alle mei-uitgaven voor "Studio Bussum" onder "Hoofdkantoor" in het dashboard verschenen. Het antwoord: het inkoopboekingen-endpoint van SnelStart accepteert een kostenplaatsId-veld op de regel, maar als het administratie-token in je bearer-header niet matcht met de administratie waar de kostenplaats bij hoort, logt de server de boeking, geeft een 200 terug en dropt de kostenplaats-referentie stilletjes. Geen validatiefout in de body. Geen waarschuwingsheader.
De fix is om het administratie-scoped token voor elke POST te verversen, ook als het token nog een half uur te gaan heeft. De TTL van het token is prima. Wat veroudert is de administration claim van het token, op het moment dat de gebruiker in de SnelStart-UI van administratie wisselt.
Vertrouw nooit een 200 van een multi-administratie boekhoud-API. Lees de response body. Komt het veld dat je stuurde niet terug, dan heeft de server het stilletjes opgevreten.
De vijf die elk een ontwikkeldag opvreten
Deze veranderen geen enkel cijfer op een balans. Ze slokken een werkdag van je duurste engineer op zodra je er voor het eerst tegenaan loopt, en ze doen het opnieuw als het team rouleert.
3. Cluster discovery van Twinfield's OAuth
Na de OAuth-dans weet je nog niet welk cluster (sb1, sb2, eu1, enzovoort) de data van de klant host. Je moet /accesstokens tegen de gateway aanroepen, de twf.clusterUrl-claim uit de JWT lezen, en die als base URL gebruiken voor elke volgende request. De Twinfield webservices docs behandelen het, maar het SDK-voorbeeld niet, dus de helft van de open-source wrappers op GitHub gaat uit van api.accounting.twinfield.com en breekt bij klanten in de oudere clusters. Decode de JWT één keer, cache de cluster URL naast het refresh token, ga nergens van uit.
4. e-Boekhouden REST versus SOAP sessiemodel
e-Boekhouden heeft twee API's. De verouderde SOAP-variant gebruikt OpenSession met een security code en een API-token, en je moet CloseSession aanroepen of je brandt je dagelijkse sessiequota in één middag op. De nieuwere REST API gebruikt één kortlopend session token van /v1/session, maar de sessie is gebonden aan het IP dat hem aanvroeg. Draait je agent op een serverless platform met wisselende egress-IP's, dan maakt elke cold start de sessie ongeldig en geeft de volgende call een generieke 401 terug, zonder hint dat het IP is gewijzigd. Pin de egress via een statische NAT, of accepteer dat je onder load ongeveer elke minuut opnieuw moet authenticeren.
5. De subscription key van SnelStart is niet de API-key
SnelStart wil twee headers: een Ocp-Apim-Subscription-Key uit het developer portal, en een Bearer-token uit de OAuth-flow. De portal-key ziet eruit als een API-key, heeft geen expiry en is genoeg om een 401 te krijgen met een misleidende boodschap ("Token invalid") die een nieuwe integrator een halve dag de verkeerde kant op stuurt. Die 401 betekent: je bearer ontbreekt of is verlopen. De subscription key is alleen de rate-limit envelope. Wij hebben de onze in onze secret store SNELSTART_RATE_KEY genoemd, om de verwarring bij de bron weg te halen.
6. Datumformaten: drie API's, drie antwoorden
Twinfield wil YYYYMMDD op sommige endpoints en YYYY-MM-DD op andere. SnelStart wil ISO 8601 met tijdzone en weigert naive dates. De REST-endpoints van e-Boekhouden willen YYYY-MM-DD, maar de SOAP-endpoints willen dd-MM-yyyy. Wij schreven één toBoekhoudDate(api, field, d)-helper, hielden die als enige plek waar datums de agent verlaten, en hadden na week één nooit meer een datumbug. Jij doet het waarschijnlijk niet. Je zou het wel moeten doen.
7. BTW-codes zijn case-sensitive in twee van de drie
De REST API van e-Boekhouden behandelt VH en vh als dezelfde code. Twinfield weigert lowercase stilletjes (de boeking landt, de BTW-code is leeg, de accountant ontdekt het drie maanden later). SnelStart geeft een nette 400 terug. Bepaal de casing aan de rand van je systeem, normaliseer één keer, en raak het daarna nooit meer aan.
De zeven die alleen je waardigheid kosten
Geen van deze houdt je 's nachts wakker. Ze duiken allemaal op vrijdagmiddag op, twee minuten voor een demo, en overtuigen je dertig seconden lang dat de hele stack stuk is.
8. Paginering: cursor op de nieuwe endpoints, offset op de oude
De REST search endpoints van Twinfield gebruiken cursorpaginering met een continuationToken. Het verouderde XML browse-endpoint gebruikt offset en limit. Bouw je een generieke iterator, schrijf dan twee implementaties en een discriminator die per endpoint kiest.
9. Nergens idempotency keys
Geen van de drie accepteert een Idempotency-Key-header per juni 2026. Een opnieuw verstuurde POST op een wankele verbinding maakt een dubbele boeking, en je boekhouder merkt het zo ongeveer nooit op. Bouw je eigen idempotency: sla een hash van (administration, document_number, line_count, total_cents) op in je eigen database voor je POST, en weiger opnieuw te POSTen als de hash al bestaat. Stripe heeft het patroon populair gemaakt; de boekhoud-API's misten de memo.
10. Limieten op bijlagegroottes zijn niet gedocumenteerd
Twinfield: ruwweg 10 MB per bijlage. SnelStart: 8 MB. e-Boekhouden REST: 5 MB. Daarboven voltooit de upload, geeft de response 200 terug, en is de bijlage stilletjes afgekapt of weg. Comprimeer bonnetjes (JPEG kwaliteit 80, max 2000 pixels aan de lange zijde) voor je uploadt. De OCR verderop werkt sowieso beter op kleinere bestanden.
11. Twinfield session token versus OAuth token
De verouderde SOAP-host gebruikt een session token dat door SessionService wordt teruggegeven. De REST-host gebruikt direct het OAuth access token. Op enig moment plak je de verkeerde in de verkeerde header en staar je veertig minuten naar "Invalid session", terwijl je collega zegt dat je de cache moet legen. Label ze duidelijk in je secret store. Maak de variabelenamen lelijk.
12. Webhook signatures op e-Boekhouden zijn SHA-1
De webhook signature is HMAC-SHA1 met het gedeelde secret. Werkt prima, maar als je handler standaard SHA-256 doet (een verstandige default in 2026), wijs je stilletjes elk event af en denk je dat de webhook aan hun kant stuk is. Lees RFC 2104 nog eens als je er even uit bent, en zet het digest-algoritme dan expliciet vast.
13. De administratielijst van SnelStart is niet stabiel
De volgorde waarin administraties terugkomen uit /v2/administraties is niet stabiel tussen calls. Indexeer niet op positie. Match op GUID. Wij hebben deze twee weken als bug genoteerd voordat we accepteerden dat het de spec was.
14. De office code van Twinfield is impliciet op sommige endpoints
Verschillende REST-endpoints van Twinfield nemen de office (administratie) code als path parameter. Andere halen hem uit de OAuth scope. Heeft je token toegang tot precies één office, dan is de path parameter optioneel en wordt hij genegeerd als je hem meestuurt. Heeft hij toegang tot meerdere, dan is de path parameter verplicht en geeft de request zonder hem een 400. Stuur hem altijd expliciet mee, en je code werkt in beide gevallen.
De adaptervorm die alle drie overleeft
Wij schreven een dunne adapter per API (rond de 600 regels TypeScript per stuk), elk met dezelfde zes methods: listAdministraties, postInkoopboeking, attachReceipt, getBtwCodes, getKostenplaatsen, healthCheck. De agent-code (het deel dat een foto van een bonnetje pakt, OCR draait, de leverancier classificeert en de juiste BTW-code kiest) praat alleen met de adapter-interface. Quirks leven achter de adapterlijn. Komt e-Boekhouden volgend jaar met een v2, dan veranderen wij één bestand.
Het andere wat wij deden, en jij ook zou moeten doen, is elke uitgaande request en zijn response in een kleine Postgres-tabel opslaan. Niet voor applicatielogging (jouw APM heeft dat al). Voor replay. Vraagt de accountant waarom een boeking €0,01 minder BTW heeft dan haar spreadsheet, dan heb je de exacte payload die je stuurde en de exacte payload die terugkwam, en kun je binnen twintig seconden laten zien dat het verschil de afronding van de server is, niet die van jou. De tabel kostte ons vier uur om toe te voegen en heeft die ongeveer een keer per week terugverdiend.
De vijfminuten-audit die je vandaag kunt draaien
Open een recente POST van je boekhoudintegratie in je logs. Kijk naar de response body. Pak elk veld dat je in de request stuurde, en controleer of hetzelfde veld terugkomt met dezelfde waarde. Is kostenplaatsId, btwCode of relatieId leeg of weg in de response, dan heeft de server het onderweg opgegeten. Schrijf een test die controleert dat elk veld round-tript op elk endpoint waar je naartoe POST. Die ene test vangt quirk nummer twee voor je af, en nog drie andere er bovenop.
Toen wij de bonnetjes-agent bouwden voor het Hilversumse mediabureau, was wat ons verraste niet de OCR of de leveranciersclassificatie, maar hoeveel van het werk eruit bestond om drie boekhoud-API's het eens te laten worden. Dat is het deel van AI-agents waar niemand de blogpost over schrijft. Daarom doen wij het.
Kern
Lees altijd de response body. Komt een veld dat je stuurde niet terug, dan heeft een Nederlandse boekhoud-API het stilletjes opgevreten en betekent de 200 OK niets.
FAQ
Welke Nederlandse boekhoud-API integreert het schoonst?
De REST van SnelStart is het modernst, maar het slechtst gedocumenteerd. Twinfield is het meest grondig, maar cluster discovery en een dubbel API-oppervlak maken de start traag. e-Boekhouden is het goedkoopst en voor een bedrijf met één administratie ruim voldoende.
Waarom rondt Twinfield BTW anders af dan mijn spreadsheet?
De REST-laag van Twinfield gebruikt bankiersafronding (half-to-even) op BTW per regel als de server de bedragen afleidt. Reken BTW vooraf uit met commerciële afronding (half-up) aan jouw kant en POST de waarden expliciet via vatLine.
Kan één wrapper-library alle drie de API's in productie aan?
Per medio 2026 geen die wij vertrouwen. Schrijf een dunne adapter per API en een gedeelde interface in je eigen code. Zes methods dekken de meeste onkosten- en inkoop-workflows, en de adapterlijn is waar elke quirk wordt opgevangen.
Hoe voorkom ik dubbele boekingen als een POST opnieuw probeert?
Geen van de drie API's accepteert een Idempotency-Key-header. Hash administratie plus documentnummer plus regelaantal plus totaal in centen in je eigen database voor elke POST, en weiger opnieuw te POSTen als de hash al bestaat.