# Uniorder: één API voor elke zending

Uniorder is één set endpoints waarmee een integratie elke zending van het account offreert, aanmaakt, print, volgt en annuleert, ongeacht hoe de zending wordt uitgevoerd. Eén offerte geeft de bezorging door het bedrijf zelf terug en, op verzoek, elke labeldienst van een vervoerder van het account, elk met een `rate_id`. De order wordt aangemaakt door de gekozen `rate_id` terug te sturen; niets anders in het verzoek selecteert de dienst.

## 1. Wat u kunt bouwen

- **Een checkout die alle verzendopties tegelijk aanbiedt.** De klant voert een adres in, de checkout roept één endpoint aan en de pagina toont lokale bezorging naast UPS, Canada Post en elke andere vervoerder die het account gebruikt, elk met de prijs.
- **Een koppeling met een ordermanagementsysteem of ERP met één codepad.** Orders uit elk kanaal doorlopen dezelfde aanroepen voor aanmaken, lezen, label, tracking en annuleren. De koppeling heeft geen aparte logica nodig voor lokale bezorging en voor vervoerderslabels.
- **Bulkverwerking gedurende de nacht.** Tot 500 zendingen worden in één taak in de wachtrij geofferd of aangemaakt, en de resultaten worden via de taak-id teruggelezen.
- **Een klantenservicescherm.** Een medewerker zoekt een order op, print het label opnieuw, leest de trackingtijdlijn en annuleert de order, met dezelfde vier aanroepen voor elke order.

## 2. Wat Uniorder voor u doet

| Zonder Uniorder | Met Uniorder |
|---|---|
| Eén API voor lokale bezorgorders en een andere voor vervoerderslabels, elk met eigen velden en responses | Eén vorm van verzoek (`from_*`, `to_*`, `packages`) en één vorm van response voor elke dienst |
| De integratie bepaalt welke vervoerders-API wordt aangeroepen | De offerte toont elke dienst; de `rate_id` van het gekozen tarief beslist |
| Aparte endpoints voor label, tracking en annuleren per dienst | `GET /label`, `GET /tracking` en `POST /cancel` werken voor elke order |
| Batchgewijs aanmaken alleen beschikbaar voor lokale bezorging | Batchofferte en batchgewijs aanmaken voor elke dienst, synchroon of in de wachtrij |

Uniorder vervangt de bestaande endpoints niet; deze blijven beschikbaar en ongewijzigd. Het is het aanbevolen toegangspunt voor een nieuwe integratie.

## 3. Overzicht van de endpoints, in de volgorde waarin een integratie ze gebruikt

| Stap | Doel | REST | GraphQL |
|---|---|---|---|
| 1 | Een toegangstoken verkrijgen | `POST /api/v1/user/login` | `userLogin` |
| 2 | Elke dienst offreren | `POST /api/v1/uniorder/rate` | `uniorderRate` |
| 3 | De order aanmaken tegen het gekozen tarief | `POST /api/v1/uniorder` | `uniorderCreate` |
| 4 | Het label printen | `GET /api/v1/uniorder/{orderId}/label` | `uniorderLabel` |
| 5 | De order lezen | `GET /api/v1/uniorder/{orderId}` | `uniorder` |
| 6 | De order volgen | `GET /api/v1/uniorder/{orderId}/tracking` | `uniorderTracking` |
| 7 | De order annuleren | `POST /api/v1/uniorder/{orderId}/cancel` | `uniorderCancel` |
| — | Een label kopen dat bij het aanmaken niet kon worden gekocht | `POST /api/v1/uniorder/{orderId}/label` | `uniorderPurchaseLabel` |
| — | Tot 20 rijen tegelijk offreren of aanmaken | `POST /api/v1/uniorder/rate/batch`, `POST /api/v1/uniorder/batch` | `uniorderRateBatch`, `uniorderCreateBatch` |
| — | Tot 500 rijen in de wachtrij plaatsen | `POST /api/v1/uniorder/rate/batch-async`, `POST /api/v1/uniorder/batch-async`, `GET /api/v1/uniorder/jobs/{jobId}` | `uniorderRateBatchAsync`, `uniorderCreateBatchAsync`, `uniorderJob` |

[REST-handboek](/api/documentation#/paths/v1-uniorder-rate/post) · [GraphQL-handboek](/api/graphql/documentation#/orders/uniorderRate)

De verzoeken, responses en controles per stap staan in het playbook **Offerte en order in één stroom**.

## 4. Voorbeeld: een checkout die elke optie aanbiedt

Een bloemist in Montreal verkoopt online. Bij de checkout offreert hij het pakket één keer, inclusief labelvervoerders:

```bash
curl -X POST https://YOUR_HOST/api/v1/uniorder/rate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "from_name": "Fleurs du Plateau", "from_telephone": "5145550100",
    "from_address": "4500 Rue Saint-Denis", "from_city": "Montreal", "from_province": "QC", "from_country": "CA", "from_postcode": "H2J2L3",
    "to_name": "Jane Recipient", "to_telephone": "5145550199",
    "to_address": "6841 Rue Saint-Denis", "to_city": "Montreal", "to_province": "QC", "to_country": "CA", "to_postcode": "H2S2S3",
    "quote_labels": true,
    "packages": [{ "weight": 1.2, "weight_unit": 2, "length": 40, "width": 25, "height": 25, "dimension_unit": 2 }]
  }'
```

Het antwoord bevat één `self_delivery`-tarief en één `label_service`-tarief per dienst van een vervoerder. De checkout toont ze als opties; de klant kiest lokale bezorging op dezelfde dag. De order wordt aangemaakt met de `rate_id` van dat tarief en dezelfde adressen en pakketten:

```bash
curl -X POST https://YOUR_HOST/api/v1/uniorder \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: checkout-10045" \
  -d '{
    "rate_id": "eyJpdiI6Ik1rT2Z...",
    "ref": "WEB-10045",
    "from_name": "Fleurs du Plateau", "from_telephone": "5145550100",
    "from_address": "4500 Rue Saint-Denis", "from_city": "Montreal", "from_province": "QC", "from_country": "CA", "from_postcode": "H2J2L3",
    "to_name": "Jane Recipient", "to_telephone": "5145550199",
    "to_address": "6841 Rue Saint-Denis", "to_city": "Montreal", "to_province": "QC", "to_country": "CA", "to_postcode": "H2S2S3",
    "packages": [{ "weight": 1.2, "weight_unit": 2, "length": 40, "width": 25, "height": 25, "dimension_unit": 2 }]
  }'
```

De response geeft de order-`id` en de trackingnummers terug. De winkel print het label met `GET /api/v1/uniorder/{orderId}/label` en toont de tijdlijn van `GET /api/v1/uniorder/{orderId}/tracking` op de orderpagina van de klant.

## 5. Voorbeeld: een ERP dat elke nacht verzendt

Een ERP exporteert om 22:00 de orders van de dag. Het stuurt de adressen naar `POST /api/v1/uniorder/rate/batch-async`, leest de taak tot `status` gelijk is aan `done`, kiest voor elke rij een tarief volgens de eigen regels en stuurt de gekozen rijen naar `POST /api/v1/uniorder/batch-async`. Elk resultaat bevat de `reference` van de rij, zodat het ERP elk resultaat aan de eigen orderregel koppelt. Een `rate_id` is 30 minuten geldig; de aanmaaktaak wordt daarom kort na het voltooien van de offertetaak verzonden.

## 6. Voorbeeld: een klantenservicescherm

Bij een klantgesprek roept het scherm van de medewerker `GET /api/v1/uniorder/{orderId}` aan voor de status en de adressen, `GET /api/v1/uniorder/{orderId}/tracking` voor de tijdlijn en het afleverbewijs, en `POST /api/v1/uniorder/{orderId}/cancel` wanneer de klant annuleert. Dezelfde aanroepen gelden voor een bezorgorder en voor een labelorder; het veld `type` onderscheidt ze.

```bash
curl https://YOUR_HOST/api/v1/uniorder/123456/tracking \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

Dezelfde order via GraphQL ([GraphQL-handboek](/api/graphql/documentation#/orders/uniorder)):

```graphql
query {
  uniorder(order_id: 123456)
}
```

## 7. Regels om rekening mee te houden

- **De `rate_id` bepaalt de dienst.** Deze is 30 minuten geldig en alleen voor het account dat de offerte heeft aangevraagd.
- **De order wordt geprijsd bij het aanmaken.** `shipping_price` is de in rekening gebrachte prijs; `quoted_price` is de prijs van de offerte. De twee kunnen verschillen.
- **Een labelorder gaat nooit verloren.** Wanneer het label bij het aanmaken niet kan worden gekocht, blijft de order bewaard en is het antwoord `LABEL_PURCHASE_FAILED` met de order-`id`; het label wordt later gekocht met `POST /api/v1/uniorder/{orderId}/label`.
- **Herhaalde verzoeken zijn veilig.** Stuur bij elke aanmaakaanroep een `Idempotency-Key`-header mee; het annuleren van een order die al geannuleerd is, geeft `already_cancelled` `true` terug.
- **Statussen zijn uniform.** Een bezorgorder meldt `pending`, `in_transit`, `out_for_delivery`, `delivered`, `exception` of `cancelled`; een labelorder meldt `label_pending`, `label_purchased` of `cancelled`, en de tracking van de vervoerder meldt waar het pakket zich bevindt.

## 8. Checklist voor livegang

- [ ] Het account heeft API-toestemming en het token wordt op de server bewaard, niet in een browser.
- [ ] Offertes worden aangevraagd met volledige adressen van afzender en ontvanger.
- [ ] Orders worden binnen 30 minuten na de offerte aangemaakt, met een `Idempotency-Key`.
- [ ] `LABEL_PURCHASE_FAILED` wordt afgehandeld door het label later te kopen, nooit door de order opnieuw aan te maken.
- [ ] Tracking wordt gelezen via `GET /api/v1/uniorder/{orderId}/tracking` of ontvangen via webhooks.
- [ ] Bij annuleren worden `ORDER_STATUS_NOT_CANCELLABLE` en `ORDER_CANCEL_REFUSED` afgehandeld door de order ongewijzigd te laten.
