Import4You logo

Boeking webhook

Editie 2026 · direct bericht in jouw systeem bij elke statuswijziging

Inhoud

Zo werkt de webhook

4

Instellen in het platform

6

Wat je ontvangt: payload en headers

9

Verificatie van het token

13

Koppelen aan de publieke API

17

Checklist en veelgestelde vragen

19

1

Zo werktde webhook

1. Zo werkt de webhook

Webhook handleiding4

Wij bellen jou, in plaats van andersom

Met de boeking webhook stuurt het Import4You platform direct een bericht (een HTTP POST verzoek) naar een adres van jouw keuze, zodra de status of de verwachte leverdatum van een boeking verandert. Je hoeft dus niet zelf te controleren of er nieuws is: het bericht bevat het boekingsnummer, je eigen kenmerk, schip, container, de datums, de producten en de nieuwe status of leverdatum.

1

Status verandert

Bijvoorbeeld van Collection naar Onderweg, of de verwachte leverdatum schuift

2

Wij sturen een POST

Naar elk adres dat je hebt geregistreerd, met de boekingsgegevens als JSON

3

Jij controleert

Met het token in de header en je client_secret bewijs je dat het van ons komt

4

Jij verwerkt

Antwoord met 2xx en werk je eigen systeem bij

Wanneer krijg je een bericht

Goed om te weten

2

Instellen inhet platform

2. Instellen in het platform

Webhook handleiding6

In vier stappen aan de slag

Wat is een callback URL

Het adres van jouw server waar wij het bericht naartoe sturen, bijvoorbeeld https://api.jouwbedrijf.nl/import4you/webhook. Het moet van buitenaf bereikbaar zijn en een geldig HTTPS certificaat hebben.

Meerdere adressen

Je kunt meer dan één adres registreren. Elk adres ontvangt elk bericht onafhankelijk van de andere adressen.

Los van de publieke API

Deze inloggegevens zijn niet dezelfde als de API sleutels (i4y_test_ of i4y_live_) onder Public API Keys. Gebruik een API secret nooit als webhook sleutel en andersom.

2. Instellen in het platform

Webhook handleiding7

Waar je endpoint aan moet voldoen

Moet

Slim

Antwoordcodes

Alles in de 200 reeks betekent voor ons "goed ontvangen". Een andere code of een time-out betekent dat het bericht niet is aangekomen. Gebruik de Testknop in het platform om te controleren of je endpoint goed antwoordt.

Firewall

Moet je adres beperkt bereikbaar zijn, laat dan verzoeken van het Import4You platform door. Vraag de actuele uitgaande adressen op via support@import4you.nl.

3

Payload enheaders

3. Wat je ontvangt

Webhook handleiding9

De payload

Je endpoint ontvangt een POST verzoek met deze JSON. Het veld event.target zegt wat er veranderd is: status of delivery.expected_date.

{ "data": { "entity": "booking", "event": { "name": "updated", "target": "status | delivery.expected_date" }, "data": { "id": 1, "uuid": "string", "vessel_name": "string | null", "container_number": "string | null", "etd_date": "dd.mm.yyyy | null", "eta_date": "dd.mm.yyyy | null", "personal_ref": "string", "products": [ { "ean_code": "string", "product_name": "string", "start_boxes_count": 10, "box_count": 8, "items_per_box": 12, "product_subs": [ { "uuid": "string", "description": "string", "ean_code": "string", "start_boxes_count": 5, "boxes": 4, "items_per_box": 6 } ] } ], "status": { "id": 1, "name": "Collection" }, "expected_delivery_date": "dd.mm.yyyy" } } }

Veld

Betekenis

entity

Altijd booking

event.name

Altijd updated

event.target

status of delivery.expected_date

data.id

Intern nummer, alleen ter informatie

data.uuid

Het publieke boekingsnummer, zoals SI4Y-000123. Dit gebruik je om de boeking terug te vinden

data.personal_ref

Je eigen kenmerk uit de boeking

vessel_name, container_number

Schip en container, leeg als nog niet bekend

etd_date, eta_date

Verwachte vertrek en aankomstdatum, dd.mm.jjjj, leeg als nog niet bekend

products[]

Producten met EAN, naam, dozen bij start en nu, stuks per doos en eventuele subproducten

status

Alleen bij target status: id en naam zoals het platform die toont

expected_delivery_date

Alleen bij target delivery.expected_date: de nieuwe verwachte leverdatum, dd.mm.jjjj

Datums in de webhook zijn dd.mm.jjjj. De publieke API gebruikt ISO datums (jjjj-mm-dd) en UTC tijdstippen. Houd daar rekening mee als je beide combineert.

3. Wat je ontvangt

Webhook handleiding10

Twee echte voorbeelden

statusDe boeking gaat naar Onderweg

{ "data": { "entity": "booking", "event": { "name": "updated", "target": "status" }, "data": { "id": 4471, "uuid": "SI4Y-000123", "vessel_name": "MSC Oscar", "container_number": "MSCU1234567", "etd_date": "12.11.2026", "eta_date": "20.12.2026", "personal_ref": "REF-4471", "products": [ { "ean_code": "1234567890123", "product_name": "Widget", "start_boxes_count": 10, "box_count": 10, "items_per_box": 10, "product_subs": [] } ], "status": { "id": 5, "name": "In transit" } } } }

datumDe verwachte leverdatum schuift

{ "data": { "entity": "booking", "event": { "name": "updated", "target": "delivery.expected_date" }, "data": { "id": 4471, "uuid": "SI4Y-000123", "vessel_name": "MSC Oscar", "container_number": "MSCU1234567", "etd_date": "12.11.2026", "eta_date": "22.12.2026", "personal_ref": "REF-4471", "products": [ ... ], "expected_delivery_date": "28.12.2026" } } }

Zo lees je het

Kijk eerst naar event.target. Bij status staat er een status object en geen expected_delivery_date. Bij delivery.expected_date is het andersom. De voorbeeldwaarden hierboven zijn fictief; de statusnamen volgen het platform.

3. Wat je ontvangt

Webhook handleiding11

De headers

Elk bericht heeft drie HTTP headers. Met token en je client_secret controleer je dat het bericht echt van Import4You komt en onderweg niet is aangepast.

token: {hmac_signature} client_id: {jouw_client_id} Content-Type: application/json

Header

Betekenis

token

De handtekening: een HMAC SHA256 in hexadecimale vorm (64 tekens), berekend met jouw client_secret. Zie hoofdstuk 4

client_id

Jouw client_id uit Instellingen → Automatisering. Gebruik hem om het juiste secret op te zoeken als je er meerdere hebt

Content-Type

Altijd application/json

Waarom een handtekening

Iedereen die je adres kent, zou er iets naartoe kunnen sturen. De handtekening kan alleen gemaakt worden met jouw secret, dat alleen jij en Import4You kennen. Klopt hij niet, dan gooi je het bericht weg.

Controleer client_id ook

Vergelijk de client_id header met de waarde uit het platform. Zo weet je zeker dat je met het juiste secret controleert, ook als je ooit nieuwe inloggegevens maakt.

Hoofdletters

HTTP headernamen zijn niet hoofdlettergevoelig. Sommige frameworks tonen ze als Token of Client-Id. Lees ze dus hoofdletterongevoelig uit.

4

Verificatievan het token

4. Verificatie van het token

Webhook handleiding13

Zo controleer je het token

Het token is een HMAC SHA256 over een Base64 versie van de inhoud van het veld data. De volgorde en de exacte bytes zijn belangrijk, dus volg de stappen precies.

In één regel

token = hex(HMAC_SHA256(secret, base64_zonder_padding(ruwe_data_json)))

Waarom knippen uit de ruwe tekst

Ons platform maakt de JSON met de standaard PHP instellingen: een / wordt \/ en niet ASCII tekens worden \uXXXX. Als jouw taal het object opnieuw zou schrijven, kan dat afwijken. Door de originele tekst te gebruiken, controleer je precies wat wij hebben ondertekend.

Let op bij geneste "data"

De payload heeft twee keer een veld data: het bovenste (de ondertekende inhoud) en daarbinnen de boekingsgegevens. Je knipt alleen het bovenste uit.

4. Verificatie van het token

Webhook handleiding14

Voorbeeld in Node.js (Express)

const express = require("express"), crypto = require("crypto"); const app = express(); const SECRET = process.env.I4Y_WEBHOOK_SECRET; const CLIENT_ID = process.env.I4Y_WEBHOOK_CLIENT_ID; // Ruwe body bewaren: geen express.json() op deze route app.post("/import4you/webhook", express.raw({ type: "application/json" }), (req, res) => { const raw = req.body.toString("utf8"); const token = req.get("token") || ""; if (req.get("client_id") !== CLIENT_ID) return res.sendStatus(401); // 1. waarde van het bovenste "data" uitknippen const colon = raw.indexOf(":", raw.indexOf('"data"')); const inner = raw.slice(colon + 1, raw.lastIndexOf("}")).trim(); // 2. base64 zonder padding, 3. HMAC SHA256 hex const b64 = Buffer.from(inner, "utf8").toString("base64").replace(/=+$/, ""); const expected = crypto.createHmac("sha256", SECRET).update(b64).digest("hex"); // 4. constant time vergelijken const ok = expected.length === token.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(token)); if (!ok) return res.sendStatus(401); // 5. nu pas parsen en verwerken const payload = JSON.parse(inner); const boeking = payload.data; console.log(boeking.uuid, payload.event.target, boeking.status?.name ?? boeking.expected_delivery_date); res.sendStatus(200); });

Belangrijk in dit voorbeeld

Verwerking buiten het verzoek

Sla het geparste bericht op en geef meteen 200 terug. Verdere verwerking doe je daarna, zodat je endpoint altijd op tijd met 2xx antwoordt.

4. Verificatie van het token

Webhook handleiding15

Voorbeeld in PHP en Python

PHP

<?php $secret = getenv("I4Y_WEBHOOK_SECRET"); $raw = file_get_contents("php://input"); $token = $_SERVER["HTTP_TOKEN"] ?? ""; $clientId = $_SERVER["HTTP_CLIENT_ID"] ?? ""; if ($clientId !== getenv("I4Y_WEBHOOK_CLIENT_ID")) { http_response_code(401); exit; } // Bovenste "data" uit de ruwe tekst knippen $start = strpos($raw, '"data"'); $colon = strpos($raw, ":", $start); $end = strrpos($raw, "}"); $inner = trim(substr($raw, $colon + 1, $end - $colon - 1)); $b64 = rtrim(base64_encode($inner), "="); $expected = hash_hmac("sha256", $b64, $secret); if (!hash_equals($expected, $token)) { http_response_code(401); exit; } $payload = json_decode($inner, true); $boeking = $payload["data"]; // ... opslaan en verwerken http_response_code(200);

Ons platform is in PHP gebouwd. Daarom werkt hier ook json_encode(json_decode($raw, true)["data"]) als basis, zolang je geen JSON vlaggen meegeeft. De uitknipmethode is in elke taal veilig.

Python (Flask)

import base64, hmac, hashlib, json, os from flask import Flask, request, abort app = Flask(__name__) SECRET = os.environ["I4Y_WEBHOOK_SECRET"].encode() CLIENT_ID = os.environ["I4Y_WEBHOOK_CLIENT_ID"] @app.post("/import4you/webhook") def webhook(): raw = request.get_data(as_text=True) token = request.headers.get("token", "") if request.headers.get("client_id") != CLIENT_ID: abort(401) start = raw.index('"data"') colon = raw.index(":", start) end = raw.rindex("}") inner = raw[colon + 1:end].strip() b64 = base64.b64encode(inner.encode("utf-8")) \ .decode().rstrip("=") expected = hmac.new(SECRET, b64.encode(), hashlib.sha256).hexdigest() if not hmac.compare_digest(expected, token): abort(401) payload = json.loads(inner) boeking = payload["data"] # ... opslaan en verwerken return "", 200

Testen

Gebruik de Testknop in het platform. Ontvang je 401 in je eigen log, controleer dan of je echt de ruwe body gebruikt en of het secret klopt. Verificatievoorbeelden met testwaarden staan ook in het platform onder Automatisering.

5

Koppelen aande publieke API

5. Koppelen aan de publieke API

Webhook handleiding17

Webhook en publieke API samen

De webhook meldt dat er iets veranderd is. De publieke API (live eind november 2026) geeft de volledige boeking met de vaste statuscodes en alle datums. Zo horen de velden bij elkaar.

1

Webhook komt binnen

Status of leverdatum veranderd voor SI4Y-000123

2

Token controleren

Hoofdstuk 4

3

Boeking ophalen

GET /bookings/SI4Y-000123 met een API token

4

Eigen systeem bijwerken

Met de vaste statuscode en alle datums

In de webhook

In de publieke API

Gebruik

data.data.uuid

Boeking id, bijvoorbeeld SI4Y-000123

Gebruik dit in GET /bookings/{uuid} en GET /bookings/{uuid}/events

data.data.id

Geen

Intern nummer, niet bruikbaar in de API

data.data.personal_ref

personal_ref

Je eigen kenmerk, gelijk in beide

data.data.status.name

status als vaste code

De webhook geeft de naam uit het platform ("Collection"), de API een vaste code (collecting, in_transit). Bouw je logica op de API code

etd_date, eta_date, expected_delivery_date (dd.mm.jjjj)

dates.estimated_departure_date, dates.estimated_arrival_date, dates.planned_delivery_date (jjjj-mm-dd)

Zelfde informatie, ander formaat

products[] met EAN en naam

products[] met UUID's

Productregels in de webhook hebben geen API ID. Zoek producten op via de boeking in de API

Twee aparte sets inloggegevens

Webhook: client_id en client_secret uit Instellingen → Automatisering. Publieke API: een sleutel uit Instellingen → Public API Keys die je inwisselt voor een Bearer token. Ze zijn niet uitwisselbaar.

Alleen webhook, zonder API

Dan heb je per bericht het boekingsnummer, je eigen kenmerk, schip, container, de datums, de producten en de statusnaam of de nieuwe verwachte leverdatum.

6

Checklisten vragen

6. Checklist en veelgestelde vragen

Webhook handleiding19

Checklist voordat je live gaat

Instellen

Verwerken

2events

Status en leverdatum

Dit zijn de twee gebeurtenissen die de webhook nu stuurt, allebei met event.name updated.

1of meer

Adressen

Elk geregistreerd HTTPS adres ontvangt elk bericht.

1secret

Eén sleutel om te controleren

Roteer je het secret in het platform, pas dan ook je endpoint aan.

6. Checklist en veelgestelde vragen

Webhook handleiding20

Veelgestelde vragen

Is de webhook nu al beschikbaar?

Ja. De boeking webhook staat in het platform onder Instellingen → Automatisering. De publieke API, waarmee je de volledige boeking kunt ophalen, volgt eind november 2026.

Krijg ik een bericht voor elke boeking?

Ja, voor elke boeking van je bedrijf, zodra de status of de verwachte leverdatum verandert. Je hoeft niets per boeking in te stellen.

Wat als mijn server even niet bereikbaar is?

Komt een bericht de eerste keer niet aan (geen 2xx), dan bieden we het opnieuw aan. Blijft je endpoint langer onbereikbaar, haal dan de actuele status op in het platform of via de publieke API.

Waarom zie ik Engelse statusnamen in het bericht?

De webhook stuurt de statusnaam zoals die in het platform heet. Wil je vaste codes om op te programmeren, gebruik dan de publieke API: die geeft altijd dezelfde Engelse code, los van de weergave.

Kan ik de webhook ook voor zendingen uit voorraad gebruiken?

Nee. De webhook stuurt alleen gebeurtenissen van boekingen. Zendingen volg je via de publieke API met GET /shipments/{id}/events.

Mijn token klopt nooit. Wat gaat er mis?

Bijna altijd één van deze drie: je gebruikt een geparsed object in plaats van de ruwe tekst, je vergeet de = tekens van de Base64 weg te halen, of je neemt de hele body in plaats van alleen de waarde van data.

Waar kan ik terecht met vragen?

Mail support@import4you.nl met het boekingsnummer en het tijdstip van het bericht, of gebruik de contactpagina .

Import4You logo

Boeking webhook

Editie 2026 · direct bericht in jouw systeem bij elke statuswijziging

Inhoud

Zo werkt de webhook4
Instellen in het platform6
Wat je ontvangt: payload en headers9
Verificatie van het token13
Koppelen aan de publieke API17
Checklist en veelgestelde vragen19
1
Zo werkt
de webhook
1. Zo werkt de webhookWebhook handleiding4

Wij bellen jou, in plaats van andersom

Met de boeking webhook stuurt het Import4You platform direct een bericht (een HTTP POST verzoek) naar een adres van jouw keuze, zodra de status of de verwachte leverdatum van een boeking verandert. Je hoeft dus niet zelf te controleren of er nieuws is: het bericht bevat het boekingsnummer, je eigen kenmerk, schip, container, de datums, de producten en de nieuwe status of leverdatum.

1
Status verandert
Bijvoorbeeld van Collection naar Onderweg, of de verwachte leverdatum schuift
2
Wij sturen een POST
Naar elk adres dat je hebt geregistreerd, met de boekingsgegevens als JSON
3
Jij controleert
Met het token in de header en je client_secret bewijs je dat het van ons komt
4
Jij verwerkt
Antwoord met 2xx en werk je eigen systeem bij

Wanneer krijg je een bericht

  • ✓De status van een boeking verandert
  • ✓De verwachte leverdatum van een boeking verandert
  • ✕Nog niet: statuswijzigingen van zendingen uit voorraad. Die lees je uit via de publieke API

Goed om te weten

  • iJe kunt meerdere adressen registreren. Elk adres krijgt elk bericht, los van elkaar
  • iElk bericht is ondertekend met HMAC SHA256 en je client_id, allebei als HTTP header
  • iAlleen HTTPS adressen zijn toegestaan
  • iDe webhook heeft eigen inloggegevens, los van de publieke API sleutels
2
Instellen in
het platform
2. Instellen in het platformWebhook handleiding6

In vier stappen aan de slag

  1. Maak inloggegevens aan. Log in op het platform en ga naar Instellingen → Automatisering. Genereer je API inloggegevens: een client_id en een client_secret.
  2. Bewaar het secret veilig. Je hebt het nodig om elk bericht te controleren. Zet het in je secret store, nooit in een mail of ticket.
  3. Registreer je adres. Voeg onder Callback URL's een of meer HTTPS adressen toe waar wij de berichten naartoe sturen.
  4. Test. Klik op de Testknop naast een adres. Wij sturen een voorbeeldbericht. Controleer dat je server antwoordt met een 2xx status (bijvoorbeeld 200).

Wat is een callback URL

Het adres van jouw server waar wij het bericht naartoe sturen, bijvoorbeeld https://api.jouwbedrijf.nl/import4you/webhook. Het moet van buitenaf bereikbaar zijn en een geldig HTTPS certificaat hebben.

Meerdere adressen

Je kunt meer dan één adres registreren. Elk adres ontvangt elk bericht onafhankelijk van de andere adressen.

Los van de publieke API

Deze inloggegevens zijn niet dezelfde als de API sleutels (i4y_test_ of i4y_live_) onder Public API Keys. Gebruik een API secret nooit als webhook sleutel en andersom.

2. Instellen in het platformWebhook handleiding7

Waar je endpoint aan moet voldoen

Moet

  • ✓HTTPS met een geldig certificaat
  • ✓POST verzoeken met Content-Type: application/json accepteren
  • ✓Snel antwoorden met een 2xx status, het liefst binnen een paar seconden
  • ✓Het token controleren voordat je iets verwerkt
  • ✓De ruwe body bewaren voor de controle, zie hoofdstuk 4

Slim

  • ✓Eerst opslaan en 2xx teruggeven, daarna pas verwerken
  • ✓Dubbel ontvangen berichten netjes afhandelen: een bericht dat niet met 2xx is bevestigd, bieden we opnieuw aan
  • ✓Het verzoek loggen met tijdstip en uuid, zonder het secret
  • ✓Een onbekende target of extra velden negeren in plaats van er een fout op te geven, dan blijft je koppeling werken bij uitbreidingen

Antwoordcodes

Alles in de 200 reeks betekent voor ons "goed ontvangen". Een andere code of een time-out betekent dat het bericht niet is aangekomen. Gebruik de Testknop in het platform om te controleren of je endpoint goed antwoordt.

Firewall

Moet je adres beperkt bereikbaar zijn, laat dan verzoeken van het Import4You platform door. Vraag de actuele uitgaande adressen op via support@import4you.nl.

3
Payload en
headers
3. Wat je ontvangtWebhook handleiding9

De payload

Je endpoint ontvangt een POST verzoek met deze JSON. Het veld event.target zegt wat er veranderd is: status of delivery.expected_date.

{
  "data": {
    "entity": "booking",
    "event": {
      "name": "updated",
      "target": "status | delivery.expected_date"
    },
    "data": {
      "id": 1,
      "uuid": "string",
      "vessel_name": "string | null",
      "container_number": "string | null",
      "etd_date": "dd.mm.yyyy | null",
      "eta_date": "dd.mm.yyyy | null",
      "personal_ref": "string",
      "products": [
        {
          "ean_code": "string",
          "product_name": "string",
          "start_boxes_count": 10,
          "box_count": 8,
          "items_per_box": 12,
          "product_subs": [
            {
              "uuid": "string",
              "description": "string",
              "ean_code": "string",
              "start_boxes_count": 5,
              "boxes": 4,
              "items_per_box": 6
            }
          ]
        }
      ],
      "status": { "id": 1, "name": "Collection" },
      "expected_delivery_date": "dd.mm.yyyy"
    }
  }
}
VeldBetekenis
entityAltijd booking
event.nameAltijd updated
event.targetstatus of delivery.expected_date
data.idIntern nummer, alleen ter informatie
data.uuidHet publieke boekingsnummer, zoals SI4Y-000123. Dit gebruik je om de boeking terug te vinden
data.personal_refJe eigen kenmerk uit de boeking
vessel_name, container_numberSchip en container, leeg als nog niet bekend
etd_date, eta_dateVerwachte vertrek en aankomstdatum, dd.mm.jjjj, leeg als nog niet bekend
products[]Producten met EAN, naam, dozen bij start en nu, stuks per doos en eventuele subproducten
statusAlleen bij target status: id en naam zoals het platform die toont
expected_delivery_dateAlleen bij target delivery.expected_date: de nieuwe verwachte leverdatum, dd.mm.jjjj
Datums in de webhook zijn dd.mm.jjjj. De publieke API gebruikt ISO datums (jjjj-mm-dd) en UTC tijdstippen. Houd daar rekening mee als je beide combineert.
3. Wat je ontvangtWebhook handleiding10

Twee echte voorbeelden

statusDe boeking gaat naar Onderweg

{
  "data": {
    "entity": "booking",
    "event": { "name": "updated", "target": "status" },
    "data": {
      "id": 4471,
      "uuid": "SI4Y-000123",
      "vessel_name": "MSC Oscar",
      "container_number": "MSCU1234567",
      "etd_date": "12.11.2026",
      "eta_date": "20.12.2026",
      "personal_ref": "REF-4471",
      "products": [
        {
          "ean_code": "1234567890123",
          "product_name": "Widget",
          "start_boxes_count": 10,
          "box_count": 10,
          "items_per_box": 10,
          "product_subs": []
        }
      ],
      "status": { "id": 5, "name": "In transit" }
    }
  }
}

datumDe verwachte leverdatum schuift

{
  "data": {
    "entity": "booking",
    "event": { "name": "updated",
               "target": "delivery.expected_date" },
    "data": {
      "id": 4471,
      "uuid": "SI4Y-000123",
      "vessel_name": "MSC Oscar",
      "container_number": "MSCU1234567",
      "etd_date": "12.11.2026",
      "eta_date": "22.12.2026",
      "personal_ref": "REF-4471",
      "products": [ ... ],
      "expected_delivery_date": "28.12.2026"
    }
  }
}

Zo lees je het

Kijk eerst naar event.target. Bij status staat er een status object en geen expected_delivery_date. Bij delivery.expected_date is het andersom. De voorbeeldwaarden hierboven zijn fictief; de statusnamen volgen het platform.

3. Wat je ontvangtWebhook handleiding11

De headers

Elk bericht heeft drie HTTP headers. Met token en je client_secret controleer je dat het bericht echt van Import4You komt en onderweg niet is aangepast.

token: {hmac_signature}
client_id: {jouw_client_id}
Content-Type: application/json
HeaderBetekenis
tokenDe handtekening: een HMAC SHA256 in hexadecimale vorm (64 tekens), berekend met jouw client_secret. Zie hoofdstuk 4
client_idJouw client_id uit Instellingen → Automatisering. Gebruik hem om het juiste secret op te zoeken als je er meerdere hebt
Content-TypeAltijd application/json

Waarom een handtekening

Iedereen die je adres kent, zou er iets naartoe kunnen sturen. De handtekening kan alleen gemaakt worden met jouw secret, dat alleen jij en Import4You kennen. Klopt hij niet, dan gooi je het bericht weg.

Controleer client_id ook

Vergelijk de client_id header met de waarde uit het platform. Zo weet je zeker dat je met het juiste secret controleert, ook als je ooit nieuwe inloggegevens maakt.

Hoofdletters

HTTP headernamen zijn niet hoofdlettergevoelig. Sommige frameworks tonen ze als Token of Client-Id. Lees ze dus hoofdletterongevoelig uit.

4
Verificatie
van het token
4. Verificatie van het tokenWebhook handleiding13

Zo controleer je het token

Het token is een HMAC SHA256 over een Base64 versie van de inhoud van het veld data. De volgorde en de exacte bytes zijn belangrijk, dus volg de stappen precies.

  1. Bewaar de ruwe body. Lees de HTTP body als tekst, voordat je JSON library hem omzet.
  2. Knip de waarde van het bovenste veld data uit die tekst. Dus alles tussen {"data": en de laatste }. Gebruik niet JSON.stringify of json.dumps op een geparsed object: grote getallen en decimalen kunnen dan net anders worden geschreven en dan klopt de handtekening niet meer.
  3. Base64 codeer die exacte UTF-8 bytes en haal de = tekens aan het einde weg.
  4. Bereken HMAC SHA256 over die Base64 tekst met je client_secret als sleutel. Neem de hexadecimale uitkomst.
  5. Vergelijk met de token header, met een constant time vergelijking. Gelijk? Dan verwerk je het bericht. Daarna parse je de uitgeknipte data tekst als JSON.

In één regel

token = hex(HMAC_SHA256(secret, base64_zonder_padding(ruwe_data_json)))

Waarom knippen uit de ruwe tekst

Ons platform maakt de JSON met de standaard PHP instellingen: een / wordt \/ en niet ASCII tekens worden \uXXXX. Als jouw taal het object opnieuw zou schrijven, kan dat afwijken. Door de originele tekst te gebruiken, controleer je precies wat wij hebben ondertekend.

Let op bij geneste "data"

De payload heeft twee keer een veld data: het bovenste (de ondertekende inhoud) en daarbinnen de boekingsgegevens. Je knipt alleen het bovenste uit.

4. Verificatie van het tokenWebhook handleiding14

Voorbeeld in Node.js (Express)

const express = require("express"), crypto = require("crypto");
const app = express();
const SECRET = process.env.I4Y_WEBHOOK_SECRET;
const CLIENT_ID = process.env.I4Y_WEBHOOK_CLIENT_ID;

// Ruwe body bewaren: geen express.json() op deze route
app.post("/import4you/webhook", express.raw({ type: "application/json" }),
  (req, res) => {
    const raw = req.body.toString("utf8");
    const token = req.get("token") || "";
    if (req.get("client_id") !== CLIENT_ID) return res.sendStatus(401);

    // 1. waarde van het bovenste "data" uitknippen
    const colon = raw.indexOf(":", raw.indexOf('"data"'));
    const inner = raw.slice(colon + 1, raw.lastIndexOf("}")).trim();

    // 2. base64 zonder padding, 3. HMAC SHA256 hex
    const b64 = Buffer.from(inner, "utf8").toString("base64").replace(/=+$/, "");
    const expected = crypto.createHmac("sha256", SECRET).update(b64).digest("hex");

    // 4. constant time vergelijken
    const ok = expected.length === token.length &&
      crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(token));
    if (!ok) return res.sendStatus(401);

    // 5. nu pas parsen en verwerken
    const payload = JSON.parse(inner);
    const boeking = payload.data;
    console.log(boeking.uuid, payload.event.target,
      boeking.status?.name ?? boeking.expected_delivery_date);
    res.sendStatus(200);
  });

Belangrijk in dit voorbeeld

  • ✓express.raw in plaats van express.json, zodat de originele bytes beschikbaar blijven
  • ✓Eerst client_id en token controleren, dan pas parsen
  • ✓timingSafeEqual voor de vergelijking
  • ✓Het secret komt uit een omgevingsvariabele, niet uit de code

Verwerking buiten het verzoek

Sla het geparste bericht op en geef meteen 200 terug. Verdere verwerking doe je daarna, zodat je endpoint altijd op tijd met 2xx antwoordt.

4. Verificatie van het tokenWebhook handleiding15

Voorbeeld in PHP en Python

PHP

<?php
$secret   = getenv("I4Y_WEBHOOK_SECRET");
$raw      = file_get_contents("php://input");
$token    = $_SERVER["HTTP_TOKEN"] ?? "";
$clientId = $_SERVER["HTTP_CLIENT_ID"] ?? "";

if ($clientId !== getenv("I4Y_WEBHOOK_CLIENT_ID")) {
    http_response_code(401); exit;
}

// Bovenste "data" uit de ruwe tekst knippen
$start = strpos($raw, '"data"');
$colon = strpos($raw, ":", $start);
$end   = strrpos($raw, "}");
$inner = trim(substr($raw, $colon + 1, $end - $colon - 1));

$b64      = rtrim(base64_encode($inner), "=");
$expected = hash_hmac("sha256", $b64, $secret);

if (!hash_equals($expected, $token)) {
    http_response_code(401); exit;
}

$payload = json_decode($inner, true);
$boeking = $payload["data"];
// ... opslaan en verwerken
http_response_code(200);

Ons platform is in PHP gebouwd. Daarom werkt hier ook json_encode(json_decode($raw, true)["data"]) als basis, zolang je geen JSON vlaggen meegeeft. De uitknipmethode is in elke taal veilig.

Python (Flask)

import base64, hmac, hashlib, json, os
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ["I4Y_WEBHOOK_SECRET"].encode()
CLIENT_ID = os.environ["I4Y_WEBHOOK_CLIENT_ID"]

@app.post("/import4you/webhook")
def webhook():
    raw = request.get_data(as_text=True)
    token = request.headers.get("token", "")
    if request.headers.get("client_id") != CLIENT_ID:
        abort(401)

    start = raw.index('"data"')
    colon = raw.index(":", start)
    end = raw.rindex("}")
    inner = raw[colon + 1:end].strip()

    b64 = base64.b64encode(inner.encode("utf-8")) \
        .decode().rstrip("=")
    expected = hmac.new(SECRET, b64.encode(),
                        hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, token):
        abort(401)

    payload = json.loads(inner)
    boeking = payload["data"]
    # ... opslaan en verwerken
    return "", 200

Testen

Gebruik de Testknop in het platform. Ontvang je 401 in je eigen log, controleer dan of je echt de ruwe body gebruikt en of het secret klopt. Verificatievoorbeelden met testwaarden staan ook in het platform onder Automatisering.

5
Koppelen aan
de publieke API
5. Koppelen aan de publieke APIWebhook handleiding17

Webhook en publieke API samen

De webhook meldt dat er iets veranderd is. De publieke API (live eind november 2026) geeft de volledige boeking met de vaste statuscodes en alle datums. Zo horen de velden bij elkaar.

1
Webhook komt binnen
Status of leverdatum veranderd voor SI4Y-000123
2
Token controleren
Hoofdstuk 4
3
Boeking ophalen
GET /bookings/SI4Y-000123 met een API token
4
Eigen systeem bijwerken
Met de vaste statuscode en alle datums
In de webhookIn de publieke APIGebruik
data.data.uuidBoeking id, bijvoorbeeld SI4Y-000123Gebruik dit in GET /bookings/{uuid} en GET /bookings/{uuid}/events
data.data.idGeenIntern nummer, niet bruikbaar in de API
data.data.personal_refpersonal_refJe eigen kenmerk, gelijk in beide
data.data.status.namestatus als vaste codeDe webhook geeft de naam uit het platform ("Collection"), de API een vaste code (collecting, in_transit). Bouw je logica op de API code
etd_date, eta_date, expected_delivery_date (dd.mm.jjjj)dates.estimated_departure_date, dates.estimated_arrival_date, dates.planned_delivery_date (jjjj-mm-dd)Zelfde informatie, ander formaat
products[] met EAN en naamproducts[] met UUID'sProductregels in de webhook hebben geen API ID. Zoek producten op via de boeking in de API

Twee aparte sets inloggegevens

Webhook: client_id en client_secret uit Instellingen → Automatisering. Publieke API: een sleutel uit Instellingen → Public API Keys die je inwisselt voor een Bearer token. Ze zijn niet uitwisselbaar.

Alleen webhook, zonder API

Dan heb je per bericht het boekingsnummer, je eigen kenmerk, schip, container, de datums, de producten en de statusnaam of de nieuwe verwachte leverdatum.

6
Checklist
en vragen
6. Checklist en veelgestelde vragenWebhook handleiding19

Checklist voordat je live gaat

Instellen

  • ✓client_id en client_secret aangemaakt onder Instellingen → Automatisering
  • ✓Secret opgeslagen in je secret store, nergens anders
  • ✓HTTPS adres geregistreerd onder Callback URL's
  • ✓Testknop gebruikt, je endpoint gaf 2xx

Verwerken

  • ✓Token wordt gecontroleerd op de ruwe body, met constant time vergelijking
  • ✓client_id wordt vergeleken
  • ✓Je endpoint antwoordt snel en verwerkt daarna
  • ✓Dubbele berichten en onbekende targets doen geen kwaad
  • ✓Je logt uuid, target en tijdstip, zonder secret
2events
Status en leverdatum
Dit zijn de twee gebeurtenissen die de webhook nu stuurt, allebei met event.name updated.
1of meer
Adressen
Elk geregistreerd HTTPS adres ontvangt elk bericht.
1secret
Eén sleutel om te controleren
Roteer je het secret in het platform, pas dan ook je endpoint aan.
6. Checklist en veelgestelde vragenWebhook handleiding20

Veelgestelde vragen

Is de webhook nu al beschikbaar?

Ja. De boeking webhook staat in het platform onder Instellingen → Automatisering. De publieke API, waarmee je de volledige boeking kunt ophalen, volgt eind november 2026.

Krijg ik een bericht voor elke boeking?

Ja, voor elke boeking van je bedrijf, zodra de status of de verwachte leverdatum verandert. Je hoeft niets per boeking in te stellen.

Wat als mijn server even niet bereikbaar is?

Komt een bericht de eerste keer niet aan (geen 2xx), dan bieden we het opnieuw aan. Blijft je endpoint langer onbereikbaar, haal dan de actuele status op in het platform of via de publieke API.

Waarom zie ik Engelse statusnamen in het bericht?

De webhook stuurt de statusnaam zoals die in het platform heet. Wil je vaste codes om op te programmeren, gebruik dan de publieke API: die geeft altijd dezelfde Engelse code, los van de weergave.

Kan ik de webhook ook voor zendingen uit voorraad gebruiken?

Nee. De webhook stuurt alleen gebeurtenissen van boekingen. Zendingen volg je via de publieke API met GET /shipments/{id}/events.

Mijn token klopt nooit. Wat gaat er mis?

Bijna altijd één van deze drie: je gebruikt een geparsed object in plaats van de ruwe tekst, je vergeet de = tekens van de Base64 weg te halen, of je neemt de hele body in plaats van alleen de waarde van data.

Waar kan ik terecht met vragen?

Mail support@import4you.nl met het boekingsnummer en het tijdstip van het bericht, of gebruik de contactpagina.