Skip to content

Conventions REST — Public API

Méthodes HTTP

VerbeSémantique StormeoOS
GET /resourceListe paginée ({ data, total, limit, offset })
GET /resource/:idLecture d'une ressource (objet brut)
POST /resourceCréation — renvoie 201 Created + ressource
PATCH /resource/:idMise à jour partielle (à privilégier)
PUT /resource/:idRemplacement complet (même payload qu'un POST)
DELETE /resource/:idSuppression — renvoie 204 No Content

Règle interne R-005 : PATCH pour les mises à jour partielles, PUT réservé aux remplacements complets. Si un seul des deux est documenté pour une ressource, utilisez-le.

Pagination

Toutes les routes de listing acceptent :

Query paramTypeDéfautMax
limitint50100
offsetint0(illimité)

Les valeurs hors bornes sont clampées silencieusement (pas d'erreur). Pour parcourir un grand jeu :

GET /clients?limit=100&offset=0
GET /clients?limit=100&offset=100
GET /clients?limit=100&offset=200
...

Le champ total de la réponse permet de calculer le nombre de pages.

Format JSON

Encodage

  • Charset : UTF-8
  • Content-Type requête : application/json pour POST/PATCH/PUT (sinon 415 Unsupported Media Type)
  • Content-Type réponse : application/json; charset=utf-8
  • Taille max body : 10 MB (filet global Express)

Dates

Toutes les dates sont au format ISO 8601 UTC :

json
{
  "createdAt": "2026-04-27T10:30:00.000Z",
  "dueDate": "2026-05-15T00:00:00.000Z"
}

Acceptées en entrée :

  • ISO 8601 complet : 2026-04-27T10:30:00.000Z
  • Date seule : 2026-04-27 (interprétée comme 00:00:00 UTC)

Identifiants

  • Numériques (int) pour la plupart des ressources : id: 42
  • Chaînes (uuid ou similaire) pour : userId, certains tokens, traceId
  • Pas de slugs dans les identifiants externes (les slugs sont des champs de display)

Booléens

true / false JSON natifs. Pas de "yes", "1", 0.

Valeurs nulles

null est utilisé pour les champs explicitement vides. undefined (champ absent) signifie "ne pas modifier" en PATCH.

Filtrage

Certaines routes acceptent des filtres en query string. Exemples :

GET /websites?clientId=42
GET /tickets?status=open&priority=high
GET /invoices?clientId=42&since=2026-01-01

Le filtrage agencyId est toujours implicite et obligatoire (lié à la clé API). Aucun moyen de le bypasser.

Tri

Quand supporté, via le query param sort :

GET /clients?sort=name             # ascendant
GET /clients?sort=-createdAt       # descendant (préfixe -)

Les champs triables sont documentés sur chaque endpoint.

Validation Zod

Tous les bodies POST/PATCH/PUT sont validés avec Zod (safeParse). Les schémas dérivent de shared/schema.ts (table source de vérité), avec :

  • omit des champs système (id, createdAt, updatedAt, agencyId)
  • partial() pour les PATCH
  • safeParse (jamais parse) pour récupérer les erreurs sans throw

Les erreurs Zod sont retournées sous details: parsed.error.flatten(). Voir errors.md.

Multi-tenant

L'agencyId est résolu côté serveur depuis la clé API. Vous ne pouvez pas le passer en body ni le surcharger. Si vous tentez :

json
POST /clients
{ "name": "Acme", "agencyId": 999 }

Le champ agencyId est ignoré (omitted dans le schéma Zod) et écrasé par celui de votre clé.

Conséquences :

  • Toutes les listes ne renvoient que les ressources de votre agence
  • Toute lecture par ID hors agence → 404 (pas 403, pour ne pas leak l'existence)
  • Toute relation FK doit pointer vers une ressource de la même agence (vérifié côté serveur, sinon 400)

Webhooks émis (effets de bord)

Plusieurs mutations émettent un webhook si vous avez souscrit. Le format est documenté ici pour info, le détail dans ../webhooks/public-api-webhooks.md.

ActionEvent
POST /clientsclient.created
PATCH/PUT /clients/:idclient.updated
DELETE /clients/:idclient.deleted
POST /contactscontact.created
...(idem pour chaque ressource)

CORS

L'API publique n'expose pas de CORS pour les origins navigateur arbitraires. Elle est conçue pour un appel server-to-server. Si vous appelez depuis un navigateur, passez par un proxy serveur.

Versionning

  • Path versioning : /api/public/v1/*
  • Breaking changes/v2. La v1 reste supportée 12 mois après la sortie de v2.
  • Additions non-breaking (nouveaux endpoints, nouveaux champs optionnels en réponse) : OK sans bump de version.
  • Voir changelog.md pour l'historique.

User-Agent recommandé

Identifiez votre intégration dans le User-Agent — utile pour le support :

User-Agent: AcmeAgency-Sync/1.2.0 (+https://acme.fr/integrations/stormeo)

Loggé dans public_api_logs.userAgent côté serveur.

StormeoOS API