# Conventies

> Antwoordstructuur, paginering, geld, percentages, datums, ids, upserts, snelheidsbeperkingen en versiebeheer.

## Aanvragen en antwoorden

- Aanvragen en antwoorden zijn JSON. Stuur `Content-Type: application/json` met een body.
- Veldnamen zijn camelCase: `listPrice`, `accountCode`, `nextOffset`.
- Query parameters zijn gewone strings: `?limit=20&status=new`.

Elk succesvol antwoord is een object met `data`:

```json
{
  "data": [],
  "nextOffset": 50,
  "currency": "GBP"
}
```

| Veld         | Wanneer het er is                             | Betekenis                                                                                 |
| ------------ | --------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `data`       | Altijd                                        | Het object of lijst die je hebt gevraagd.                                                 |
| `nextOffset` | Lijsten die pagineren                         | Geef het als `offset` door om de volgende pagina te krijgen. `null` op de laatste pagina. |
| `currency`   | Antwoorden met prijzen in de werkruimtevaluta | ISO 4217 code voor elk geldveld in `data`.                                                |

Bestellingen hebben hun eigen `currency` veld, vastgelegd op het moment dat de bestelling is geplaatst.

## Pagineren

Lijsten nemen `limit` en `offset`:

| Parameter | Standaard   | Maximum                                  |
| --------- | ----------- | ---------------------------------------- |
| `limit`   | het maximum | 50 voor producten, 100 voor alles anders |
| `offset`  | 0           | geen                                     |

Blijf aanvragen met `offset=nextOffset` tot `nextOffset` `null`:

```js
let offset = 0
const all = []
while (offset !== null) {
  const res = await fetch(
    `https://tradecatalog.app/api/v1/products?limit=50&offset=${offset}`,
    {
      headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
    },
  )
  const page = await res.json()
  all.push(...page.data)
  offset = page.nextOffset
}
```

Op `/products` wordt `nextOffset` ingesteld wanneer een pagina vol is. Als de catalogusgrootte een exacte veelvoud is van `limit`, retourneert de laatste aanvraag een lege `data`-lijst met `nextOffset: null`.

Enkele lijsten zijn beperkt. `GET /orders` omvat de 200 meest recente overeenkomende bestellingen, en `GET /price-changes` de 100 meest recente.

## Geld

Geld is een **geheel getal in mindere eenheden** van de valuta, zodat er geen afrondingsfout is:

| Waarde | Valuta | Betekent                    |
| ------ | ------ | --------------------------- |
| `1234` | GBP    | £12.34                      |
| `1850` | EUR    | €18.50                      |
| `500`  | JPY    | ¥500 (geen mindere eenheid) |

Deel door 10 tot de macht van het aantal decimalen van de valuta om het weer te geven. In JavaScript doet `Intl.NumberFormat` dat voor je:

```js
const format = (minor, currency) => {
  const f = new Intl.NumberFormat('en-GB', { style: 'currency', currency })
  const digits = f.resolvedOptions().maximumFractionDigits
  return f.format(minor / 10 ** digits)
}
format(1234, 'GBP') // "£12.34"
```

De prijzen zijn zoals de leverancier ze in hun prijslijst heeft ingesteld. Of dat belastingen bevat, is de instelling van de werkruimte `taxLabel` op [`GET /workspace`](/docs/api/workspace): bijvoorbeeld “ex BTW” (voor belasting), “inc BTW” (inclusief belasting), of leeg wanneer de leverancier geen belastingtekst toont.

## Percentages

Percentages zijn **basispunten**: honderdsten van een procent, als gehele getallen.

| Waarde  | Betekent |
| ------- | -------- |
| `1250`  | 12,5%    |
| `3500`  | 35%      |
| `10000` | 100%     |

## Datums

Datums zijn ISO 8601-strings in UTC, zoals `2026-09-27T08:15:00.000Z`. De tijdzone van de werkruimte is `timezone` op [`GET /workspace`](/docs/api/workspace).

## Ids

Ids zijn UUID-strings. Waar een eindpunt een **klant** verwacht, kunt u het klant-id of hun accountcode (gevoelig voor hoofdletters) doorgeven, afhankelijk van wat uw systeem heeft:

```bash
curl "https://tradecatalog.app/api/v1/prices?customer=OAK01&sku=HX-8510" \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
```

## Aanmaken en bijwerken (PUT)

`PUT /products` en `PUT /customers` maken of bijwerken in één oproep, maximaal 100 rijen tegelijk:

- Producten worden geïdentificeerd aan de hand van **SKU** (gevoelig voor hoofdletters).
- Klanten worden geïdentificeerd aan de hand van **accountcode**, anders **e-mail**, anders **naam**.

Beide zijn veilig te herhalen. Hetzelfde rijen meesturen twee keer geeft hetzelfde resultaat; de tweede oproep rapporteert ze alleen als bijgewerkt.

Een `PUT` schrijft de velden die u stuurt. Sommige optionele productvelden worden gewist wanneer u ze weglaat, andere blijven behouden. Elke eindpuntpagina geeft aan welke velden dat zijn.

## Ratenbeperkingen

Elke sleutel kan **120 aanvragen per minuut** maken. Boven dat aantal krijgt u `429 rate_limited` met `Retry-After: 60`. Wacht en probeer het opnieuw. [Fouten](/docs/api/errors#retrying) bevat een herhaalhulpmiddel.

Batch-schrijven waar mogelijk: één `PUT` met 100 producten telt als één aanvraag.

## Versiebeheer

De versie is in de pad: `/api/v1`.

- Binnen v1 maken we alleen **additieve** wijzigingen: nieuwe eindpunten, nieuwe optionele parameters, nieuwe velden in antwoorden. Schrijf je code zodat deze onbekende velden negeert.
- Een brekende wijziging krijgt een nieuwe versie (`/api/v2`), met v1 die nog draait en de wijziging aangekondigd in de [changelog](/docs/changelog).
