Aller au contenu

#API HTTP

Le CLI n’a aucun privilège : il appelle la même API que n’importe quel script. Cette section documente cette API — ses deux modes d’authentification, ses quatre familles de routes, et chacun de ses codes d’erreur. Tout ce que node scripts/theme.mjs pousser sait faire, une boucle curl de six lignes sait le faire aussi.

#L’adresse de base

Une seule, et elle n’est pas celle de cette page :

L’origine de l’API
https://cms.webcosa.com
Danger

Sur dev.webcosa.com, /api/themes désigne la page qui documente la route, pas la route. Les deux vivent dans la même application Next et ne se distinguent que par le nom d’hôte. Tout exemple de cette section porte donc l’adresse absolue du CMS : un chemin relatif copié depuis ici irait chercher de la documentation et rendrait du HTML.

#Un premier appel

Il faut une clé d’API, créée depuis le CMS — Réglages → Clés d’API. Elle commence par wc_ et ne s’affiche qu’une fois. Voir Authentification pour ce que la clé désigne exactement, et Clés pour la créer par l’API plutôt qu’à la souris.

curl -s https://cms.webcosa.com/api/themes \-H "Authorization: Bearer $WEBCOSA_CLE"{ "site": { "id": "9f2c…", "slug": "atelier-durand", "nom": "Atelier Durand" }, "themes": [ { "id": "11111111-1111-4111-8111-111111111111", "nom": "Origo", "version": "1.0.0", "role": "publie", "origine": "origo", "maj_le": "2026-08-05T09:12:44.108Z" }, { "id": "22222222-2222-4222-8222-222222222222", "nom": "Origo (copie)", "version": "1.0.0", "role": "brouillon", "origine": "origo", "maj_le": "2026-08-04T17:02:11.740Z" } ] }

La réponse contient le site résolu en plus des thèmes, et ce n’est pas du confort : sans en-tête de désignation, la clé retient le premier site accessible à son porteur, et cet ordre bouge le jour où un site s’ajoute au compte. Le champ site dit sur quoi on vient de travailler.

#Ce que l’API permet

En un tableau, les neuf opérations existantes :

MéthodeCheminCe qu’elle faitPortée exigée
GET/api/themesLes thèmes du site, et le site résolulecture
POST/api/themesInstaller un thème du catalogue, ou dupliquerthemes
PATCH/api/themesRenommer, publierthemes
DELETE/api/themes?id=Retirer un thème brouillonthemes
GET/api/themes/<id>/fichiersL’arborescence, ou un fichierlecture
PUT/api/themes/<id>/fichiersÉcrire un fichierthemes
DELETE/api/themes/<id>/fichiers?chemin=Supprimer un fichierthemes
GET/api/themes/<id>/exportLe thème entier, en un JSONlecture
POST/api/themes/importerCréer un thème depuis une archivethemes

Les trois opérations sur /api/cles sont à part : elles refusent les clés d’API et exigent une session de navigateur. La raison tient en une phrase — une clé volée ne doit pas pouvoir s’en fabriquer d’autres et survivre à sa propre révocation. Elle est développée dans Clés.

#Ce que l’API ne permet pas

L’application expose une cinquantaine de routes sous /api/ : pages, articles, contacts, menus, domaines, facturation. Une seule famille est ouverte aux clés d’API, /api/themes et ses sous-chemins ; le proxy la nomme explicitement et refuse toutes les autres avec un 401 avant même que la route soit atteinte.

Ce n’est pas un oubli. Une route ajoutée demain sous /api/ reste fermée par défaut, et l’ouvrir est un geste délibéré dans une liste courte qu’on relit — au lieu d’être l’effet de bord du chemin qu’on lui a choisi.

Trois autres absences, à connaître avant de concevoir une intégration :

  • Pas de webhooks. Rien ne te prévient qu’un thème a changé. Le CLI reconstruit l’information en comparant les empreintes de l’arborescence.
  • Pas de pagination. Les listes rendent tout. Un site a quelques thèmes, un thème a au plus 120 fichiers : ce plafond-là est le seul qui compte.
  • Pas de rendu. Aucune route ne rend du Liquid à la demande. L’aperçu d’un thème, c’est le site lui-même — voir La boucle locale.

#Le format

JSON dans les deux sens, UTF-8. Une requête qui porte un corps déclare Content-Type: application/json ; un corps illisible n’est pas une exception, il vaut 400 invalid_input comme un corps absent.

Les identifiants sont des uuid. Ceux des thèmes voyagent dans le chemin (/api/themes/<id>/fichiers) ou en paramètre de requête (?id=), jamais dans le corps — sauf PATCH /api/themes, qui prend le sien dans le corps parce qu’il l’accompagne des champs à modifier.

Une erreur est un objet à un ou deux champs :

Réponse d’erreurjson
{
  "error": "theme_publie",
  "message": "Ce thème est celui que voient tes visiteurs. Publie-en un autre avant de le retirer."
}

error est un code stable, fait pour être comparé en code. message est une phrase rédigée en français, faite pour être affichée — elle n’est présente que sur les refus où le code seul ne suffirait pas à savoir quoi faire. Le catalogue complet est dans Codes d’erreur.

Astuce

Traitez error et jamais le statut seul. 400 couvre à lui seul un chemin hors dossier, une extension refusée, une erreur de syntaxe Liquid et un plafond de fichiers atteint — quatre situations qui n’appellent pas la même réaction.

#Ce qui est compté

Un compteur, un seul, et il ne porte pas sur les appels réussis : dix clés invalides par heure, comptées par empreinte du secret présenté. Au-delà, 429 trop_de_tentatives. Il n’existe aucune limite de débit sur les requêtes authentifiées — voir Codes d’erreur pour ce que ce compteur arrête réellement, et ce qu’il n’arrête pas.