# Leveringen via de API Gebruik `https://api.vlows.io` met een key uit de console. Lezen vereist `deliveries:read`; aanmaken, wijzigen, gebeurtenissen en pogingen vereisen `deliveries:write`. Selecteer beide rechten als je koppeling schrijft én leest. ## Gebruik een geregistreerde vervoerder Vraag je contactpersoon bij vlows welke vervoerderscodes voor je organisatie beschikbaar zijn. Laat ontbrekende vervoerders eerst toevoegen. De code is uniek binnen je organisatie; je neemt hem exact over in `courier`. ```sh curl --fail-with-body --silent --show-error \ 'https://api.vlows.io/account/v1/couriers' \ -H "Authorization: Bearer $API_KEY" ``` Je stuurt geen body of filters. Het antwoord is een lijst met `code` en `name` per vervoerder, bijvoorbeeld `{"data":[{"code":"demo","name":"Demo vervoerder"}],"error":null}`. Dit is een voorbeeld; gebruik een code uit je eigen antwoord. ## Een levering aanmaken **Aanvraag:** `POST /deliveries/v1/parcels`. | Plaats | Veld | Verplicht? | Invullen | | --- | --- | --- | --- | | Header | `Authorization` | Ja | `Bearer `. | | Header | `Content-Type` | Ja voor deze JSON-aanvraag | `application/json`. | | Header | `Idempotency-Key` | Ja | Nieuwe unieke sleutel van 8–128 bytes; bewaar voor retries. | | Body | `courier` | Ja | Geregistreerde vervoerderscode. | | Body | `country` | Ja | Exact `"NL"`. | | Body | `postal_code` | Ja | Nederlandse postcode als tekst, bijvoorbeeld `"9901KB"`. | | Body | `number` | Ja | Huisnummer als tekst, bijvoorbeeld `"16"`. | | Body | Overige pakketvelden | Nee of voorwaardelijk | Zie [alle 41 velden](/pakketvelden/). | ```sh API_URL=https://api.vlows.io curl --fail-with-body --silent --show-error \ -X POST "$API_URL/deliveries/v1/parcels" \ -H "Authorization: Bearer $API_KEY" \ -H 'Content-Type: application/json' \ -H 'Idempotency-Key: levering-voorbeeld-0001' \ --data '{"courier":"demo","country":"NL","postal_code":"9901KB","number":"16","parcel_weight":1200}' ``` Vervang vervoerder en adres door de gegevens die je wilt registreren. Een geslaagde aanvraag slaat een levering op en geeft **201** terug. Bewaar `data.entry_id` voor vervolgaanvragen en de responseheader `ETag` voor versiecontrole. Een herhaling van exact dezelfde aanvraag met dezelfde idempotentiesleutel maakt geen tweede levering. ## Uitgebreid voorbeeld Dit voorbeeld voegt afmetingen, planning en een gepland bezorgadres toe. De tijden zijn 16 oktober 2026, 08:00–10:00 UTC. Gebruik je eigen werkelijke planning. Het is geen verplichte lijst: laat optionele gegevens weg die je niet kent. ```json { "courier": "demo", "country": "NL", "postal_code": "9901KB", "number": "16", "parcel_length": 300, "parcel_width": 200, "parcel_height": 150, "parcel_weight": 1200, "time_slot_period_start": 1792137600, "time_slot_period_end": 1792144800, "delivery_status": 0, "service_type": 1, "delivery_vehicle": "bestelbus", "planned_country": "NL", "planned_postal_code": "9901KB", "planned_number": "16", "planned_location_type": 0 } ``` Wil je deze JSON versturen, bewaar hem als `levering.json`. Gebruik dezelfde POST-aanvraag als hierboven en vervang het `--data`-argument door `--data-binary @levering.json`. Kies voor iedere nieuwe levering een nieuwe idempotentiesleutel. Dit betekent een pakket van 30 × 20 × 15 cm en 1,2 kg, met expressservice, bezorging aan de deur en status aangemeld. `delivery_attempts` wordt 0 omdat je geen historische pogingen hebt meegegeven. `time_delivered` en `delivered_*` ontbreken omdat er nog geen aflevering is vastgelegd. Bij de response vind je ook `version`, `created_at`, `updated_at` en adrescontroles. `address.status: "not_found"` betekent dat het opgegeven adres niet exact is gevonden; lees de [adresuitkomst](/adressen/) ook als de levering succesvol is geregistreerd. ## Een bestaande afgeleverde levering importeren Bij de eerste registratie kun je een historische levering met status 4 aanleveren. Dan is `time_delivered` verplicht. Historische pogingslots zijn optioneel; als je ze invult, beginnen ze bij 1 en bevatten ze steeds tijd en uitkomst. ```json { "courier": "demo", "country": "NL", "postal_code": "9901KB", "number": "16", "delivery_status": 4, "time_delivered": 1792141200, "delivered_country": "NL", "delivered_postal_code": "9901KB", "delivered_number": "16", "delivered_location_type": 0, "delivery_attempts": 2, "time_attempt_1": 1792137600, "attempt_1_outcome": 1, "attempt_1_reason": 1, "time_attempt_2": 1792141200, "attempt_2_outcome": 0, "attempt_2_reason": 0 } ``` Dit beschrijft twee pogingen: eerst niet thuis, daarna afgeleverd. `delivery_attempts: 2` komt exact overeen met de twee ingevulde slots. De actuele aflevertijd en status zijn expliciet meegegeven. Gebruik voor een **al bestaande** levering de [pogingenaanvraag](/gebeurtenissen/) om een nieuwe waarneming toe te voegen; maak niet nogmaals dezelfde levering aan. ## Een levering opvragen **Aanvraag:** `GET /deliveries/v1/parcels/{id}`. Vervang `{id}` door de ontvangen `entry_id`. Stuur alleen de `Authorization`-header; er zijn geen body of filters nodig. ```sh ENTRY_ID='' curl --fail-with-body --silent --show-error -i \ "https://api.vlows.io/deliveries/v1/parcels/$ENTRY_ID" \ -H "Authorization: Bearer $API_KEY" ``` `-i` toont ook de headers. Noteer `ETag`, bijvoorbeeld `"3"`, als je wilt wijzigen. `data` bevat de levering met de actuele versie en adrescontroles. Een onbekend of niet-toegankelijk ID geeft 404; een ongeldig UUID-formaat geeft 400. ## Lijstfilters en paginering **Aanvraag:** `GET /deliveries/v1/parcels`. Geen JSON-body. Alle filters zijn optioneel. | Queryveld | Formaat | Standaard | Voorbeeld en gedrag | | --- | --- | --- | --- | | `limit` | Geheel getal 1–100 | 50 | `limit=25` vraagt maximaal 25 resultaten. | | `status` | Geheel getal 0–5 | Alle statussen | `status=2` voor onderweg. | | `courier` | Vervoerderscode als tekst | Alle vervoerders | `courier=demo`; exacte code uit jouw organisatie. | | `cursor` | Ongewijzigde ontvangen cursor | Eerste pagina | Neem `data.next_cursor` uit het vorige antwoord over. | ```sh curl --fail-with-body --silent --show-error --get \ 'https://api.vlows.io/deliveries/v1/parcels' \ -H "Authorization: Bearer $API_KEY" \ --data-urlencode 'limit=25' \ --data-urlencode 'status=2' \ --data-urlencode 'courier=demo' ``` Het antwoord heeft `data.items` en `data.next_cursor`. De nieuwste registraties komen eerst. Zolang `next_cursor` niet leeg is, vraag je de volgende pagina op met dezelfde filters plus die cursor. Je hoeft de cursor niet zelf te ontleden of te maken. Een lege `items`-lijst is geldig. Als `next_cursor` leeg is, ben je klaar. De lijst is bedoeld voor bladeren; haal voor complete actuele adrescontroles het detail van de gewenste levering op. Er is geen vrij tekstzoekveld of filter op klantnaam. ## Een levering wijzigen **Aanvraag:** `PATCH /deliveries/v1/parcels/{id}`. Gebruik een key met `deliveries:write`, de juiste UUID in het pad en `Content-Type: application/json`. 1. Lees de levering en de actuele `ETag` via GET. 2. Kies alleen velden met **PATCH ja** in de [veldgids](/pakketvelden/). 3. Stuur de ETag letterlijk mee als `If-Match`, inclusief dubbele aanhalingstekens. 4. Verwerk het antwoord en bewaar de nieuwe versie. ```sh curl --fail-with-body --silent --show-error \ -X PATCH "https://api.vlows.io/deliveries/v1/parcels/$ENTRY_ID" \ -H "Authorization: Bearer $API_KEY" \ -H 'Content-Type: application/json' \ -H 'If-Match: "3"' \ --data '{"parcel_weight":1500,"parcel_height":180}' ``` Vervang `"3"` door de werkelijk ontvangen ETag. In dit voorbeeld veranderen alleen gewicht en hoogte. Weggelaten velden blijven bestaan. Met `null` kun je een toegestaan optioneel numeriek veld wissen; optionele tekst maak je leeg met `""`. Bij gekoppelde velden moet het resultaat geldig blijven. Een geslaagde wijziging geeft 200, de bijgewerkte levering en een nieuwe `ETag`. Een ontbrekende of verouderde `If-Match` geeft 412. Lees dan opnieuw en beoordeel je wijziging. Ook een adreshercontrole kan de versie verhogen. Je kunt de vervoerder, status, aflevertijd, pogingenteller, afgeleverde adresvelden of historische pogingslots niet met PATCH wijzigen. Gebruik [gebeurtenissen en pogingen](/gebeurtenissen/) voor voortgang en nieuwe bezorguitkomsten. ## Veelgemaakte invoerfouten | Invoer | Waarom gaat het mis? | Correctie | | --- | --- | --- | | `"parcel_weight":"1200"` | Een getal als tekst. | Gebruik `1200`. | | `"number":16` | Huisnummer is juist een tekstveld. | Gebruik `"16"`. | | Alleen `time_slot_period_start` | Het einde ontbreekt. | Stuur beide grenzen. | | `delivery_status: 4` zonder tijd | Afgeleverd vereist een aflevertijd. | Voeg `time_delivered` toe. | | `time_delivered` bij status 2 | Een onderwegstatus mag geen aflevertijd hebben. | Laat de aflevertijd weg. | | Eén historisch slot en `delivery_attempts: 3` | Teller klopt niet met de ingevoerde slots. | Laat teller weg of gebruik 1. | | `parcel_id` of `entry_id` in de body | Geen toegestane invoervelden. | Gebruik het ontvangen `entry_id` alleen in vervolgpaden. | | Niet geregistreerde `courier` | De vervoerder hoort nog niet bij je organisatie. | Gebruik een bestaande code of laat registratie regelen. |