# Gids voor integratie-updates

Elke wijziging hieronder is strikt aanvullend: bestaande endpoints, responsvelden, HTTP-statuscodes, webhook-payloadbytes en de legacy `Signature`-header blijven ongewijzigd. Integraties die niets doen, blijven exact zo werken als voorheen. Neem elke functie onafhankelijk en in willekeurige volgorde in gebruik.

Live beschikbaar op https://api.superlabel.ca/api/documentation/integration-updates?lang=nl (`?download=1` om op te slaan). Bijbehorende documenten: OpenAPI (https://api.superlabel.ca/api/documentation?lang=nl), Postman-collectie (https://api.superlabel.ca/api/documentation/postman-collection), statuswoordenboek & flow (https://api.superlabel.ca/api/documentation/order-status-flow?lang=nl).

---

## 1. Annuleren op trackingnummer (idempotent)

`POST https://api.superlabel.ca/api/v1/orders/cancel` — geef precies één van `order_id`, `tracking_number`, `external_tracking_number` op.

```json
POST /api/v1/orders/cancel
Authorization: Bearer <token>
{ "external_tracking_number": "WP1234567890" }
```

- Succes: `200 {"result":true,"id":123456,"already_cancelled":false,...}`
- Al geannuleerd (veilig opnieuw te proberen): `200` met `"already_cancelled": true`
- Nummer komt overeen met meerdere actieve orders: `409` met `"matched_order_ids": [...]` — probeer opnieuw met `order_id`.
- De legacy `GET /v1/orders/{orderId}/cancel` is ongewijzigd.

## 2. Incrementele reconciliatiefeeds

Doorloop alles wat binnen een tijdvenster is gewijzigd; dankzij cursorpaginering mist of dubbeltelt u nooit een record.

- `GET https://api.superlabel.ca/api/v1/orders-reconciliation?updated_from=&updated_to=&per_page=&cursor=`
- `GET https://api.superlabel.ca/api/v1/tracking-events/reconciliation?...`

`updated_from` / `updated_to` accepteren unix-timestamps of `Y-m-d H:i:s`-strings in de tijdzone van het platform. Volg `next_cursor` totdat `has_more` false is; vergelijk met de `*_timestamp` unix-velden, nooit met lokale-tijdstrings. Tracking-event-items bevatten `occurred_at` / `occurred_timestamp` (zakelijk tijdstip van optreden; gelijk aan de aanmaaktijd voor historische rijen).

## 3. Machineleesbare foutcodes & request-tracing

Veelvoorkomende foutresponses bij aanmaken/annuleren bevatten nu een stabiel `code`-veld naast de ongewijzigde `message`:
`VALIDATION_FAILED`, `MISSING_IDENTIFIER`, `ORDER_NOT_FOUND`,
`ORDER_CANCEL_UNAUTHORIZED`, `ORDER_STATUS_NOT_CANCELLABLE`,
`ORDER_CANCEL_BLOCKED_THIRD_PARTY`, `LABEL_CANCEL_FAILED`,
`DUPLICATE_TRACKING_NUMBER`, `MULTIPLE_ORDERS_MATCHED`.
Codes zijn stabiele identifiers — er kunnen nieuwe bijkomen, maar bestaande veranderen nooit van betekenis. Elke API-respons stuurt bovendien `X-Request-ID` terug; vermeld deze wanneer u een probleem meldt.

## 4. Verbeteringen in duplicaatdetectie

Met `auto_deduplication=1` bevatten de `exist_package_ref` / `exist_external_tracking_number`-vermeldingen van een geblokkeerde create nu ook de bestaande `order_id` en `order_ref`, en bevat de envelope `"duplicate": true`. Optionele strikte modus: stuur `strict_duplicate_check=1` om HTTP `409` te ontvangen in plaats van de legacy `200` + `result:false` (laat de vlag weg om het legacy-gedrag te behouden).

## 5. Opties voor batchaanmaak

- `per_order_transaction: 1` (op het hoogste niveau van de batch-body): elke order wordt onafhankelijk gecommit — één mislukking rolt de andere orders niet langer terug. Als de verwerking voortijdig wordt afgebroken, ontvangt u nog steeds rijen voor alles wat is voltooid, plus een afsluitende rij `{"result":false,"batch_aborted":true}`.
- Batches met meer dan 100 orders krijgen een adviserende `X-Batch-Size-Warning`-responsheader; gebruik bij voorkeur `POST /v1/client/batchOrderCreateAsync` voor grote volumes.

## 6. Synchronisatie van proof-of-delivery-bestanden

De `proofs[]`-vermeldingen in tracking-responses bevatten nu ook:

```json
{ "url": "...", "full_url": "...", "type": 2,
  "file_id": 234567, "uploaded_at": "2026-07-20 14:30:05",
  "uploaded_timestamp": 1784745005,
  "signed_url": "https://.../files/pod/234567?expires=...&signature=...",
  "signed_url_expires_at": 1784745005 }
```

Gebruik `file_id` als uw sleutel voor deduplicatie / incrementele synchronisatie. `signed_url` is een verlopende link (7 dagen) — vraag na afloop de tracking opnieuw op voor een nieuwe. De permanente `url` / `full_url`-links zijn ongewijzigd.

## 7. Webhooks: v2-handtekeningheaders (alle afleveringen)

Elke webhook stuurt nu OOK, naast de ongewijzigde legacy `Signature`-header:

| Header | Betekenis |
|---|---|
| `X-Webhook-Event-Id` | UUID, stabiel over retries heen — gebruik voor deduplicatie |
| `X-Webhook-Timestamp` | unix-seconden bij de eerste verzending |
| `X-Webhook-Signature-V2` | HMAC-SHA256 van `"<timestamp>.<raw body>"` |

Verificatie (in elke taal): `expected = HMAC_SHA256(timestamp + "." + raw_body, your_secret)`; vergelijk in constante tijd met de header. De timestamp wordt bij de eerste verzending vastgelegd en bij retries hergebruikt (retries kunnen tot ~3 uur doorlopen), dus combineer een ruim tolerantievenster met deduplicatie op `X-Webhook-Event-Id` in plaats van een strakke replay-grens. Uw bestaande legacy-`Signature`-verificatie blijft ongewijzigd werken — neem v2 in gebruik wanneer u maar wilt. Test beide handtekeningen tegen uw endpoint vanaf de webhook-gidspagina (`/webhooks-guide`).

## 8. Webhooks: opt-in payload-envelope

Stel `webhook_payload_envelope = 1` in in uw webhookinstellingen om, binnen de JSON-body van object-vormige events, het volgende te ontvangen: `event_id`, `event_time` (ISO8601 met offset), `event_timestamp` (unix), plus — waar van toepassing — `is_correction: true` (een eerder bezorgde/overgedragen order is opnieuw in de flow gekomen) en `occurred_at` / `occurred_timestamp` op tracking-events. Zonder de vlag blijft uw payload byte-voor-byte identiek aan voorheen. Lijst-vormige payloads (`order.create_async`) worden nooit aangepast.

## 9. Nieuwe webhook-events (opt-in-URL's)

- `pod.files_updated` — een bezorgfoto/handtekening is achteraf toegevoegd of verwijderd. Configureer `pod_files_webhook_url`. Payload:
  `{"action":"added|removed","order_id":...,"order_ref":...,
  "tracking_number":[...],"external_tracking_number":[...],
  "file":{"file_id":...,"type":1|2,"note":null}, "event_id":..., ...}`
- `order.deleted` — een order is permanent verwijderd. Configureer `order_deleted_webhook_url`. Payload: `{"action":"deleted","order_id":...,
  "order_ref":...,"tracking_number":[...],"external_tracking_number":[...],
  "event_id":..., ...}`

Beide events bevatten altijd de envelope-velden en worden alleen verzonden wanneer hun URL is geconfigureerd.

## 10. Webhooks: afleveringsopties

- **Meerdere endpoints per event**: elke webhook-URL-instelling accepteert één URL (zoals voorheen), een door komma's gescheiden lijst of een JSON-array. Elk endpoint krijgt zijn eigen afleverings- en retrycyclus.
- **TLS-verificatie**: stel `webhook_verify_ssl = 1` in om uitgaande aanroepen uw certificaat te laten verifiëren. De standaard blijft uit (historisch gedrag).
- **order.created voor alle ordertypen**: stel `order_created_webhook_all_types = 1` in om het order-created-event uit te breiden naar meer dan alleen lokale-bezorgorders. De standaard behoudt de historische reikwijdte.
- **Ondertekeningsgeheimen**: nieuwe configuraties krijgen een sterk willekeurig geheim; bestaande geheimen worden nooit automatisch geroteerd.

## 11. API-tokenscopes & IP-allowlists (optioneel)

Individuele tokens kunnen worden beperkt tot `orders:read`, `orders:write` en/of `webhooks:manage`, en tot een lijst met toegestane IP-adressen. Onbeperkte tokens (de standaard, en elk bestaand token) gedragen zich exact zoals voorheen. Een beperkt token dat buiten zijn scopes/IP-adressen aanroept, ontvangt `403`. Neem contact op met support om een token te beperken.

## 12. Vervoerderpakket indienen (Smart Locker)

Externe vervoerders dienen pakketten in één batch-aanroep in bij het voorbereidingsgebied van een magazijn. Optioneel kun je een telefoon/e-mail van de ontvanger toevoegen zodat de ontvanger met een afhaalcode wordt gemeld zodra het pakket in een slimme locker is geplaatst. Ontvangervelden zijn optioneel — het endpoint blijft achterwaarts compatibel.

```json
POST https://api.superlabel.ca/api/v1/inventories/carrier-staging-stockin
Authorization: Bearer <token>
{ "carrier_id": 1, "warehouse_id": 10, "data": [
  { "carrier_reference_number": "TRK1", "ref": "REF1",
    "recipient_phone": "+14165550123", "recipient_email": "r@example.com" }
] }
```

```graphql
mutation($carrierId: Int!, $warehouseId: Int, $packages: Json) {
  submitCarrierPackages(carrierId: $carrierId, warehouseId: $warehouseId, packages: $packages)
}
```

- Authenticatie: Bearer-token zoals gebruikelijk. Klantaccounts moeten een vervoerder-uploadautorisatie hebben (vervoerder + magazijn); client-, medewerker- en partneraccounts gebruiken hun eigen magazijnbereik.
- Per pakket: member_number en/of carrier_reference_number zoals vereist door de vervoerderinstellingen; optioneel carrier_order_id, batch, ref, grid_code (alleen personeel), recipient_phone, recipient_email.
- Elke rij van de respons geeft een pickup_code; dezelfde code wordt in de afhaalmelding aan de ontvanger geleverd.

Contactgegevens van de ontvanger worden versleuteld opgeslagen en nooit teruggegeven. Wanneer telefoon en e-mail beide zijn opgegeven, worden beide kanalen gemeld; telefoon/e-mail heeft voorrang op de in-app-melding via lidkoppeling.

## Compatibiliteitsbelofte

Zie het wijzigingsbeheerbeleid: uitsluitend aanvullende responses, nieuwe endpoints naast de oude, webhook-bodies standaard bevroren, standaardwaarden die nooit veranderen, deprecaties minimaal 30 dagen vooraf aangekondigd met `Deprecation` / `Sunset`-headers. Deze garanties worden bij elke release afgedwongen door geautomatiseerde compatibiliteitstests op byte-niveau.