# API kortex.center — piloter votre flotte d'espaces Kortex par script

Pour chaque site que vous lui confiez, kortex.center met en service un
**espace Kortex** à son adresse (`<site>.kortex.center`), fait de deux
composants :

- le **dashboard** : le référentiel du site, ses contenus, le suivi des
  actions et, selon les modules, les ventes et la logistique ;
- le **superviseur** : les agents IA qui surveillent le site en continu et
  agissent dessus — SEO, GEO, contenus, avis, correctifs — sous votre
  validation.

kortex.center ne touche pas à votre site lui-même : il installe, met à jour
et surveille ces deux composants, avec preuve de vie et retour arrière
automatique à chaque version.

**Cette API vous donne la même main que la console, depuis un script ou un
agent IA :** déclarer un nouveau site et suivre son installation de bout en
bout (DNS, certificat, dashboard, superviseur), lister votre flotte et l'état
de chaque composant, redémarrer un dashboard ou un superviseur, lancer une
mise à jour, allumer les modules achetés (SEO, e-commerce, Shippingbo,
Merchant, API du site). Dix sites ou cent, le geste est le même.

Pour travailler **dans** un espace — remplir le référentiel, piloter les
agents —, chaque espace a sa propre API, activable par site : voir
[l'API des espaces](/modules/dashboard-api/).

**Activer l'API d'un espace avec cette API** : lire les modules du site
(`GET /api/admin/sites/:id`), puis les renvoyer **tous** avec
`api = {actif: true}` (`PUT /api/admin/sites/:id/modules`, portée
`pilotage`). La clé de l'espace, elle, ne peut être créée que par un
administrateur du site, connecté à son espace : une clé ne fabrique jamais
une autre clé.

**Premier administrateur d'un site** : l'email est **obligatoire** à la
déclaration (`POST /api/admin/sites {slug, domaine, email, admin_nom?}`,
sinon `400 email_requis`) ; pour en ajouter un ensuite :
`POST /api/admin/sites/:id/membres {email, nom?}` (portée `pilotage`). Un
email inconnu reçoit une invitation (lien de 7 jours pour choisir son mot de
passe, qui ne sort que par email) ; un compte existant est rattaché sans
changer son mot de passe. Réponse :
`{admin: {email, issue: "invite"|"rattache", email_envoye?, expire_le?}}`. Pas à pas, exemples `curl` et `jq` :
[Activer l'API d'un espace](/modules/guide-espace/#activer-l-api-d-un-espace).

**La techno du site du client** est obligatoire aussi à la déclaration
(champ `techno`, sinon `400 techno_requise`) et se modifie ensuite par
`PUT /api/admin/sites/:id/techno {techno}` (portée `pilotage`). Elle décide
des outils que le superviseur peut utiliser sur le site :

```json
{ "plateforme": "wordpress", "url": "https://www.exemple.fr", "constructeur": "divi5", "boutique": "woocommerce" }
{ "plateforme": "statique", "url": "https://www.exemple.fr", "forge": "gitlab", "generateur": "astro" }
{ "plateforme": "shopify", "url": "https://boutique.exemple.fr" }
```

| `plateforme` | Sous-choix obligatoires (interdits pour les autres) |
|---|---|
| `wordpress` | `constructeur` : `gutenberg`, `classique` (éditeur HTML), `divi4`, `divi5`, `elementor` ; `boutique` : `woocommerce` ou `aucune` |
| `statique` | `forge` : `gitlab`, `github`, `autre` ; `generateur` : `astro`, `hugo`, `eleventy`, `jekyll`, `html` |
| `shopify`, `wix`, `prestashop`, `autre` | aucun |

`url` : l'adresse `https://` du site du client. Ce que les agents peuvent
écrire sur le site dépend ensuite du **connecteur de publication** réglé dans
l'espace : voir [le connecteur webhook](/modules/publication-webhook/).

Cette page est publique : elle décrit la forme de l'API, jamais vos données.
Pour l'utiliser, il faut une clé, créée par un administrateur dans la
console.

## En bref

- Adresse : `https://kortex.center`
- Format : JSON en entrée et en sortie.
- Authentification : en-tête `Authorization: Bearer kx_…`.
- Une clé appartient à un compte administrateur. Elle n'agit qu'au nom de ce
  compte, et seulement dans les limites de sa **portée**.

```sh
export KORTEX_API_KEY='kx_XXXXXXXX_...'   # jamais en argument d'une commande
curl -sS https://kortex.center/api/me \
  -H "Authorization: Bearer $KORTEX_API_KEY"
```

Réponse :

```json
{ "email": "admin@exemple.fr", "name": "Admin", "role": "admin",
  "auth": "cle",
  "cle": { "prefixe": "Ab12Cd34", "portee": "pilotage", "expire_le": "2027-01-28T10:00:00.000Z" } }
```

## Obtenir une clé

1. Connectez-vous à la console : `https://kortex.center/admin/cles-api/`.
2. Choisissez :
   - un **nom**, qui dit à quoi sert la clé (« rollout de nuit ») ;
   - une **portée** ;
   - une **durée** : 30, 90 ou 365 jours. Aucune clé n'est sans fin.
3. Copiez la clé. **Elle ne sera plus jamais affichée.** La plateforme n'en
   garde qu'une empreinte et ne peut pas la retrouver.

Une clé a la forme `kx_<prefixe>_<secret>` :
- le **préfixe**, 8 caractères, identifie la clé dans la console et dans le
  journal ;
- le **secret** ne se montre jamais.

Une clé ne crée pas d'autre clé. La création se fait toujours dans la
console, en session.

## Portées

Chaque portée comprend les précédentes.

| portée | permet |
|---|---|
| `lecture` | lire la flotte d'espaces, la fiche de chaque site, ses membres, le journal, les inscriptions, l'état des dashboards et superviseurs |
| `pilotage` | `lecture`, plus déclarer un site (installation de son dashboard et de son superviseur), relancer une installation, redémarrer un composant, lancer une mise à jour, régler les modules |
| `destructif` | `pilotage`, plus **annuler un site** : retrait complet de son espace (DNS, dashboard, superviseur, volumes et données de l'espace). Le site client lui-même n'est jamais touché |

Choisissez la plus petite portée qui suffit. `destructif` n'est jamais coché
par défaut.

**Aucune clé, quelle que soit sa portée, ne peut** :
- créer, lister ou révoquer des clés ;
- gérer les comptes de la plateforme (créer, changer un rôle ou un mot de
  passe, désactiver, supprimer) ;
- lister, déclencher ou télécharger des sauvegardes ;
- utiliser l'installation guidée.

Ces gestes exigent une session humaine. Une clé y reçoit
`403 session_requise`. Une clé volée ne peut donc pas se rendre permanente.

## Routes ouvertes aux clés

La table ci-dessous est générée à partir du code de la plateforme. Elle
existe aussi en JSON : `GET /api/aide/routes`, sans authentification.

### Portée `lecture`

lire la flotte d'espaces, la fiche de chaque site, ses membres, le journal, les inscriptions et l'état des dashboards et superviseurs.

| méthode | chemin |
|---|---|
| GET | `/api/admin/audit` |
| GET | `/api/admin/composants` |
| GET | `/api/admin/consommation` |
| GET | `/api/admin/inscriptions` |
| GET | `/api/admin/sites` |
| GET | `/api/admin/sites/:id` |
| GET | `/api/admin/sites/:id/membres` |
| GET | `/api/admin/sites/:id/rollouts/:rollout_id` |
| GET | `/api/me` |

### Portée `pilotage`

lecture, plus déclarer un site (installation de son dashboard et de son superviseur), relancer une installation, redémarrer un composant, lancer une mise à jour, régler les modules.

| méthode | chemin |
|---|---|
| POST | `/api/admin/sites` |
| POST | `/api/admin/sites/:id/composants/:composant/redemarrer` |
| POST | `/api/admin/sites/:id/membres` |
| PUT | `/api/admin/sites/:id/modules` |
| PUT | `/api/admin/sites/:id/reference` |
| POST | `/api/admin/sites/:id/relancer` |
| POST | `/api/admin/sites/:id/rollout` |
| PUT | `/api/admin/sites/:id/techno` |

### Portée `destructif`

pilotage, plus annuler un site : retrait complet de son espace (DNS, dashboard, superviseur, volumes et données de l'espace) ; le site client lui-même n'est jamais touché.

| méthode | chemin |
|---|---|
| POST | `/api/admin/sites/:id/annuler` |


### Exemples

Lister les sites :

```sh
curl -sS https://kortex.center/api/admin/sites \
  -H "Authorization: Bearer $KORTEX_API_KEY"
```

Déclarer un site (portée `pilotage`) :

```sh
curl -sS -X POST https://kortex.center/api/admin/sites \
  -H "Authorization: Bearer $KORTEX_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"slug":"ma-boutique","domaine":"ma-boutique.kortex.center","nom":"Ma boutique"}'
```

Mettre un site à la dernière version, puis suivre le rollout :

```sh
curl -sS -X POST https://kortex.center/api/admin/sites/42/rollout \
  -H "Authorization: Bearer $KORTEX_API_KEY"
# → 202 {"rollout_id":"…","orchestrateur":{"sockets":1}}
curl -sS https://kortex.center/api/admin/sites/42/rollouts/<rollout_id> \
  -H "Authorization: Bearer $KORTEX_API_KEY"
```

`orchestrateur.sockets` à `0` veut dire que personne n'a entendu la demande :
l'orchestrateur est déconnecté, rien ne se passera.

Annuler un site (portée `destructif`). Le slug doit être retapé :

```sh
curl -sS -X POST https://kortex.center/api/admin/sites/42/annuler \
  -H "Authorization: Bearer $KORTEX_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"confirmation":"ma-boutique"}'
```

## Erreurs

| code | `error` | sens |
|---|---|---|
| 400 | `cle_dans_url` | la clé était dans l'URL. Elle passe uniquement dans l'en-tête `Authorization` |
| 400 | `auth_ambigue` | la requête porte à la fois un cookie de session et une clé. Choisissez l'un des deux |
| 400 | `invalid_input` | corps invalide |
| 401 | `unauthenticated` | clé absente, inconnue, mal formée, révoquée, expirée, ou compte désactivé. La réponse est la même dans tous les cas |
| 403 | `session_requise` | ce geste n'est jamais ouvert aux clés |
| 403 | `portee_insuffisante` | la clé n'a pas la portée exigée par la route |
| 404 | `site_inconnu` | aucun site sous cet identifiant |
| 409 | `slug_pris`, `domaine_pris`, … | conflit avec l'état actuel |
| 429 | `trop_de_requetes` | plus de 120 requêtes par minute avec cette clé |
| 429 | `trop_de_tentatives` | trop d'échecs d'authentification depuis cette adresse. Attendez 15 minutes |

Un 401 ne dit jamais pourquoi la clé est refusée. Vérifiez-la avec
`GET /api/me` et, depuis la console, regardez son état.

## Bonnes pratiques

- **La clé ne va jamais dans une URL**, un dépôt git ou un argument de
  commande (visible dans `ps`). Mettez-la dans une variable d'environnement
  ou un fichier en `chmod 600`.
- **Une clé par usage**, avec un nom parlant. On révoque alors l'une sans
  casser les autres.
- **Tourner une clé** : créez la nouvelle, basculez le script, puis révoquez
  l'ancienne. Une clé n'est jamais « régénérée » sur place.
- La console affiche, pour chaque clé, sa **dernière utilisation** (date et
  adresse). Une clé qui sert d'une adresse inattendue doit être révoquée.

## En cas de fuite

1. **Révoquez la clé** dans la console (Clés API, bouton Révoquer, avec un
   motif). L'effet est immédiat : la requête suivante reçoit 401. Dans le
   doute, utilisez « Tout révoquer ».
2. Ouvrez le **journal des gestes** (`/admin/journal/`). Tout geste fait par
   clé y apparaît avec l'auteur `cle:<prefixe> <email>`.
3. Si la console est inaccessible, l'exploitant de la plateforme peut couper
   **toutes** les clés d'un coup : `API_KEYS_DISABLED=1` dans l'environnement
   de l'application, puis redémarrage. Les sessions continuent de
   fonctionner.

Désactiver un compte ou lui retirer le rôle administrateur coupe aussi toutes
ses clés, immédiatement.

## Ce que l'API ne fait pas (encore)

- Une clé de la plateforme ouvre `kortex.center`, pas les espaces
  `<site>.kortex.center` : ceux-ci ont leurs propres clés, créées par le
  client dans son espace quand le module « API » est activé
  ([API des espaces](/modules/dashboard-api/)).
- Il n'existe pas de clé limitée à un seul site.
- Poser l'administrateur d'un site par clé n'est pas encore possible. Ce sera
  fait par invitation par email.
