Conventions REST — Public API
Méthodes HTTP
| Verbe | Sémantique StormeoOS |
|---|---|
GET /resource | Liste paginée ({ data, total, limit, offset }) |
GET /resource/:id | Lecture d'une ressource (objet brut) |
POST /resource | Création — renvoie 201 Created + ressource |
PATCH /resource/:id | Mise à jour partielle (à privilégier) |
PUT /resource/:id | Remplacement complet (même payload qu'un POST) |
DELETE /resource/:id | Suppression — 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 param | Type | Défaut | Max |
|---|---|---|---|
limit | int | 50 | 100 |
offset | int | 0 | (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/jsonpour POST/PATCH/PUT (sinon415 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 :
{
"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 comme00:00:00 UTC)
Identifiants
- Numériques (
int) pour la plupart des ressources :id: 42 - Chaînes (
uuidou 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-01Le 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 :
omitdes champs système (id,createdAt,updatedAt,agencyId)partial()pour les PATCHsafeParse(jamaisparse) 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 :
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(pas403, 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.
| Action | Event |
|---|---|
POST /clients | client.created |
PATCH/PUT /clients/:id | client.updated |
DELETE /clients/:id | client.deleted |
POST /contacts | contact.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. Lav1reste supportée 12 mois après la sortie dev2. - 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.