Wat is een echte API-koppeling?
Een API-koppeling is veel meer dan gegevens van systeem A naar systeem B sturen. Datastructuren moeten vaak vertaald worden, systemen kunnen verschillende rollen hebben en er moet...
Wat is een échte API-koppeling?
“Er is een API, dus koppelen zal wel eenvoudig zijn.”
Die redenering hoor je vaak. En technisch gezien zit daar natuurlijk iets in. Als een pakket een degelijke API aanbiedt, heb je alvast een manier om gegevens uit te wisselen. Maar daarmee heb je nog geen goede koppeling.
Een API-call uitvoeren is meestal niet het moeilijke deel. De echte complexiteit zit in alles wat errond komt kijken: wanneer haal je gegevens op, welk systeem is leidend, hoe ga je om met fouten, wat gebeurt er wanneer dezelfde boodschap twee keer binnenkomt, en moet je gegevens eigenlijk wel altijd synchroniseren?
Daar komt nog iets bij wat in de praktijk bijna altijd meespeelt: de datastructuur van twee pakketten is zelden identiek. Een klant, artikel of bestelling kan in pakket A op een heel andere manier opgebouwd zijn dan in pakket B. Voor je gegevens kunt doorsturen, moet je dus bepalen hoe velden, codes, statussen en onderlinge relaties naar elkaar vertaald worden.
Soms is die omzetting vrij eenvoudig. In andere situaties zijn er tussentabellen nodig die bijvoorbeeld bijhouden dat code 10 uit pakket A overeenkomt met code KLANT in pakket B. Of dat artikel 12345 in het ene systeem gekend is als ART-001 in het andere. Zulke mappings zijn belangrijk, want je kunt niet verwachten dat verschillende softwarepakketten toevallig dezelfde interne sleutels en coderingen gebruiken.
En soms gaat een koppeling nog een stap verder. Soms moet een koppeling gegevens uit meerdere systemen combineren. Zo kan een klantenportaal basisgegevens en facturen uit het ERP ophalen, terwijl openstaande interventies of servicegegevens uit een andere toepassing komen. De koppeling brengt die gegevens samen in één consistente structuur.
Dat zijn de vragen en keuzes die uiteindelijk bepalen of twee of meerdere systemen gewoon technisch met elkaar verbonden zijn, of echt goed samenwerken.
Een API is een toegang tot een systeem
Heel eenvoudig gesteld laat een API toe dat software rechtstreeks met andere software communiceert. Bij een REST API gebeurt dat meestal via HTTP. Een toepassing kan bijvoorbeeld een klant opvragen, een bestelling aanmaken, voorraad controleren of een status aanpassen.
Een eenvoudige aanvraag kan er bijvoorbeeld zo uitzien:
GET /api/customers/1548
De API antwoordt vervolgens met de gegevens van die klant, vaak in JSON:
{
"id": 1548,
"name": "Voorbeeld NV",
"email": "info@voorbeeld.be",
"vatNumber": "BE0123456789"
}
Of je maakt via de API een bestelling aan:
POST /api/orders
Op zich is dat technisch vrij rechtlijnig.
Maar een API vertelt je alleen hoe je gegevens kunt lezen of schrijven. Ze vertelt je niet automatisch hoe jouw proces moet werken, welke gegevens je moet gebruiken of welke toepassing op welk moment verantwoordelijk is.
Daar begint het eigenlijke integratiewerk.
Een koppeling is meer dan data van A naar B sturen
Neem een webshop die bestellingen moet doorsturen naar een ERP-systeem.
De eerste technische vraag lijkt eenvoudig: kunnen we via de API een bestelling aanmaken?
Maar voor je die bestelling correct kunt verwerken, moet je veel meer beslissen.
- Welke klant hoort bij de bestelling?
- Bestaat die klant al in het ERP?
- Hoe herkennen we hem: via klantnummer, e-mailadres, btw-nummer of een externe identifier?
- Is de prijs uit de webshop definitief of moet het ERP die opnieuw berekenen?
- Hoe verwerken we kortingen, transportkosten en btw?
- Wat gebeurt er wanneer een artikelcode uit de webshop niet bestaat in het ERP?
Daar stopt het niet. Ook nadat de bestelling is aangemaakt, moet duidelijk zijn welk systeem nog wijzigingen mag doorvoeren. Mag de webshop een bestelling later nog aanpassen? Of wordt het ERP vanaf dat moment de leidende toepassing?
Dat is de businesslogica achter de integratie.
De POST waarmee je de bestelling technisch doorstuurt, is meestal niet het moeilijkste stuk.
Datastructuren moeten vertaald worden
Zoals in de intro al aangehaald, sluiten datastructuren zelden één op één op elkaar aan.
Systeem A geeft bijvoorbeeld dit terug:
{
"company": "Voorbeeld NV",
"vat": "BE0123456789"
}
Terwijl systeem B dit verwacht:
{
"name": "Voorbeeld NV",
"country": "BE",
"vatNumber": "0123456789"
}
In dit geval is de omzetting nog eenvoudig. Maar in echte projecten wordt het al snel complexer. Het ene systeem kent één adres, het andere werkt met aparte facturatie- en leveringsadressen. Het ene gebruikt een landcode zoals BE, het andere een interne numerieke waarde. Het ene pakket gebruikt vrije tekst voor een betalingsvoorwaarde, terwijl het andere een interne ID verwacht.
Ook statussen zorgen regelmatig voor extra logica. Een webshop kent misschien alleen processing en completed, terwijl het ERP onderscheid maakt tussen besteld, klaargezet, geleverd, gefactureerd en afgesloten.
Daarom werken koppelingen vaak met mappings of tussentabellen.
| Webshop-status | ERP-status |
|---|---|
| processing | 20 |
| shipped | 40 |
| completed | 60 |
Hetzelfde kan gelden voor bijvoorbeeld magazijnen:
| Extern magazijn | ERP-magazijn |
|---|---|
| MAIN | 1 |
| OUTLET | 3 |
| RETURNS | 7 |
Die vertaling lijkt op papier eenvoudig, maar moet technisch wel goed beheerd worden. Wat gebeurt er bijvoorbeeld wanneer er in pakket A een nieuwe code bijkomt die nog niet gekend is in pakket B? Mag de verwerking stoppen? Gebruik je een standaardwaarde? Moet iemand gewaarschuwd worden?
Dat zijn zaken waar je vooraf over moet nadenken.
Gegevens hoeven niet altijd gesynchroniseerd te worden
Bij API-koppelingen wordt vaak automatisch gedacht aan synchronisatie: systeem A bevat gegevens, systeem B bevat gegevens en beide moeten gelijk blijven.
Dat kan nodig zijn, maar is zeker niet altijd de beste aanpak.
Soms wil je gegevens helemaal niet lokaal kopiëren. Je kunt ze ook live via de API ophalen wanneer je ze nodig hebt.
Stel dat een externe toepassing actuele voorraad nodig heeft. Dan kun je ervoor kiezen die voorraad elke paar minuten te synchroniseren, maar je kunt ook rechtstreeks aan het ERP vragen wat de actuele voorraad is:
GET /api/articles/ABC123/stock
Het voordeel daarvan is dat je altijd met de gegevens uit het bronsysteem werkt. Er is geen lokale kopie die achter kan lopen en je hoeft geen apart synchronisatieproces te onderhouden.
Maar ook daar zijn afwegingen. Als een webshop bij iedere paginaweergave meerdere live API-calls naar het ERP uitvoert, kan dat zwaar worden. Je bent bovendien rechtstreeks afhankelijk van de snelheid en beschikbaarheid van dat ERP.
Wat gebeurt er wanneer het ERP tijdelijk niet bereikbaar is? Toon je dan geen voorraad? Gebruik je een gecachte waarde? En hoe oud mag die waarde zijn?
Er bestaat dus niet één juiste aanpak. Soms is live ophalen logisch. Soms synchronisatie. En in veel projecten worden beide technieken naast elkaar gebruikt.
Polling: zelf controleren of er iets gewijzigd is
Een klassieke manier om wijzigingen uit te wisselen is polling.
Daarbij vraagt systeem A op regelmatige tijdstippen aan systeem B of er nieuwe of gewijzigde gegevens zijn.
GET /api/orders?modifiedSince=2026-09-25T10:00:00
Het ontvangende systeem kan die vraag bijvoorbeeld iedere vijf minuten uitvoeren.
Het voordeel daarvan is dat je zelf controle hebt over het moment waarop je gegevens ophaalt. Je bent niet afhankelijk van een extern systeem dat jou actief moet contacteren en je kunt zelf bepalen hoe vaak je controleert.
Voor veel integraties is dat perfect voldoende. Zeker wanneer het niet belangrijk is of een wijziging enkele minuten later verwerkt wordt.
Het nadeel is uiteraard dat je ook requests uitvoert wanneer er niets gewijzigd is. Bij kleine volumes is dat meestal geen probleem. Bij grotere omgevingen, strikte rate limits of processen die sneller moeten reageren, kan een andere aanpak interessanter zijn.
Webhooks: het andere systeem laat weten dat er iets gebeurd is
Een webhook werkt anders.
In plaats van voortdurend te vragen of er iets gewijzigd is, laat het andere systeem zelf weten dat er een gebeurtenis heeft plaatsgevonden.
Stel dat een webshop een betaling ontvangt. Zodra die bestelling betaald is, stuurt de webshop een request naar een endpoint dat wij voorzien:
POST /webhooks/order-paid
Die webhook hoeft niet noodzakelijk alle gegevens van de bestelling mee te sturen. Vaak volstaat iets als:
{
"event": "order.paid",
"orderId": 48215
}
De webhook zegt dan eigenlijk alleen: er is iets gebeurd met bestelling 48215.
Op basis daarvan kan onze integratie zelf de actuele gegevens ophalen:
GET /api/orders/48215
De webhook fungeert dan als trigger, terwijl de API zelf de bron blijft van de actuele gegevens. Je bent minder afhankelijk van de exacte inhoud van de webhook en kunt bij verwerking zelf bepalen welke informatie je nodig hebt.
Stel dat de webhook meldt dat een bestelling gewijzigd is. We halen ze opnieuw op via de API en krijgen bijvoorbeeld:
{
"id": 48215,
"status": "paid",
"customerId": 812,
"total": 1248.50,
"lines": [
{
"articleCode": "ART-001",
"quantity": 4
}
]
}
Daarna beslist de koppeling zelf wat ermee moet gebeuren. Bestaat de bestelling al? Is alleen de status gewijzigd? Zijn er lijnen aangepast? Mag deze wijziging nog verwerkt worden?
De webhook meldt dus dat er iets gewijzigd is. De logica om daar correct mee om te gaan blijft in de integratie zitten.
Een webhook is geen garantie dat iets exact één keer binnenkomt
Een webhook is uiteindelijk ook maar een HTTP-request.
Ons endpoint kan tijdelijk niet bereikbaar zijn. Er kan ergens onderweg een netwerkprobleem optreden. Of het externe platform krijgt geen tijdig antwoord terug en besluit dezelfde webhook opnieuw te versturen.
Dat betekent dat je er niet vanuit mag gaan dat een gebeurtenis exact één keer binnenkomt.
Stel dat bestelling 48215 betaald wordt. De webshop stuurt een webhook en wij verwerken die correct. Door een timeout ontvangt de webshop ons antwoord niet en probeert ze opnieuw.
Als de integratie daar geen rekening mee houdt, zou dezelfde bestelling misschien twee keer in het ERP terechtkomen.
Daarom bouwen we waar nodig idempotente verwerking in. Het principe is eenvoudig: dezelfde boodschap meerdere keren ontvangen mag niet automatisch betekenen dat dezelfde actie meerdere keren wordt uitgevoerd.
Voor bestellingen kan je bijvoorbeeld een externe referentie bewaren. Komt bestelling 48215 opnieuw binnen, dan kan de koppeling eerst controleren of die al verwerkt werd.
Welk systeem is leidend?
Een van de belangrijkste keuzes bij een koppeling is bepalen welk systeem verantwoordelijk is voor welke gegevens.
Neem klantgegevens. Een klant kan tegelijk bestaan in het ERP, CRM, de webshop en een klantenportaal.
Maar waar wordt het officiële facturatieadres beheerd?
Stel dat een klant zijn adres wijzigt in het klantenportaal en die wijziging naar het ERP gestuurd wordt. Vijf minuten later past een medewerker datzelfde adres rechtstreeks in het ERP aan. Welke versie is dan correct?
Als je daar vooraf geen afspraken over maakt, kunnen systemen elkaar blijven overschrijven.
Daarom moet je bepalen welk systeem voor welke gegevens leidend is. Dat hoeft trouwens niet altijd per volledig object te zijn. Het ERP kan bijvoorbeeld eigenaar zijn van klantnummer, betalingsvoorwaarden en facturatieadres, terwijl commerciële contactgegevens uit het CRM komen.
Belangrijk is vooral dat dit vooraf duidelijk wordt vastgelegd.
Soms combineer je gegevens uit meerdere systemen
Niet elke integratie is A naar B.
Soms moet een toepassing gegevens uit verschillende bronnen samenbrengen.
Een klantenportaal kan bijvoorbeeld klantgegevens en facturen ophalen uit het ERP, terwijl openstaande interventies of servicegegevens uit een andere toepassing komen.
Voor de gebruiker moet dat één geheel vormen. Technisch betekent dit dat de koppeling gegevens uit meerdere systemen moet ophalen, vertalen en combineren tot één consistente structuur.
- ERP → klantgegevens en facturen
- Serviceplatform → interventies
- Klantenportaal → toont beide als één geheel
Daar komt opnieuw extra logica bij kijken. Wat als één van beide systemen tijdelijk niet beschikbaar is? Mag het portaal dan gedeeltelijke informatie tonen? Hoe combineer je gegevens die verschillende identifiers gebruiken? En welke gegevens haal je live op, terwijl andere lokaal gecachet mogen worden?
Op dat moment is de integratie eigenlijk een tussenlaag die meerdere toepassingen met elkaar verbindt.
Real-time is niet automatisch beter
Bij koppelingen wordt vaak gevraagd of iets real-time kan.
Meestal kan dat. Maar de betere vraag is of het real-time moet.
Een online betaalde bestelling wil je waarschijnlijk snel verwerken. Voor voorraad kan real-time belangrijk zijn wanneer beschikbaarheid kritisch is.
Maar rapporteringsgegevens die één keer per nacht vernieuwd worden? Daar is meestal niets mis mee.
Ook bij grote datasets kan het interessanter zijn om periodiek alleen de wijzigingen te synchroniseren in plaats van iedere wijziging afzonderlijk onmiddellijk te versturen.
Real-time maakt systemen bovendien sterker afhankelijk van elkaar. Als toepassing A voor iedere handeling onmiddellijk antwoord nodig heeft van toepassing B, wordt de beschikbaarheid van B plots ook belangrijk voor A.
Daarom kijk ik liever naar wat het proces echt nodig heeft dan automatisch naar “zo real-time mogelijk”.
In de praktijk gebruik je verschillende technieken naast elkaar
Een integratie hoeft niet volledig volgens één principe opgebouwd te zijn.
Bij een webshop kan je bijvoorbeeld verschillende technieken combineren:
- Productgegevens: periodieke synchronisatie vanuit het ERP naar de webshop.
- Voorraad: live opvragen via een API.
- Nieuwe bestelling: webhook vanuit de webshop, waarna de integratie de bestelling via de API ophaalt.
- Verzending: status vanuit het ERP terugsturen naar de webshop.
Voor ieder onderdeel kies je wat technisch en functioneel het meest logisch is.
En dat is volgens mij ook de essentie van een goede integratie. Niet vertrekken vanuit “we hebben een REST API, dus we gaan alles via REST in real-time doen”, maar vanuit de gegevensstroom en het proces.
Wat gebeurt er wanneer er iets fout gaat?
Want vroeg of laat gebeurt dat.
Een extern platform is tijdelijk niet beschikbaar. Een API geeft een 500 Internal Server Error of 503 Service Unavailable terug. Een request loopt in timeout. Een access token is verlopen. Of een API retourneert 429 Too Many Requests omdat er te veel requests uitgevoerd worden.
Daarnaast zijn er functionele fouten.
- Een artikel bestaat niet in het ERP.
- Een verplicht veld ontbreekt.
- Een btw-nummer heeft een onverwacht formaat.
- Een betalingsvoorwaarde uit pakket A bestaat niet in pakket B.
Die twee soorten fouten moet je niet op dezelfde manier behandelen.
Bij een tijdelijke 503 is het logisch om later automatisch opnieuw te proberen. Bij een onbekende artikelcode niet. Dat artikel zal dertig seconden later waarschijnlijk nog altijd niet bestaan.
Een degelijke koppeling maakt daarom onderscheid tussen tijdelijke technische fouten en inhoudelijke fouten waarvoor iemand moet tussenkomen.
Retry-mechanismen zijn nuttig, maar niet voor alles
Bij tijdelijke fouten kan automatisch opnieuw proberen veel problemen opvangen.
- Request uitvoeren
503 Service Unavailable- 30 seconden wachten
- Opnieuw proberen
- Bij een nieuwe fout 2 minuten wachten
- Opnieuw proberen
Maar retries moeten ook begrensd zijn.
Je wilt niet dat een integratie eindeloos dezelfde fout blijft herhalen. Bovendien moet je kunnen zien wanneer een verwerking na meerdere pogingen definitief niet gelukt is.
Bij functionele fouten werkt een retry meestal niet. Daar is eerder duidelijke foutregistratie nodig, zodat iemand kan zien wat er moet worden gecorrigeerd.
Logging is geen detail
Logging is bij koppelingen belangrijk.
Niet omdat iemand iedere ochtend de logs moet bekijken. Als alles goed werkt, zou niemand ermee bezig moeten zijn.
Maar zodra iemand zegt: “Bestelling 48215 staat wel in de webshop maar niet in het ERP”, wil je snel kunnen achterhalen wat er gebeurd is.
Een bruikbare logging kan bijvoorbeeld tonen:
14:03:12 webhook ontvangen
14:03:12 bestelling 48215 opgehaald
14:03:13 klant 812 gevonden
14:03:13 artikel ART-001 gevonden
14:03:14 request naar ERP verstuurd
14:03:14 fout: betalingsvoorwaarde 17 bestaat niet
Daar kun je iets mee.
Een melding zoals Error while processing request helpt nauwelijks.
Een integratie is pas goed onderhoudbaar als je achteraf kunt reconstrueren welke gegevens binnenkwamen, welke beslissingen genomen werden en waar het eventueel fout ging.
Beveiliging hoort er uiteraard ook bij
API's zijn normaal gezien beveiligd.
Afhankelijk van het platform kan authenticatie gebeuren met:
- API keys
- Bearer tokens
- OAuth 2.0
- Client credentials
- Certificaten
Ook daar komen praktische vragen bij kijken. Waar worden credentials veilig bewaard? Hoe worden tokens automatisch vernieuwd? Welke rechten krijgt de koppeling? Mag ze alleen lezen of ook gegevens wijzigen? En wat gebeurt er wanneer een secret of certificaat vervangen wordt?
Ook het principe van minimale rechten is belangrijk. Als een integratie alleen voorraad moet uitlezen, hoeft ze niet automatisch toegang te krijgen tot alle andere gegevens of schrijfrechten.
Dat zijn minder zichtbare onderdelen van een integratie, maar ze moeten wel goed zitten.
De API zelf is uiteindelijk maar een technisch middel
Voor mij zit het interessante aan API-koppelingen uiteindelijk niet in de API zelf.
Een GET, POST, webhook of OAuth-flow is gewoon techniek.
De echte meerwaarde zit in wat je ermee kunt verbinden.
Een bestelling wordt geplaatst in een webshop. De webshop meldt dat via een webhook. De integratie haalt de bestelling op, controleert de klant, vertaalt de gegevens naar de structuur van het ERP en maakt de bestelling aan. Later wordt een verzendstatus teruggestuurd en kan een factuur automatisch beschikbaar worden gemaakt in een portaal.
Voor de gebruiker voelt dat als één proces.
Technisch zijn er misschien verschillende toepassingen, API's en gegevensmodellen bij betrokken.
Bij Debiscom bouwen we regelmatig dit soort koppelingen tussen ERP, webshops, SaaS-platformen, boekhoudsoftware, klantenportalen en toepassingen op maat. REST API's, webhooks, webservices, mappings en achtergrondprocessen zijn daarbij technische bouwstenen.
Het moeilijke zit zelden in één GET of POST.
Het zit in alles errond: welke gegevens heb je nodig, hoe vertaal je ze, wanneer haal je ze op, welk systeem is leidend, wat gebeurt er bij fouten en hoe zorg je ervoor dat de hele gegevensstroom betrouwbaar blijft werken?
Als dat goed zit, merkt de gebruiker uiteindelijk weinig van de koppeling zelf. En dat is meestal het beste teken dat ze goed gebouwd is.