# Conventions

> Forme de la réponse, pagination, argent, pourcentages, dates, identifiants, mises à jour, limites de taux et versioning.

## Demandes et réponses

- Les demandes et les réponses sont au format JSON. Envoyez `Content-Type: application/json` avec un corps.
- Les noms de champ sont en camelCase : `listPrice`, `accountCode`, `nextOffset`.
- Les paramètres de requête sont des chaînes simples : `?limit=20&status=new`.

Toute réponse réussie est un objet avec `data` :

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

| Champ        | Quand il est présent                                    | Signification                                                                          |
| ------------ | ------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `data`       | Toujours                                                | L'objet ou la liste que vous avez demandée.                                            |
| `nextOffset` | Listes paginées                                         | Utilisez-la comme `offset` pour obtenir la page suivante. `null` sur la dernière page. |
| `currency`   | Réponses avec des prix en devise de l'espace de travail | Code ISO 4217 pour chaque champ monétaire dans `data`.                                 |

Les commandes portent leur propre champ `currency`, fixé lors de la commande.

## Pagination

Les listes prennent `limit` et `offset` :

| Paramètre | Défaut     | Maximum                                      |
| --------- | ---------- | -------------------------------------------- |
| `limit`   | le maximum | 50 pour les produits, 100 pour tout le reste |
| `offset`  | 0          | aucun                                        |

Continuez à demander avec `offset=nextOffset` jusqu'à ce que `nextOffset` soit `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
}
```

Le `/products`, `nextOffset` est défini chaque fois qu'une page est pleine. Si la taille du catalogue est un multiple exact de `limit`, la dernière demande renvoie une liste vide `data` avec `nextOffset: null`.

Certaines listes sont limitées. `GET /orders` couvre les 200 dernières commandes correspondantes, et `GET /price-changes` les 100 dernières.

## Argent

L'argent est un **entier en unités mineures** de la devise, donc il n'y a pas d'erreur d'arrondi :

| Valeur | Devise | Signifie                   |
| ------ | ------ | -------------------------- |
| `1234` | GBP    | £12.34                     |
| `1850` | EUR    | €18.50                     |
| `500`  | JPY    | ¥500 (pas d'unité mineure) |

Divisez par 10 à la puissance du nombre de décimales de la devise pour l'afficher. En JavaScript, `Intl.NumberFormat` le fait pour vous :

```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"
```

Les prix sont tels que le fournisseur les définit dans sa liste de prix. Si cela inclut la taxe dépend de l'option `taxLabel` de l'espace de travail sur [`GET /workspace`](/docs/api/workspace) : par exemple « hors TVA » (avant la taxe), « inclus GST » (avec la taxe), ou vide lorsque le fournisseur n'affiche pas de mention de taxe.

## Pourcentages

Les pourcentages sont **des points de base** : centièmes de pourcentage, sous forme d'entiers.

| Valeur  | Signifie |
| ------- | -------- |
| `1250`  | 12,5 %   |
| `3500`  | 35 %     |
| `10000` | 100 %    |

## Dates

Les dates sont des chaînes au format ISO 8601 en UTC, comme `2026-09-27T08:15:00.000Z`. La zone horaire propre à l'espace de travail est `timezone` sur [`GET /workspace`](/docs/api/workspace).

## Identifiants

Les identifiants sont des chaînes UUID. Là où un point de terminaison prend un **client**, vous pouvez passer l'identifiant du client ou son code de compte (insensible à la casse), selon ce que votre système possède :

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

## Création et mise à jour (PUT)

`PUT /products` et `PUT /customers` créent ou mettent à jour en une seule appel, jusqu'à 100 lignes à la fois :

- Les produits sont identifiés par **SKU** (insensible à la casse).
- Les clients sont identifiés par **code de compte**, sinon par **email**, sinon par **nom**.

Les deux sont sans danger pour être répétés. Envoyer les mêmes lignes deux fois donne le même résultat ; le second appel ne signale que les mettre à jour.

Un `PUT` écrit les champs que vous envoyez. Certains champs de produit optionnels sont effacés lorsque vous les omettez, d'autres sont conservés. Chaque page d'endpoint liste lesquels.

## Limites de débit

Chaque clé peut effectuer **120 requêtes par minute**. Au-delà, vous obtenez `429 rate_limited` avec `Retry-After: 60`. Attendez et réessayez. [Erreurs](/docs/api/errors#retrying) dispose d'un assistant de réessai.

Effectuez des écritures par lot là où c'est possible : un `PUT` avec 100 produits compte comme une seule requête.

## Versioning

La version est dans le chemin : `/api/v1`.

- Dans v1, nous n’apportons que des changements **additifs** : nouveaux points de terminaison, nouveaux paramètres optionnels, nouveaux champs dans les réponses. Écrivez votre code pour ignorer les champs qu’il ne connaît pas.
- Un changement rétrograde entraînerait une nouvelle version (`/api/v2`), avec v1 qui continue à fonctionner et le changement annoncé dans le [changelog](/docs/changelog).
