Aller au contenu

Aller au sommaire de la doc

L’API

L’API REST de while sert aux CI, aux scripts et aux outils qui ne parlent pas MCP. Cette page en donne les 265 opérations.

Mis à jour le 9 novembre 2026.

Voir en Markdown

Lister vos projets
curl -H "Authorization: Bearer $WHILE_TOKEN" https://app.while.sh/v1/projects

L’adresse et le format

L’API répond à https://app.while.sh, en JSON. Sa description complète, au format OpenAPI 3.1, est à https://app.while.sh/openapi.json : un client se génère depuis elle.

S’authentifier

Chaque appel porte un jeton dans l’en-tête Authorization: Bearer. Un jeton de projet sert à une CI ou à un script ; un jeton d’organisation voit les projets de l’organisation que voit sa personne ; la clé d’un compte de service (whcs_…) agit avec exactement ses droits. Un jeton de projet ou d’organisation ne dépasse jamais les droits de sa personne. Les rubriques Jetons et Droits et validations disent comment les créer.

Trois exceptions : le dépôt git prend le jeton comme mot de passe (Basic, utilisateur au choix), les SDK Sentry s’authentifient par la clé de leur DSN, et la télémétrie OTLP par la clé dans son adresse. Le serveur MCP, lui, est à https://app.while.sh/mcp.

Les erreurs

Une erreur suit la RFC 9457 (application/problem+json) : type, title, status, detail. Un refus (403, 409, 428, 429) dit en plus ce qui bloque (why), la suite (what_to_do) et, quand il existe, l’appel tout prêt (action) : l’outil MCP, la requête de l’API ou le lien à ouvrir, et qui doit le faire, l’agent, une personne ou l’équipe while.

Les validations

Un geste sensible, comme détruire la production ou rendre une appli publique, peut demander l’accord d’une personne. La réponse est alors 428, avec la demande (validation) et son lien. Une personne l’accorde dans while ; renvoyez ensuite le même appel avec approval_id. Un jeton ne valide jamais une demande.

Sans jeton

Ce qui se lit sans s’authentifier : les tarifs, le catalogue des briques, les outils MCP, le schéma de while.yaml, les adresses réseau de while, sa version et la description de l’API.

Lire la version de while

GET /v1/info

Rend la version de while et ses adresses publiques. Avec un jeton, la réponse ajoute le moteur et les briques, sans leurs adresses internes.

Lire le catalogue des briques

GET /v1/catalog

Rend le contrat d’une appli et les briques qu’un while.yaml peut déclarer, chacune avec son état : disponible, partiel ou prévu.

Lire les adresses réseau de while

GET /v1/network

Rend l’entrée de while (son nom, pour le CNAME d’un sous-domaine ou l’ALIAS d’un domaine nu, et ses adresses IPv4 et IPv6, pour les A et AAAA d’un domaine nu en dernier recours seulement : elles peuvent changer) et les adresses IPv4 par où sortent les applis (à déclarer chez un tiers qui filtre par adresse). Chacune avec le jour depuis lequel elle vaut, s’il est dit, et un changement annoncé : le jour et les nouvelles adresses. Sans jeton. La réponse est gardée cinq minutes, et un même réseau peut la lire 60 fois par minute.

Lire les tarifs publics

GET /v1/pricing

Rend la grille de prix en vigueur (sa version et sa date), ses offres et leurs prix, et le prix de chaque ressource tel que la page Facture le montre. Sans jeton. La réponse est gardée cinq minutes, et un même réseau peut la lire 60 fois par minute.

Lister les outils MCP

GET /v1/mcp/tools

Rend exactement ce que tools/list sert à l’agent d’une organisation sur /mcp, ou, avec serveur=essai, sur le serveur d’essai /essai. Sans jeton. La réponse est gardée cinq minutes, et un même réseau peut la lire 60 fois par minute.

Paramètres : server.

Lire le schéma de while.yaml

GET /schema/while.v1.json

Rend le JSON Schema de while.yaml, le fichier qui décrit une appli. Votre agent s’en sert pour vérifier son fichier avant de mettre en ligne.

Lire la description OpenAPI

GET /openapi.json

Rend la description de l’API, au format OpenAPI 3.1.

Projets

Créer, lire et détruire un projet, lister les applis d’une organisation avec leurs mesures, et changer le propriétaire ou le budget d’un projet.

Lister les applis d’une organisation, avec leurs mesures

GET /v1/teams/{slug}/apps

Les applis que votre identité voit dans cette organisation. Pour chacune, en production : la version en ligne (version.number, version.since), la dernière construction si rien n’est en ligne (build), les incidents ouverts, à qui ses services sont ouverts (open), ses requêtes des deux dernières heures par tranche de 5 minutes, la plus ancienne d’abord (traffic), le temps sous lequel 95 % des réponses sont parties (p95_ms), ses tâches et leur dernier passage, et la pause de l’organisation (stop) ; ce qu’elle a coûté ce mois-ci, en euros (null sans mesure).

Lister les projets

GET /v1/projects

Rend les projets que votre identité voit.

Paramètres : organization.

Créer un projet

POST /v1/projects

Crée un projet dans votre organisation, avec un budget mensuel de 20 € par défaut. Un budget sans plafond ou au-delà de 20 €, et le bac à sable, demandent un admin de l’organisation et la validation d’une autre personne : le projet est alors créé au budget par défaut, et la réponse 202 porte la demande. Un admin le fait sans attendre personne, au journal (son agent, au plancher, le lui fait confirmer), sauf si l’organisation a réglé deux approbateurs.

Corps : slug (obligatoire), name, budget_eur, account, sandbox, team.

Lire un projet

GET /v1/projects/{slug}

Rend le projet s’il vous est visible, sinon 404.

Détruire un projet

DELETE /v1/projects/{slug}

Détruit le projet avec ses environnements, son dépôt git et ses sources, en tâche de fond (202). Il faut le droit de détruire le projet. Un jeton d’agent le demande toujours à une personne (428, avec la demande) ; une production qui a des données se fait valider par un admin, qui la détruit lui-même dans while.

Paramètres : approval_id.

Lire l’historique d’un projet

GET /v1/projects/{slug}/history

Rend les versions, les plans et les événements du projet, mêlés, du plus récent au plus ancien.

Paramètres : limit.

Changer le propriétaire d’un projet

PUT /v1/projects/{slug}/owner

Réservé aux admins et au propriétaire actuel.

Corps : email (obligatoire).

Changer le budget d’un projet

PUT /v1/projects/{slug}/budget

Réservé aux admins. Baisser le budget mensuel s’applique tout de suite ; le relever se fait confirmer ou valider (202, avec le lien).

Corps : budget_eur (obligatoire).

Jetons

Les jetons d’un projet ou d’une organisation, pour une CI ou un script, et l’identifiant git d’un agent branché par OAuth.

Lister les jetons d’un projet

GET /v1/projects/{slug}/tokens

Rend les jetons du projet, sans leur valeur. Réservé à l’administration du projet : un jeton de projet ne les liste pas.

Créer un jeton de déploiement

POST /v1/projects/{slug}/tokens

Crée un jeton pour une CI externe ou un agent. Il faut la permission access.grant sur le projet. Le jeton agit pour vous : ses droits ne dépassent jamais les vôtres sur ce projet, et se recalculent à chaque requête.

Corps : name, days, scopes, environments.

Révoquer un jeton

DELETE /v1/tokens/{id}

La personne du jeton le révoque toujours, dans sa session ; sinon, il faut pouvoir donner des accès sur sa portée (son projet ou l’organisation). Un refus répond 404, comme un jeton qui n’existe pas.

Créer un jeton d’organisation

POST /v1/teams/{slug}/tokens

Le jeton voit les projets de l’organisation que vous voyez, et eux seuls. Sa valeur n’est montrée qu’une fois.

Corps : name, scopes, days, environments.

Fixer l’expiration d’un jeton

POST /v1/tokens/{id}/expiry

30 jours par défaut. L’appel ne peut que rapprocher l’expiration.

Corps : days.

Obtenir un identifiant git pour un agent OAuth

POST /v1/projects/{slug}/git

Un agent branché par OAuth ne crée pas de jeton : cet appel lui donne un identifiant git valable une heure, pour ce projet seul, avec les droits que sa personne a consentis. Il sert à git push et git fetch, sur les branches seulement, sans tag, sans réécrire ni supprimer de branche ; l’API et le MCP le refusent. Il se coupe quand on déconnecte l’agent, ou quand sa personne ne peut plus mettre en ligne, et cinq au plus sont en cours à la fois. Passez-le dans l’en-tête Authorization: Bearer, jamais dans l’adresse.

while.yaml et plans

Le fichier qui décrit l’appli, et le plan qui dit ce qui va changer avant de l’appliquer.

Lire le while.yaml en cours

GET /v1/projects/{slug}/intent

Rend le fichier qui décrit l’appli, tel que while l’a reçu en dernier.

Paramètres : environment.

Déposer un while.yaml

PUT /v1/projects/{slug}/intent

Enregistre le fichier qui décrit l’appli, une fois validé par le schéma de while.yaml.

Paramètres : environment.

Calculer un plan

POST /v1/projects/{slug}/environments/{env}/plan

Rend ce qui va changer dans l’environnement, sans rien appliquer. Sans intent ni from_state : le dernier while.yaml déposé.

Corps : intent, from_state.

L’offre de l’organisation

GET /v1/teams/{slug}/plan

Réservé à ses admins. Son offre (ce qu’elle comprend, l’usage face à ses limites, ses options), les offres à comparer, les prix et ce qui est compris ; le moyen de paiement quand il est obligatoire (son état, la carte en quatre chiffres) et la cause d’une pause.

Lire un plan

GET /v1/plans/{id}

Rend l’état d’un plan.

Appliquer un plan

POST /v1/plans/{id}/apply

Applique les changements du plan. Une suppression en production, ou d’une base, demande une validation humaine : la réponse est 428, avec le lien de la demande.

Corps : approval_id.

Écarter un plan

POST /v1/plans/{id}/discard

Écarte un plan que vous n’appliquerez pas. Il faut pouvoir mettre en ligne sur l’environnement du plan. Un plan déjà appliqué ou écarté rend 409.

Lire le schéma d’une appli

GET /v1/projects/{slug}/environments/{env}/schema

Ce qui compose l’appli et comment c’est relié, tiré de son while.yaml : ses entrées (internet, l’équipe), ses services, ses tâches, ses bases, ses caches, ses stockages, les bases d’autres applis qu’elle lit, ses sources de données et ses sauvegardes (briques), et leurs liens (links : de, vers, genre). Avec ce que while en mesure et que status ne dit pas : les requêtes de chaque service sur deux heures par tranche de 5 minutes (serie_2h) et leur temps de réponse, les passages du jour de chaque tâche, les appels des sources sur 24 h, la dernière sauvegarde et son essai de restauration. L’état de chaque brique se lit dans status.

Versions et mises en ligne

Les sources, les versions, les retours arrière et l’état d’un environnement.

Mettre en ligne

POST /v1/projects/{slug}/environments/{env}/deploy

Met en ligne une archive de sources (tar.gz), une image déjà construite, ou, avec un corps JSON vide, le dernier commit des sources du projet chez while, écrites par POST …/files ou poussées par git. Si ce commit est déjà en ligne, rien n’est reconstruit (200) ; si une version de ce commit est en cours, elle est rendue telle quelle. Sinon, une nouvelle version démarre (202).

Corps : image, digest, intent.

Lister les fichiers des sources

GET /v1/projects/{slug}/files

Rend les fichiers des sources du projet chez while, celles de son dépôt git : chemins, tailles et dernier commit.

Paramètres : path.

Écrire des fichiers des sources

POST /v1/projects/{slug}/files

Écrit les fichiers nommés dans un commit sur la branche du projet ; les autres ne changent pas, et delete retire des fichiers ou des dossiers. Du texte UTF-8, 2 Mio et 200 fichiers au plus par appel. Si rien ne change, aucun commit n’est créé (200) ; si un autre commit arrive pendant l’écriture, la réponse est 409. Ensuite, POST …/environments/{env}/deploy sans archive met ce commit en ligne.

Corps : files, delete, message.

Lire un fichier des sources

GET /v1/projects/{slug}/files/{path}

Rend le contenu d’un fichier texte, 256 Kio au plus. Un fichier binaire est signalé, sans son contenu.

Télécharger les sources d’une version

GET /v1/projects/{slug}/environments/{env}/releases/{number}/sources

Rend l’archive tar.gz que la construction de la version a lue. Elle est gardée 7 jours, et tant que la version est en ligne.

Lister les versions

GET /v1/projects/{slug}/environments/{env}/releases

Rend l’historique des versions de l’environnement.

Paramètres : limit.

Remettre en ligne une version construite

POST /v1/projects/{slug}/environments/{env}/releases/{number}/redeploy

Crée une nouvelle version avec la même image et les variables d’aujourd’hui, sans rien reconstruire. Quand la politique de l’organisation demande une validation, la réponse est 428 avec le lien ; une mise en production durcie s’arrête avant d’être appliquée et reprend d’elle-même une fois validée.

Lire une version

GET /v1/releases/{id}

Rend la version, avec sa construction et son plan.

Retirer une version de ses liaisons

POST /v1/projects/{slug}/environments/{env}/releases/{number}/remove-for-binding

Pour la fédération OIDC, avec le corps { reason }. Une personne approbatrice du projet, par sa session. La version ne signera plus jamais, même revenue par un retour arrière.

Corps : reason (obligatoire).

Revenir à une version précédente

POST /v1/projects/{slug}/environments/{env}/rollback

Remet en ligne une version précédente. Quand une validation est attendue, la réponse est 428 avec le lien de la demande, comme pour POST …/deploy, et une CI s’arrête là ; renvoyez l’appel avec approval_id une fois la demande accordée. Par un lien d’envoi d’agent (POST /v1/upload/{token}), la réponse est 202 avec la suite ; par l’outil MCP, « validation humaine requise ».

Corps : to, approval_id.

Redémarrer un service

POST /v1/projects/{slug}/environments/{env}/services/{service}/restart

Redémarre un service de la version en ligne, sans créer de version, et coupe les requêtes en cours. Les instances d’un service web sont recréées ; un worker redémarre en roulement. 409 si rien ne tourne.

Lire l’état d’un environnement

GET /v1/projects/{slug}/environments/{env}/status

Rend la version en ligne, son adresse, sa santé et le trafic des 15 dernières minutes (p50, p95, p99). services[].titre et taches[].titre donnent le titre du while.yaml, null sans titre. Pour chaque brique, la réponse donne ce qu’elle réserve et ce qu’elle prend, mesuré (null tant que ce n’est pas mesuré).

Paramètres : probe.

Lire ce qui a été appliqué

GET /v1/projects/{slug}/environments/{env}/state

Rend les 20 dernières versions d’état de l’environnement : ce qui a réellement été appliqué.

Créer une prévisualisation

POST /v1/projects/{slug}/previews

Crée un environnement de prévisualisation. Il faut le droit de mettre en ligne.

Corps : name, branch, from, copy_database, ttl_hours.

Détruire un environnement

DELETE /v1/projects/{slug}/environments/{env}

Il faut le droit de détruire le projet (un admin ; pas une développeuse). Détruire la production demande une validation humaine : la réponse est 428 avec le lien, et vous renvoyez ensuite l’appel avec approval_id.

Corps : approval_id.

Lire une construction

GET /v1/builds/{id}

Rend l’état d’une construction : ses temps, son cache, chaud ou froid, et ses tests.

Lire le journal d’une construction

GET /v1/builds/{id}/logs

Rend le journal en JSON, ou en flux SSE avec l’en-tête Accept: text/event-stream.

Paramètres : stream, since.

Envoyer des sources par un lien signé

POST /v1/upload/{token}

Reçoit l’archive des sources d’une mise en ligne. Le lien vient de l’outil MCP upload_source et vaut 15 minutes.

Sans jeton.

Télécharger des sources par un lien signé

GET /v1/sources/{token}

Rend l’archive tar.gz des sources d’une version. Le lien vient de l’outil MCP read_source et vaut 10 minutes.

Sans jeton.

Git et forges

Le dépôt git de chaque projet, et la liaison avec GitHub ou GitLab.

Lire l’aide du dépôt git

GET /git/{repo}

Rend les commandes pour ajouter le dépôt comme remote et y pousser.

Sans jeton.

Annoncer les références git

GET /git/{repo}/info/refs

Le protocole git intelligent. git-receive-pack demande le droit de mettre en ligne. Un projet déjà à sa limite de stockage (sources et git) reçoit 409, avec le refus du quota en texte, que git affiche.

Paramètres : service (obligatoire).

Récupérer un dépôt git

POST /git/{repo}/git-upload-pack

git fetch et git clone (stateless-rpc). Corps brut ou en gzip (Content-Encoding) ; 413 au-delà de la limite d’une archive de sources, 64 Mo, décompressé compris.

Pousser sur un dépôt git

POST /git/{repo}/git-receive-pack

git push : il faut le droit de mettre en ligne. Un push sur main met en production ; sur une autre branche, il ouvre une prévisualisation. Corps brut ou en gzip (Content-Encoding) ; 413 au-delà de la limite d’une archive de sources, 64 Mo, décompressé compris.

Lire la liaison GitHub ou GitLab

GET /v1/projects/{slug}/forge

Rend le dépôt lié au projet, sans son jeton.

Lier un dépôt GitHub ou GitLab

POST /v1/projects/{slug}/forge

Un push sur la branche par défaut met en production ; une demande de fusion ouvre une prévisualisation, commentée sur la demande ; chaque commit reçoit son statut. Le jeton de la forge est vérifié chez elle, rangé au coffre, et sert à créer le webhook.

Corps : forge (obligatoire), repository (obligatoire), token (obligatoire), base_url.

Délier le dépôt

DELETE /v1/projects/{slug}/forge

Retire la liaison, et le webhook chez la forge.

Domaines et liens d’accès

Les domaines d’un environnement, l’adresse principale d’un projet, et les liens qui montrent une appli à quelqu’un sans compte.

Lister les domaines

GET /v1/projects/{slug}/environments/{env}/domains

Rend les domaines de l’environnement : statut, enregistrements DNS à créer, constat de while et certificat.

Ajouter un domaine

POST /v1/projects/{slug}/environments/{env}/domains

Ajoute un domaine à un service (le seul qui a une adresse quand service est absent) : le while.yaml change et une nouvelle version part (statut inchangé, sans version, quand le service a déjà ce domaine). Le domaine attend ensuite son enregistrement DNS (GET domains le dit).

Corps : domain (obligatoire), service.

Vérifier un domaine

POST /v1/projects/{slug}/environments/{env}/domains/{host}/verify

Vérifie tout de suite un domaine personnalisé : sa preuve TXT et son pointage.

Donner l’adresse du projet à un autre service

PUT /v1/projects/{slug}/principal-address

Sous un domaine des applis sans région : l’adresse du projet seul passe à ce service web public, dans chaque environnement en ligne (la version en ligne est remise telle quelle). Une personne seulement, par sa session : le propriétaire de l’appli ou un admin ; un jeton reçoit 403. Sans confirmer, 409 et l’aperçu : les adresses qui partent et celles qui arrivent.

Corps : service (obligatoire), confirm.

Lister les liens d’accès

GET /v1/projects/{slug}/environments/{env}/links

Rend les liens d’accès de l’environnement : nom, échéance, mot de passe ou non, ouvertures. Le lien lui-même n’est jamais rendu.

Créer un lien d’accès

POST /v1/projects/{slug}/environments/{env}/links

Un lien d’accès montre l’appli à quelqu’un qui n’a pas de compte while. Corps : { name, days, password }, avec days de 1 à 90 et un password facultatif, de 8 caractères au moins. Le lien n’est rendu qu’une fois : url, et urls, une par adresse de l’environnement. En production, il naît inactif et s’active quand une autre personne le valide (approval : la demande). Il faut être une personne, avec project.configure : un jeton d’agent reçoit 403, et l’outil MCP request_access_link rend la page de while à ouvrir. Un admin le fait sans attendre personne, au journal (son agent, au plancher, le lui fait confirmer), sauf si l’organisation a réglé deux approbateurs.

Corps : name (obligatoire), days (obligatoire), password.

Révoquer un lien d’accès

DELETE /v1/projects/{slug}/environments/{env}/links/{id}

Le lien cesse de marcher tout de suite. Il faut être une personne, avec project.configure.

Créer un lien présigné vers un objet

POST /v1/projects/{slug}/environments/{env}/buckets/{name}/links

Rend un lien présigné vers un objet, valable 15 minutes par défaut et 24 heures au plus. Un lien d’envoi sert à un PUT et demande data.write.

Corps : key (obligatoire), upload, minutes, upstream.

Variables et secrets

Les variables d’un environnement, et le coffre, compatible avec Vault.

Lister les variables

GET /v1/projects/{slug}/environments/{env}/variables

Les noms posés dans l’environnement (secrets, variables en clair, secrets en fichier et de construction), jamais leurs valeurs.

Poser des variables

PUT /v1/projects/{slug}/environments/{env}/variables

Pose plusieurs variables d’un coup, sans toucher aux autres : au coffre par défaut, en clair avec secret: false. Elles arrivent dans l’appli à la prochaine mise en ligne.

Corps : values (obligatoire), secret, file, build.

Supprimer une variable

DELETE /v1/projects/{slug}/environments/{env}/variables/{name}

Supprime une variable ou un secret de l’environnement, et le note au journal ; sur un projet lié à une connexion fédérée, la production passe par ses approbateurs (428). Avec ?build=1, un secret de construction. Le changement vaut à la prochaine mise en ligne.

Paramètres : build.

Vérifier le coffre

GET /vault/v1/sys/health

Compatible avec l’API de Vault.

Sans jeton.

Lire un secret

GET /vault/v1/secret/data/{path}

Compatible Vault KV v2.

Paramètres : project, environment.

Écrire un secret

POST /vault/v1/secret/data/{path}

Compatible Vault KV v2. Il faut le droit de mettre en ligne.

Paramètres : project, environment.

Corps : data (obligatoire).

Supprimer un secret

DELETE /vault/v1/secret/data/{path}

Compatible Vault KV v2. Il faut le droit de mettre en ligne.

Paramètres : project, environment.

Lister les secrets sous un préfixe

GET /vault/v1/secret/metadata/{path}

Compatible Vault KV v2.

Paramètres : project, environment.

Bases et buckets

Lire une base en SQL, ses sauvegardes et ses requêtes, et les fichiers des buckets.

Lister les sauvegardes

GET /v1/projects/{slug}/environments/{env}/backups

Rend les sauvegardes des bases de l’environnement, avec le résultat de leur test de restauration. status : running, succeeded, failed, ou expired (ses objets sont effacés depuis expired_at : elle ne se restaure plus).

Restaurer une sauvegarde

POST /v1/projects/{slug}/environments/{env}/restore

Restaure une sauvegarde d’une base, en une seule transaction, après une sauvegarde de sûreté. Demande une validation humaine.

Corps : database, backup_id, instant, approval_id.

Rétablir une connexion coupée

POST /v1/teams/{slug}/connections/{connection}/restore

Il faut le même droit que pour la couper. La source reprend telle qu’elle était : phase, règles, droits. Sans effet sur une source ouverte. Pas d’outil MCP.

Lire une base en SQL

POST /v1/projects/{slug}/environments/{env}/query

Exécute une seule requête SQL de lecture (SELECT, WITH, VALUES, TABLE), plafonnée, notée au journal sans ses valeurs. Elle tourne au nom de la personne, sous le rôle PostgreSQL de son identité (while_id_…), qui n’a que ses droits sur les données : data.read sur la base, un schéma, une table ou des colonnes. Transaction READ ONLY ; 10 secondes par défaut (30 au plus), 400 lignes par défaut (2 000 au plus), 512 000 octets par défaut. Refusé (400) : plusieurs instructions, une écriture, du DDL, COPY, DO, les fonctions qui agissent, et ce qui fabrique une grosse valeur en mémoire (repeat, lpad, rpad ou format au-delà de 10 000 caractères, array_fill, generate_series au-delà de 100 000 valeurs, une agrégation sur une série plus grande). Faute de droit, PostgreSQL refuse : 403, avec la table, les colonnes lisibles et la permission (missing_permission, object, readable_columns). Une base qui n’a pas encore le rôle while_acces, avant sa prochaine mise en ligne, rend 409 ; un autre refus de la base, 422. Une base qui déclare replica: true est encore lue sur la base principale, en lecture seule, et la réponse le dit.

Corps : sql (obligatoire), database, limit, max_bytes, timeout_seconds.

Exporter une requête en CSV

POST /v1/projects/{slug}/environments/{env}/export.csv

Exporte le résultat d’une seule requête SQL de lecture, avec le même rôle, les mêmes refus et le même journal que POST …/query. 10 000 lignes et 1 000 000 octets par défaut (50 000 et 4 000 000 au plus), 10 secondes (30 au plus). Un en-tête puis les lignes, en CRLF ; NULL est vide, une chaîne vide s’écrit "". Une cellule qu’un tableur lirait comme une formule (=, +, - ou @ en tête, hors nombres) est précédée d’une apostrophe. Les en-têtes x-while-lignes et x-while-tronque (non, rows ou bytes) disent ce qui a été rendu. 429 quand l’organisation a déjà 3 lectures en cours.

Corps : sql (obligatoire), database, limit, max_bytes, timeout_seconds.

Lire un bucket

GET /v1/projects/{slug}/environments/{env}/buckets/{name}/objects

Lit un bucket par while : la liste de ses objets, 200 par page, ou, avec ?cle=, le début d’un objet texte (64 Kio). Il faut data.read.

Paramètres : key, prefix, next.

Renouveler la clé S3

POST /v1/projects/{slug}/environments/{env}/buckets/rotate-key

Crée une nouvelle clé S3 pour l’environnement. Elle part à la prochaine mise en ligne, et l’ancienne vaut jusque-là ; avec immediate, l’ancienne est supprimée tout de suite.

Corps : immediate.

Lire les requêtes SQL d’une base

GET /v1/projects/{slug}/environments/{env}/databases/{database}/queries

Rend les requêtes que l’appli envoie à sa base, d’après pg_stat_statements : les plus fréquentes et les plus lentes. L’outil MCP observe_app (vue database) rend la même chose.

Paramètres : limit, window_minutes.

Tâches planifiées

Les tâches de chaque environnement et leurs passages.

Lister les tâches planifiées

GET /v1/projects/{slug}/environments/{env}/tasks

Pour chaque tâche : son titre (celui du while.yaml, null sans titre), son planning, son dernier passage (état, durée, erreur, quoi faire), le passage en cours, le prochain et les 14 derniers.

Lister les passages d’une tâche

GET /v1/projects/{slug}/environments/{env}/tasks/{task}/runs

Rend l’historique des passages : déclenchement, auteur, arguments, début, fin, état, code de sortie, extrait du journal et Job.

Paramètres : limit.

Lancer une tâche maintenant

POST /v1/projects/{slug}/environments/{env}/tasks/{task}/runs

Lance un passage manuel, ou le rattrapage d’une période (WHILE_CATCH_UP_START et WHILE_CATCH_UP_END). Refusé si un passage tourne déjà.

Corps : args, env, catch_up.

Observer

Les journaux, les métriques, les traces, les erreurs et les événements de l’appli, et ce que les agents y ont fait.

Lire les traces

GET /v1/projects/{slug}/environments/{env}/traces

Rend les traces OpenTelemetry les plus récentes de l’appli, ou une trace par son trace_id. Elles sont gardées 30 jours. L’outil MCP observe_app (vue traces) rend la même chose.

Paramètres : trace_id, service, operation, since_minutes, min_duration, errors, limit.

Lire les menaces d’un environnement

GET /v1/projects/{slug}/environments/{env}/security

Rend ce qui menace l’environnement : les failles de ses dépendances (UpdateFOMO), son exposition (Probr), les robots qui l’appellent (Polygraphe) et les constats de sa construction (Gitleaks, Opengrep). L’outil MCP get_app_status en rend les failles et les robots (include: ["vulnerabilities"]).

Paramètres : since.

Télécharger la nomenclature de la version en ligne

GET /v1/projects/{slug}/environments/{env}/sbom

Rend la nomenclature CycloneDX de la version en ligne de l’environnement, tirée de ses fichiers de verrouillage, en fichier à télécharger ; 404 quand cette version n’en a pas.

Lire les journaux de l’appli

GET /v1/projects/{slug}/logs

Interroge les journaux de l’appli en LogsQL.

Paramètres : environment, query, limit, minutes.

Lire les métriques

GET /v1/projects/{slug}/metrics

Interroge les métriques de l’appli en PromQL, par l’API HTTP de Prometheus.

Paramètres : query, environment, start, end, step, range.

Lire les événements

GET /v1/projects/{slug}/events

Rend les événements du projet au format CloudEvents. Avec stream=1, ou l’en-tête Accept: text/event-stream, la réponse est un flux SSE. Le flux revérifie votre identité (le jeton ou la session, avec project.view) à chaque événement et toutes les 60 secondes, et se ferme dès qu’elle ne passe plus : membre retiré, jeton révoqué, session fermée.

Paramètres : limit, stream.

Lire les erreurs de l’appli

GET /v1/projects/{slug}/errors

Rend les erreurs de l’appli regroupées par empreinte, 50 groupes au plus : le nombre d’occurrences, la première et la dernière fois, la version déclarée.

Paramètres : environment, since.

Lire la consommation en CloudEvents

GET /v1/usage/events

Rend la consommation des projets que vous voyez, en CloudEvents au format OpenMeter.

Lire ce que consomme chaque service

GET /v1/projects/{slug}/environments/{env}/resources

Le processeur (vCPU), la mémoire (octets) et le nombre de copies de chaque service sur les dernières heures (1, 6, 24 ou 168), une centaine de points par courbe ([instant en ms, valeur]). Un service endormi n’a pas de point. mesure : false quand les mesures n’ont pas répondu. Ce que chaque service réserve se lit dans status (sizes).

Paramètres : hours.

Lire le trafic dans le temps

GET /v1/projects/{slug}/environments/{env}/traffic

Par tranche (1 min sur 1 heure, 5 min sur 6 heures, 15 min sur 24 heures, 2 heures sur 7 jours) : les requêtes, celles en erreur, la médiane et le 95e centile de la réponse en ms, la version qui a servi le plus. Avec les mises en ligne et les incidents de la plage, et le seuil d’erreurs de l’alerte par défaut en % (null en sourdine).

Paramètres : hours.

Lire la ligne de temps

GET /v1/projects/{slug}/timeline

Rend sur une seule ligne les événements de while, les erreurs nouvelles et les actions des agents par MCP.

Paramètres : environment, since, until, families, limit.

Lire l’impact d’une version

GET /v1/projects/{slug}/environments/{env}/impact

Compare le trafic, les erreurs et la latence avant et après la version, et rend un verdict et des propositions.

Paramètres : version, window_minutes.

Poser une question sur la production

POST /v1/projects/{slug}/environments/{env}/ask

Une question comme « pourquoi c’est lent depuis 14 h ? ». Rend les faits, les suspects et des propositions.

Corps : question (obligatoire).

Lister les actions des agents par MCP

GET /v1/projects/{slug}/mcp-actions

Rend ce que les agents ont fait par MCP : l’outil, ses paramètres (valeurs sensibles masquées) et le résultat.

Paramètres : since, limit.

Lire les points d’entrée de la télémétrie

GET /v1/projects/{slug}/telemetry

Rend le DSN Sentry et les points d’entrée OpenTelemetry du projet. La clé du DSN se crée à la première lecture.

Lire une erreur et sa pile

GET /v1/projects/{slug}/errors/{fingerprint}

Une erreur regroupée par son empreinte : ses occurrences, la première et la dernière fois, et la pile de son dernier événement (fonction, fichier, ligne), avec l’adresse demandée.

Lister les webhooks

GET /v1/projects/{slug}/webhooks

Rend les webhooks du projet, sans leur secret.

Abonner un webhook

POST /v1/projects/{slug}/webhooks

Abonne une adresse aux événements du projet : il faut le droit de configurer le projet, et une URL https:// qui se résout vers une adresse publique. Les envois sont signés, au format CloudEvents 1.0, selon Standard Webhooks.

Corps : url (obligatoire), types, secret.

Retirer un webhook

DELETE /v1/projects/{slug}/webhooks/{id}

Il faut la permission project.configure. Les livraisons en attente partent avec lui.

Envoyer des erreurs au format Sentry

POST /api/{projectRef}/envelope/

Reçoit les enveloppes des SDK Sentry, tels quels. Authentifié par la clé publique du DSN (X-Sentry-Auth, Authorization ou ?sentry_key), pas par un jeton. 50 événements et 512 Ko au plus par enveloppe.

Envoyer de la télémétrie OTLP

POST /otlp/{key}/{environment}/v1/{signal}

Relaie les journaux et les métriques OTLP/HTTP de l’appli. Le projet et l’environnement sont imposés aux données relayées ; 502 si le collecteur refuse.

Sans jeton.

Alertes et incidents

Les règles, les incidents, les canaux et la page de statut.

Lire les alertes d’un projet

GET /v1/projects/{slug}/alerts

Rend les alertes par défaut (état, sourdine), les règles écrites (condition en clair, dernier état), les incidents ouverts et ceux des 30 derniers jours, les canaux, et l’origine de la mesure de disponibilité.

Créer ou modifier une règle d’alerte

POST /v1/projects/{slug}/alerts/rules

Par son nom : une source, une condition structurée (ni PromQL ni LogsQL), une fenêtre, une gravité, quoi faire et les canaux. Noté au journal.

Corps : name (obligatoire), source (obligatoire), condition, window_minutes, severity, message (obligatoire), channels, environment, active.

Supprimer une règle d’alerte

DELETE /v1/projects/{slug}/alerts/rules/{rule}

Par son identifiant ou son nom. Ses incidents ouverts sont clos.

Mettre une alerte par défaut en sourdine

PUT /v1/projects/{slug}/alerts/defaults/{default}

Met en sourdine, ou réactive, une alerte par défaut pour ce projet, parmi : task, unreachable, restarts, errors, latency, budget, vulnerabilities, disk.

Corps : mute (obligatoire).

Lister les incidents

GET /v1/projects/{slug}/incidents

Rend les incidents ouverts ou acquittés, et ceux clos dans les 30 derniers jours : quoi, depuis quand, quoi faire, et les envois.

Acquitter un incident

POST /v1/projects/{slug}/incidents/{id}/acknowledge

Dit que quelqu’un s’en occupe. L’incident se clôt de lui-même au retour à la normale. Noté au journal.

Corps : reason.

Lire la page de statut

GET /v1/projects/{slug}/status-page

Rend les données de la page de statut (/statut/<organisation>/<projet>) : ouverte au public ou non, état, disponibilité sur 30 jours, temps de réponse et incidents récents.

Ouvrir ou fermer la page de statut

PUT /v1/projects/{slug}/status-page

public: true l’ouvre, false la ferme ; elle est fermée par défaut. Ouverte, tout le monde y lit le nom affiché, l’état et les incidents en cours, sans détail technique. Noté au journal.

Corps : public (obligatoire).

Lister les canaux d’alerte

GET /v1/teams/{slug}/alert-channels

Rend les canaux de l’organisation, sans leurs adresses, qui sont des secrets, et le réveil des agents, toujours actif.

Ajouter un canal d’alerte

POST /v1/teams/{slug}/alert-channels

Réservé aux admins. Un courriel (l’adresse d’un membre de l’organisation, sinon 403), Slack (l’adresse du webhook entrant) ou un webhook générique (CloudEvents signés ; la clé de signature n’est rendue qu’une fois). 10 canaux par organisation ; au-delà, 409 quota-exceeded.

Corps : type (obligatoire), name, address, url, min_severity.

Retirer un canal d’alerte

DELETE /v1/teams/{slug}/alert-channels/{channel}

Réservé aux admins. Son adresse est détruite dans le coffre.

Tester un canal d’alerte

POST /v1/teams/{slug}/alert-channels/{channel}/test

Envoie un message d’essai par ce canal. Réservé aux admins ; un par minute et par canal, sinon 429 avec Retry-After.

Lire la page de statut d’une appli

GET /v1/status/{organization}/{project}

La page de statut publique d’une appli, sans compte quand son projet l’a ouverte : le nom affiché, l’état, les incidents en cours, sans détail technique. Qui voit le projet la lit entière (chaque adresse, ses 30 jours, la page ouverte ou non). Sinon 404, la même réponse qu’une appli inconnue.

Sans jeton.

Applis internes

Qui entre dans une appli réservée à l’organisation, l’accès de son agent, et la clé qui signe son identité.

Lister qui entre dans les applis internes

GET /v1/projects/{slug}/sign-ins

Rend, pour chaque appli interne du projet, la règle qui dit qui peut entrer, et qui est entré, avec la date. Il faut pouvoir lire le journal du projet.

Lister les accès des agents aux applis internes

GET /v1/projects/{slug}/app-access

Rend les identifiants en cours que vos agents ont ouverts pour entrer dans une appli du projet. L’identifiant lui-même n’est jamais rendu, seulement son id, pour le couper.

Ouvrir une appli interne à l’agent d’une personne

POST /v1/projects/{slug}/app-access

Donne à l’agent d’une personne un identifiant d’une heure pour entrer dans une appli interne (access: internal). L’agent passe par son branchement OAuth ou par un jeton de sa personne ; un compte de service ou une session est refusé. L’appli le reçoit comme la personne : mêmes droits, sa règle relue à chaque requête, et while_agent dans l’en-tête X-While-Identity. L’identifiant vaut pour cette adresse seule, cinq au plus en cours par personne, et passe dans l’en-tête Cookie (__Host-while-agent=…), jamais dans l’adresse. Avec read_only, seuls GET et HEAD passent. Il se coupe quand on déconnecte l’agent, ou par DELETE. Quand le rôle de la personne demande une validation pour « Ouvrir une appli interne par son agent », la réponse est 428 avec la demande ; renvoyez ensuite l’appel avec approval_id.

Corps : service, environment, address, read_only, approval_id.

Couper l’accès d’un agent à une appli

DELETE /v1/projects/{slug}/app-access/{id}

Coupe l’identifiant tout de suite. Sa personne ou un admin de l’organisation peut le faire.

Lire la clé des jetons d’identité

GET /.well-known/jwks.json

Rend la clé publique (JWKS, ES256) qui signe le jeton d’identité X-While-Identity transmis aux applis internes.

Sans jeton.

Connexions

Les API externes que les applis appellent par while : leurs règles, leurs secrets, leurs plafonds et leur journal.

Lister les connexions

GET /v1/teams/{slug}/connections

Rend les connexions de l’organisation : phase (déclaration à valider, déclarée, apprentissage, verrouillée), authentification (sans secret), appels et refus des dernières 24 heures, et la variable <NOM>_URL.

Déclarer une connexion

POST /v1/teams/{slug}/connections

La déclaration est validée par un approbateur, ou passe à votre nom quand les règles de l’organisation vous laissent autonome (201). Elle se fait sans secret, saisi ensuite par PUT …/secret. La connexion démarre en apprentissage pendant 7 jours : les écritures y sont bloquées, sauf les lectures en POST (post_read).

Corps : name (obligatoire), label, domain, model, secondary_domains, authentication, post_read, rules, learning_days, rate_per_minute, owner, project, apps, direction.

Lire une connexion

GET /v1/teams/{slug}/connections/{connection}

Rend ses règles (sa surface : méthode, motif, validation à chaque appel) ; qui peut l’appeler (attributions connection.read et connection.write, en phrases) ; qui l’a appelée cette semaine, et par quelle attribution ; les refus de la semaine ; les changements de règles et d’accès ; les appels observés et la proposition de verrouillage ; les derniers appels (métadonnées) et les appels retenus ; les approbateurs, et comment l’appeler.

Saisir le secret d’une connexion

PUT /v1/teams/{slug}/connections/{connection}/secret

Qui peut configurer la connexion (un admin, ou une attribution qui le donne) saisit ou change le secret. Il est rangé au coffre, jamais rendu ni noté au journal ; une connexion déclarée entre alors en apprentissage. Les champs suivent la recette : value ; user et password (basic) ; client_id et client_secret (oauth2) ; identifier et password (session).

Lire la proposition de verrouillage

GET /v1/teams/{slug}/connections/{connection}/lock

Rend, sans rien demander, les règles que while calcule depuis les appels observés (identifiants généralisés en *) et le diff. Qui peut appeler reste l’affaire des attributions.

Verrouiller une connexion sur ce qui a servi

POST /v1/teams/{slug}/connections/{connection}/lock

while calcule les règles depuis les appels observés et crée la demande de validation, avec le diff ; un admin n’attend personne : verrouillée tout de suite (200). Une fois validée, tout appel hors des règles reçoit 403 (règle manquante), et l’appelant est prévenu.

Couper une connexion

POST /v1/teams/{slug}/connections/{connection}/revoke

Le geste d’urgence d’une personne qui en répond (connection.configure), sans validation. Le relais refuse ensuite tout appel vers la source (code « coupee »), avant même les droits. Sans effet sur une source déjà coupée. Pas d’outil MCP.

Lister les plafonds d’appels

GET /v1/teams/{slug}/call-limits

Rend les plafonds d’appels par appelant : ceux d’une source (?connexion=) et ceux de toute l’organisation, qui valent aussi pour elle ; sans connexion, tous. Pour chacun : l’appelant, la fenêtre, le nombre, les appels de la fenêtre en cours et quand elle repart. Il faut org.view.

Paramètres : connection.

Poser un plafond d’appels

POST /v1/teams/{slug}/call-limits

Pose un plafond, ou change son nombre : un appelant, une fenêtre (minute, heure, jour, mois) et un nombre d’appels, sur une source (connexion) ou sur toute l’organisation. L’appelant est appli:<projet>/<service>, task:<projet>/<tâche>, personne:<e-mail>, agent:<identifiant>, compte_service:<nom> ou groupe:<nom>, qui compte ses membres et leurs agents ensemble. Au-delà, le relais refuse l’appel (429, code « plafond », Retry-After) ; une alerte part à 80 %, une autre au premier refus, une fois par fenêtre. Il faut connection.configure, et l’offre Entreprise pour une organisation qui a une offre.

Corps : connection, caller (obligatoire), window (obligatoire), calls (obligatoire).

Retirer un plafond d’appels

DELETE /v1/teams/{slug}/call-limits/{limit}

Il faut le même droit que pour le poser (connection.configure), quelle que soit l’offre. Noté au journal.

Lister les liaisons d’une connexion fédérée

GET /v1/teams/{slug}/connections/{connection}/bindings

Pour la fédération OIDC : chaque liaison, avec son sub (b:<uuid>), son état (demandee, validee, suspendue ou retiree), ce qu’elle fige (audience, hôte d’échange, hôtes d’API) et ses validations.

Demander une liaison à une connexion fédérée

POST /v1/teams/{slug}/connections/{connection}/bindings

Lie une appli de production, un service ou task:<nom>, à une connexion fédérée, en figeant ses paramètres et l’empreinte de ses règles. La liaison demande une validation stricte des approbateurs de la connexion, jamais par la plateforme ni en force, et, quand la case est cochée, celle de chaque version par les approbateurs du projet, sur une mise en production. 409 si la fédération n’est pas en service, si la connexion n’est pas fédérée et verrouillée, si l’appelant a déjà une liaison vivante, ou si la version en ligne a une image sans digest. Une liaison tombe d’elle-même (version de production sans son appelant, environnement détruit, projet supprimé, connexion refusée ou redéclarée, retrait) et ne revient jamais.

Corps : project (obligatoire), caller (obligatoire), approve_production_deploys.

Reprendre une liaison suspendue

POST /v1/teams/{slug}/connections/{connection}/bindings/{binding}/resume

Un retour arrière automatique suspend la liaison. Cet appel crée une demande stricte aux approbateurs du projet ; validée, la liaison reprend si la version en ligne est validée pour elle et n’en a pas été retirée.

Faire valider chaque mise en production d’une liaison

POST /v1/teams/{slug}/connections/{connection}/bindings/{binding}/case

La case « Faire valider chaque mise en production », avec le corps { approve: boolean, reason }. La cocher prend effet tout de suite (200) ; la décocher est une demande stricte aux approbateurs du projet (202).

Corps : approve (obligatoire), reason (obligatoire).

Retirer une liaison

DELETE /v1/teams/{slug}/connections/{connection}/bindings/{binding}

Pour de bon, avec le corps { reason }. Il faut pouvoir configurer le projet ou la connexion. Noté au journal de l’organisation.

Corps : reason (obligatoire).

Changer les lectures en POST d’une connexion

PUT /v1/teams/{slug}/connections/{connection}/post-read

Envoie la liste voulue des chemins où un POST est une lecture. Un chemin retiré vaut tout de suite : un POST y redevient une écriture, qui demande connection.write et une règle. Un chemin ajouté se fait valider, comme une règle ; un admin autonome passe sans attendre.

Corps : post_read, reason.

Demander une règle de plus

POST /v1/teams/{slug}/connections/{connection}/rules

Une règle donne une méthode, un motif (* pour un segment, ** en dernier pour le reste) et approve_each_call ; elle passe par une validation, sauf pour un admin, qui n’attend personne : appliquée tout de suite (200). Qui peut appeler se donne par une attribution (appel-connexion, appel-connexion-ecriture), pas par la règle.

Corps : method (obligatoire), pattern (obligatoire), approve_each_call, read, reason.

Lire le journal des appels d’une connexion

GET /v1/teams/{slug}/connections/{connection}/calls

Rend chaque appel et chaque refus : quand, qui et par où, méthode, chemin normalisé, statut, durée, taille du corps envoyé en octets, raison du refus, et l’attribution qui l’a permis (rôle, donnée par qui, quand) ; jamais le contenu. Rend aussi les appels retenus. 7 jours par défaut ; au-delà de 30 jours, il faut log.read sur la connexion. Gardé selon la conservation de l’organisation.

Paramètres : since, until, refusal, limit.

Lire un appel retenu

GET /v1/teams/{slug}/connections/{connection}/calls/{call}

Un appel d’une règle « valider chaque appel » : en attente, exécuté (statut et réponse, bornée), refusé ou expiré. Refusé par le relais à son exécution, il porte le refus de while : refusal, refused_by, message, what_to_do, detail.

Appeler une connexion depuis un poste

GET /sorties/{organization}/{connection}/{path}

Depuis un poste de développement ou un script, avec votre jeton personnel. while ajoute la clé, applique les règles, relaie en flux (SSE et gros envois compris) et réécrit les adresses du fournisseur vers cette adresse. Les applis hébergées par while, elles, appellent <NOM>_URL.

Appeler une connexion depuis un poste (POST)

POST /sorties/{organization}/{connection}/{path}

Mêmes règles qu’en GET. Les écritures sont bloquées pendant l’apprentissage, et une règle « valider chaque appel » rend 202.

Appeler une connexion depuis un poste (PUT)

PUT /sorties/{organization}/{connection}/{path}

Mêmes règles qu’en GET. Les écritures sont bloquées pendant l’apprentissage, et une règle « valider chaque appel » rend 202.

Appeler une connexion depuis un poste (PATCH)

PATCH /sorties/{organization}/{connection}/{path}

Mêmes règles qu’en GET. Les écritures sont bloquées pendant l’apprentissage, et une règle « valider chaque appel » rend 202.

Appeler une connexion depuis un poste (DELETE)

DELETE /sorties/{organization}/{connection}/{path}

Mêmes règles qu’en GET. Les écritures sont bloquées pendant l’apprentissage, et une règle « valider chaque appel » rend 202.

Découvrir l’émetteur OIDC d’une organisation

GET /oidc/o/{organization}/.well-known/openid-configuration

Pour la fédération OIDC : iss est l’adresse de while suivie de /oidc/o/<uuid>, avec son JWKS, en RS256. Servi dès qu’une connexion fédérée est déclarée, avant même la première clé ; 404 sans émetteur.

Sans jeton.

Lire les clés de l’émetteur OIDC d’une organisation

GET /oidc/o/{organization}/.well-known/jwks.json

Rend les clés publiques RS256 de l’organisation, jamais celles d’une autre ; kid est la version de la clé. { keys: [] } avant la première clé, 404 sans émetteur, 503 si les clés sont illisibles.

Sans jeton.

Votre compte et vos agents

Votre compte et sa langue, vos agents branchés et leurs droits, vos annonces, et la déconnexion des agents d’une personne.

Lire mes notifications : chaque sorte de courriel ou d’alerte, et si je la reçois

GET /v1/me/notifications

Régler mes notifications

PUT /v1/me/notifications

Corps { notifications: { <kind>: true | false } } (alerts, approvals, access_requests, audit_streams) ; les sortes absentes ne changent pas. Depuis la session de la personne seulement, jamais par un agent.

Corps : notifications (obligatoire).

Lire mon compte

GET /v1/me

Rend la personne connectée : son adresse, son nom s’il en a donné un, la langue de while pour elle (null : celle de son organisation), platform (true : la session du back-office d’un administrateur nommé de la plateforme) et platform_administrator (true : un administrateur de la plateforme, qui peut entrer au back-office par le SSO). Par sa session ou un jeton de son compte.

Changer la langue de mon compte

PUT /v1/me

Corps { language } : l’une des langues de while, ou vide pour celle de son organisation (400 sinon).

Corps : language.

Lire mes applis dans une organisation

GET /v1/me/apps/{organization}

Les applis internes que la personne de la session peut ouvrir dans cette organisation (la règle de chacune, comme à l’ouverture), et les invitations de cette organisation qui attendent son accord. Une organisation où rien ne lui est ouvert, ou qui n’est pas la sienne, répond comme une adresse inconnue : organization vaut null, sans son nom ; home : ses applis dans l’organisation de sa connexion d’entreprise. Depuis une session seulement.

Lister mes agents

GET /v1/me/agents

Rend les agents de la personne, de tous genres : ses agents branchés par OAuth, les jetons de son compte qui ont servi, les clients vus au journal par sa session. Chacun avec où il travaille, ce qu’il peut faire (permissions null : comme elle), quand il a été vu, ses actions des 14 derniers jours (lire, agir), sa journée heure par heure et les sources qu’il appelle. Paramètre organization (son adresse) : ceux de cette organisation, et le nombre d’agents des autres membres (other_agents). Depuis la session de la personne seulement.

Paramètres : organization.

Lire mes annonces

GET /v1/me/announcements

Les annonces en cours de l’équipe while pour la personne. Par sa session : son bandeau, moins celles qu’elle a fermées ou accusées ; ?organization= : le bandeau de cette organisation seule (404 si elle n’en est pas membre). Par son jeton : toutes celles en cours de son organisation, avec acknowledged (son accusé). read_receipt : l’annonce demande un accusé de lecture (POST /v1/announcements/{id}/acknowledge).

Paramètres : organization.

Lire un de mes agents

GET /v1/me/agents/{id}

Rend l’agent (son id : o-<famille>, j-<jeton>, s-<empreinte>), ses 25 dernières actions et ses appels aux sources, les applis où il agit, ses demandes dans son organisation (validations, droits), et les choix pour changer ses droits (change_permissions, null pour une session ou un agent qui crée son projet). Depuis la session de la personne seulement.

Déconnecter un de mes agents

DELETE /v1/me/agents/{id}

Un agent branché par OAuth (o-<famille>, ou sa famille) : ses jetons tombent, ses droits restent, il se rebranche par OAuth. Un jeton se révoque par DELETE /v1/tokens/{id}. Depuis la session de la personne seulement : un jeton ou un agent reçoit 403.

Changer les droits d’un de mes agents

PUT /v1/me/agents/{id}/permissions

Corps { level, scope, project } : level read, act ou admin ; scope app (avec project, une appli de son organisation) ou organization (pour qui y donne des accès ; admin seulement là). Jamais plus que ce que la personne peut là où il travaille ; vaut tout de suite, et c’est noté au journal. Une famille OAuth ou un jeton de son compte, depuis sa session.

Corps : level (obligatoire), scope (obligatoire), project.

Régler les groupes d’outils d’un de mes agents

PUT /v1/me/agents/{id}/tool-groups

Corps { tool_groups } : la liste entière des groupes d’outils MCP que l’agent garde (alerts, feedback_and_docs, environments_and_storage, administer), ceux qu’il a chargés par load_tools. Un groupe ne donne aucun droit : il ne fait que lister ses outils. Noté au journal ; une famille OAuth ou un jeton de son compte, depuis sa session.

Corps : tool_groups (obligatoire).

Déconnecter les agents d’une personne

POST /v1/teams/{slug}/agents/disconnect

Révoque tout de suite les familles d’accès données (agents[].familles de la liste des membres) ; la personne garde ses droits. Elle-même, ou qui administre l’organisation ; chacune au journal.

Corps : families (obligatoire).

Organisation et membres

Les organisations, leurs membres, les invitations, les groupes et la connexion d’entreprise.

Lister les organisations

GET /v1/teams

Rend vos organisations avec votre rôle, ou celle du jeton. Chacune vient avec son offre, le niveau de son abonnement du mois ({ key, label, since }), ou null sans abonnement.

Créer une organisation

POST /v1/teams

Crée une organisation depuis une session. La personne qui la crée en est propriétaire. moyen_de_paiement: "a_enregistrer" est réservé à la plateforme, pour une organisation jetable des tests (e2e-…, mode test de Mollie) : elle naît sans dérogation, son moyen de paiement à enregistrer.

Corps : slug (obligatoire), name, payment_method, language.

Lire une organisation

GET /v1/teams/{slug}

Rend la même fiche que la liste, avec votre rôle et l’offre. Une organisation qui ne vous est pas visible rend 404.

Changer la langue d’une organisation

PATCH /v1/teams/{slug}

Sa langue (langue) : celle de while pour ses membres qui n’ont pas choisi la leur, pour ses agents et ses courriels ; ses propriétaires et ses admins la changent. Une organisation ne se renomme pas : un corps qui porte slug ou nom rend 400, sans rien changer. Une organisation renommée avant garde chaque ancienne adresse 90 jours : l’API y répond sans rediriger, l’interface y redirige (302), personne d’autre ne la prend.

Corps : language, slug, name.

Lire la carte de l’organisation

GET /v1/teams/{slug}/card

Rend, en faits, les projets, leurs services (publics ou internes), leurs bases, leurs tâches et leurs connexions, et qui lit quelle base ou appelle quelle connexion. Seulement ce que vous voyez : rien d’un projet invisible, ni de ce qui le lie aux autres. L’outil MCP get_organization { view: "map" } rend la même chose.

Lister les personnes entrées par la connexion d’entreprise

GET /v1/teams/{slug}/enterprise-people

Rend les personnes entrées par la connexion d’entreprise sans être membres : adresse, nom de l’émetteur, groupes reçus, applis ouvertes et la règle qui les y laisse, dates et session. Elles n’ont aucun droit dans while.

Fermer les sessions d’une personne d’entreprise

POST /v1/teams/{slug}/enterprise-people/{id}/close-sessions

Ferme ses sessions dans while, dans le realm et dans les applis ouvertes. Elle revient tant que la règle d’une appli la laisse passer : la réponse dit lesquelles, et comment l’en empêcher.

Lister les membres

GET /v1/teams/{slug}/members

Rend les membres de l’organisation : leur rôle, depuis quand, leur nom, leur dernière connexion, s’ils entrent par le SSO, leurs groupes et leurs agents branchés. Pour qui administre, en plus : les clés de comptes de service que chacun a créées (révoquées s’il part) et l’échéance de son invitation.

Inviter une personne

POST /v1/teams/{slug}/members

Réservé aux propriétaires et aux admins. La réponse est la même, que la personne ait déjà un compte while ou non : 202 { status, email, role, invitation: null, note }. status vaut « invitation envoyée », ou « invitation enregistrée » quand aucun courriel n’est parti : envoi impossible, ou adresse de test ou d’exemple, à laquelle while n’écrit jamais. Le lien part par courriel à l’adresse invitée, jamais à qui invite. Un rôle admin ou propriétaire s’applique après la validation d’une autre personne. 10 invitations d’adresses non membres par heure et par organisation (429, retry_after_s). Un admin le fait sans attendre personne, au journal (son agent, au plancher, le lui fait confirmer), sauf si l’organisation a réglé deux approbateurs.

Corps : email (obligatoire), role, reason.

Renvoyer une invitation

POST /v1/teams/{slug}/members/{email}/resend

Envoie un nouveau lien par courriel à l’adresse invitée ; l’ancien ne sert plus. Une fois par heure au plus.

Lire un lien de rattachement à la connexion d’entreprise

GET /v1/sso/link

Le lien reçu à l’adresse d’un compte while après sa première connexion par la connexion d’entreprise de son organisation (le SSO des membres, 30 minutes) : l’adresse du compte, l’organisation et l’émetteur. Un lien qui ne sert plus : 410.

Sans jeton.

Paramètres : j (obligatoire).

Relier son compte à la connexion d’entreprise

POST /v1/sso/link

Le titulaire confirme, une fois : son compte entre ensuite par la connexion d’entreprise. Aucune session ne s’ouvre ici ; né d’une session par mot de passe, le lien demande cette même session. to : la page où aller. Depuis une page de while (contrôle d’origine) ; un lien qui ne sert plus : 410.

Sans jeton.

Corps : j (obligatoire).

Lister les groupes

GET /v1/teams/{slug}/groups

Rend les groupes de l’organisation, faits dans while ou venus de la connexion d’entreprise, et leurs membres.

Créer un groupe

POST /v1/teams/{slug}/groups

Réservé aux admins.

Corps : name (obligatoire), sso, enterprise.

Relier un groupe à un groupe d’entreprise

PUT /v1/teams/{slug}/groups/{group}/enterprise

Relie un groupe while à un groupe de l’émetteur de la connexion d’entreprise, par l’identifiant stable du groupe chez l’émetteur, jamais par son nom ; entreprise: null le détache. Réservé à une personne admin, par sa session : un jeton reçoit 403. Relier fait entrer dans le groupe ceux que l’émetteur y annonce : il faut donc avoir soi-même ce que le groupe donne. Un groupe qui administre, ou désigné approbateur, attend une validation (428). Noté au journal.

Corps : enterprise.

Lier un groupe à un groupe du SSO

PUT /v1/teams/{slug}/groups/{group}/sso

Réservé aux admins ; sso: null le détache. À chaque connexion par le SSO, si le jeton d’identité porte la revendication groups, la personne entre dans les groupes while dont le sso y figure, et sort de ceux dont il n’y figure plus, sans tenir compte de la casse ni d’un « / » initial. Noté au journal.

Corps : sso.

Changer le rôle d’un membre

PUT /v1/teams/{slug}/members/{email}/role

Donne le nouveau rôle sur l’organisation et retire l’ancien, comme les outils MCP grant_role et revoke_role, et le note au journal. access.grant suffit, puisque vous ne donnez que ce que vous avez. Nommer un admin ou un propriétaire attend la validation d’une autre personne (202) : d’ici là, la personne garde son rôle, et l’ancien part une fois validé. Le rôle externe retire tout rôle sur l’organisation entière. Un admin le fait sans attendre personne, au journal (son agent, au plancher, le lui fait confirmer), sauf si l’organisation a réglé deux approbateurs.

Corps : role (obligatoire), reason.

Supprimer un groupe

DELETE /v1/teams/{slug}/groups/{group}

Réservé aux admins. Ses désignations d’approbateur partent avec lui ; un groupe désigné approbateur demande d’abord une validation (428).

Ajouter un membre à un groupe

POST /v1/teams/{slug}/groups/{group}/members

Réservé aux admins, pour un membre de l’organisation, et pas pour un groupe de la connexion d’entreprise. Un groupe qui administre, ou désigné approbateur, demande d’abord une validation (428).

Corps : email (obligatoire).

Retirer quelqu’un d’un groupe

DELETE /v1/teams/{slug}/groups/{group}/members/{email}

Réservé aux admins. Un groupe désigné approbateur demande d’abord une validation (428).

Retirer une personne de l’organisation

DELETE /v1/teams/{slug}/members/{email}

Réservé aux admins. Ses jetons et ses agents branchés sont révoqués. Seul un propriétaire retire un propriétaire, et le dernier propriétaire ne peut pas être retiré (409).

Lire la connexion d’entreprise

GET /v1/teams/{slug}/sso

Réservé aux admins. Rend l’émetteur OpenID, le client, les domaines et la revendication des groupes. Le secret du client est au coffre : seule sa présence est dite (secret: true). domain_proofs dit, pour chaque domaine, s’il est prouvé, et l’enregistrement DNS TXT à créer (_while-challenge.<domaine>, while-verification=<jeton>). Sans preuve, aucune adresse de ce domaine n’entre.

Régler la connexion d’entreprise

PUT /v1/teams/{slug}/sso

Une connexion OpenID Connect générique : Keycloak, Google Workspace, Microsoft Entra… while lit tout de suite sa découverte (/.well-known/openid-configuration, en https, à une adresse publique). Seules les adresses des domaines donnés entrent par cette connexion, une fois chaque domaine prouvé par un enregistrement DNS TXT (domain_proofs). L’adresse de retour à déclarer chez l’émetteur est https://app.while.sh/access/sso/callback. Noté au journal.

Corps : name, issuer, client_id, secret, domains, groups_claim, active, member_sso, member_passwords, provider, tenant_id.

Retirer la connexion d’entreprise

DELETE /v1/teams/{slug}/sso

Son secret est détruit, et les sessions ouvertes par elle sont révoquées.

Lire où en est la connexion d’entreprise

GET /v1/teams/{slug}/sso/setup

Réservé aux admins, même sans connexion (connection null) : le réglage et son fournisseur, l’adresse de retour à déclarer chez lui, la preuve de chaque domaine et son enregistrement TXT, la validation en cours et qui peut la donner, les groupes reliés, les applis déjà ouvertes, le dernier essai de la personne, et les membres qui perdraient l’organisation sans mot de passe.

Vérifier les domaines maintenant

POST /v1/teams/{slug}/sso/verify

Relit tout de suite l’enregistrement TXT de chaque domaine (une fois toutes les 15 secondes au plus) et rend leurs preuves. Réservé aux admins.

Faire entrer les membres par la connexion d’entreprise

PUT /v1/teams/{slug}/sso/members

Corps { member_sso, member_passwords, confirm } : les membres entrent avec leur compte d’entreprise, et le mot de passe leur reste permis ou non (seuls les propriétaires le gardent alors). confirm est exigé quand des membres perdraient l’organisation. Une personne admin, par sa session ; noté au journal.

Corps : member_sso, member_passwords, confirm.

Essayer la connexion d’entreprise

POST /v1/teams/{slug}/sso/trial

Rend l’adresse du fournisseur où la personne admin se connecte avec son propre compte ; rien n’est ouvert ni créé. Le navigateur garde la demande (un cookie) ; au retour, while revient à la page de la connexion d’entreprise avec ce que l’essai a vu. Depuis sa session seulement.

Lister mes invitations

GET /v1/invitations

Rend les organisations où votre compte est invité. Depuis la session de la personne invitée seulement, ni jeton ni agent. Une adresse pas encore prouvée, ni par le SSO ni par un lien reçu par courriel, n’en voit aucune : address_verified: false.

Accepter une invitation

POST /v1/invitations/{organization}/accept

La personne entre dans l’organisation avec le rôle proposé, donné au nom de qui l’a invitée, dans la limite de ses droits d’aujourd’hui (sinon granted_role: false). Un rôle admin ou propriétaire attend une validation. Depuis une session, avec une adresse prouvée : un jeton, un agent ou une adresse non prouvée reçoit 403 (adresse_a_prouver : ouvrir le lien reçu par courriel, ou se connecter par le SSO).

Décliner une invitation

POST /v1/invitations/{organization}/decline

L’invitation disparaît, et le journal de l’organisation le note. Depuis une session, avec une adresse prouvée.

Accuser réception d’une annonce

POST /v1/announcements/{id}/acknowledge

La personne dit avoir pris connaissance d’une annonce de l’équipe while ; c’est daté, et son bandeau se ferme. Une personne, par sa session ou son jeton ; une clé de compte de service reçoit 403. 404 si ce n’est pas une annonce en cours de ses organisations ; 409 si elle ne demande pas d’accusé. La deuxième fois, la première date reste.

Fermer une annonce pour soi

POST /v1/announcements/{id}/close

Le bandeau ne montre plus cette annonce à la personne. Par sa session seulement (403 par un jeton). Une urgente ne se ferme pas avant sa fin (403) ; une qui demande un accusé reste jusqu’à l’accusé (POST /v1/announcements/{id}/acknowledge). 404 si ce n’est pas une annonce en cours de ses organisations.

Droits et validations

Qui peut faire quoi, les demandes d’accès, les comptes de service, les approbateurs et les règles de validation.

Lister les identités

GET /v1/teams/{slug}/identities

Les personnes, les groupes, les comptes de service et les applis qui ont une attribution, chacune avec ses accès directs en cours. Qui voit l’organisation les voit toutes ; sinon, soi seulement.

La grille des accès aux sources

GET /v1/teams/{slug}/access/grid

Ce que peut chacun sur chaque source de données, d’après ses droits effectifs : les agents (avec les droits de leur personne), les applis, les tâches et les comptes de service en lignes, les sources en colonnes ; pour chaque case, lire et écrire (sur toute la source, ou une partie de ses chemins) et la régler. Toutes les sources pour qui donne des accès, sinon celles qu’on voit.

Changer qui ouvre un service

PUT /v1/projects/{slug}/environments/{env}/services/{service}/access

ouvert : public (tout internet), organization (ses membres) ou people (qui : des adresses, des @domaines, des groupes ; membres : true pour garder aussi les membres). Le while.yaml change et une nouvelle version part (statut inchangé, sans version, quand l’accès est déjà celui demandé) ; ouvrir à tout internet ou au-delà de l’organisation demande une validation par défaut (428), que les règles de l’organisation peuvent assouplir.

Corps : open (obligatoire), who, members.

Lire les règles de validation et le budget

GET /v1/teams/{slug}/rules

rules est la liste ordonnée des règles, chacune en clair : la première qui correspond l’emporte. budget_eur est le budget mensuel (null : sans plafond). defaults dit ce qui vaut sans règle : autonome dans le budget, même en production ; validation au-delà ; validation pour rendre une appli publique. floor liste ce qui demande toujours une validation. pending montre un assouplissement qui attend sa validation.

Changer les règles et le budget

PUT /v1/teams/{slug}/rules

Réservé aux admins. Envoie la liste entière des règles, le budget, ou les deux. Ce qui durcit s’applique tout de suite (200, status « appliquées »). Ce qui assouplit, même un seul cas, ou relève le budget (null : plus de plafond) passe en entier par une validation (202, status « validation humaine requise », avec la demande). Une règle sur un geste du plancher, comme détruire des données de production, changer les accès ou la politique, ou un appel « valider chaque appel », est refusée (400, avec la raison). Le journal note ce qui assouplit et ce qui durcit.

Corps : rules, budget_eur, required_approvals.

Lire qui a accès à un projet

GET /v1/projects/{slug}/access

Rend chaque attribution du projet, ou d’une de ses bases ou tables, en une phrase : « Léa Martin lit la table ventes.commandes (entrepot, pilotage) jusqu’au 30 novembre. Donné par Oscar le 21 octobre : audit du T4. » Avec ?qui=, tout ce que cette identité peut faire ; avec ?qui= et ?permission=, oui ou non, et la chaîne qui le permet : attribution, groupe, rôle, qui l’a donnée, quand et pourquoi. Les droits des autres se lisent là où vous avez access.grant.

Paramètres : activity, project, environment, database, schema, table, columns, connection, paths, who, permission.

Lire qui a accès à une ressource de l’organisation

GET /v1/teams/{slug}/access

Comme pour un projet, sur une ressource de l’organisation, ou sur l’organisation elle-même sans ressource : qui a accès, ce que peut faire une identité, et si elle peut faire une chose.

Paramètres : activity, project, environment, database, schema, table, columns, connection, paths, who, permission.

Donner un accès

POST /v1/teams/{slug}/attributions

Donne à une identité un rôle sur une portée, jusqu’à une date, pour une raison, comme l’outil MCP grant_role. Vous ne donnez que ce que vous avez (access.grant, et chaque permission du rôle sur la portée) : jamais à vous-même, ni par votre agent, ni à un groupe dont vous êtes membre, ni à un compte de service que vous utilisez. Sans échéance : seulement pour un admin de la portée, sinon 90 jours au plus. Donner le droit d’administrer des accès attend une validation (202). Un refus (403) dit ce qui manque. Tout va au journal (famille access).

Corps : who (obligatoire), role (obligatoire), scope, until, days, reason (obligatoire).

Retirer une attribution

DELETE /v1/teams/{slug}/attributions/{id}

L’attribution ne compte plus, tout de suite. La raison est obligatoire, et va au journal.

Paramètres : reason.

Corps : reason.

Lister les attributions inutilisées

GET /v1/teams/{slug}/attributions/unused

Rend les attributions que vous pouvez retirer, sans usage depuis 60 jours : ni action permise, ni appel. Le travail périodique les signale une fois, au journal. Pour en retirer une : DELETE …/attributions/{id} ; pour la garder : POST …/attributions/{id}/garder.

Garder une attribution inutilisée

POST /v1/teams/{slug}/attributions/{id}/keep

Son compte de 60 jours repart, sans inventer d’usage, et la raison va au journal. Qui peut retirer l’attribution peut la garder, jamais la sienne.

Corps : reason (obligatoire).

Exporter les attributions pour un auditeur

GET /v1/teams/{slug}/attributions/export

Il faut log.read sur l’organisation. En JSON : chaque attribution (en cours, échue ou retirée), ses entrées du journal (numéro, empreinte, précédente, contenu haché), la tête de la chaîne et sa vérification. En CSV : une ligne par attribution, puis une ligne « verification ». L’export est noté au journal (famille security).

Paramètres : format.

Lister les rôles

GET /v1/teams/{slug}/roles

Rend les rôles fournis par while et ceux de l’organisation, leurs permissions en clair, où ils se donnent et qui les tient, et ce que vous pouvez composer.

Lire un rôle

GET /v1/teams/{slug}/roles/{role}

Rend un rôle, fourni ou de l’organisation, par sa clé.

Créer ou modifier un rôle

PUT /v1/teams/{slug}/roles/{role}

Compose un rôle de l’organisation à partir du catalogue des permissions. Il faut roles.define, et vous ne composez qu’avec ce que vous avez : chacune des permissions du rôle sur l’organisation. Modifier un rôle tenu revient à le redonner à ses titulaires : jamais pour un rôle que vous tenez vous-même (403), et un rôle tenu ne gagne jamais access.grant, org.admin ni roles.define (409). Tout va au journal (famille access), refus compris.

Corps : name, description, permissions (obligatoire), reason (obligatoire).

Supprimer un rôle

DELETE /v1/teams/{slug}/roles/{role}

Il faut roles.define. Seulement quand plus aucune attribution en cours ne le donne.

Paramètres : reason.

Corps : reason.

Lister les demandes d’accès

GET /v1/teams/{slug}/access-requests

Rend les demandes que vous pouvez trancher, les vôtres, et vos attributions qui arrivent à échéance, avec l’appel qui les renouvelle.

Demander un accès

POST /v1/teams/{slug}/access-requests

Demande un droit pour soi : une personne, son agent pour elle, ou un compte de service par sa clé. Sert aussi à renouveler une de ses attributions (renew), ou à demander l’appel d’une connexion pour une appli (for). Se fait après un 403 qui porte missing : le plus petit rôle, la plus petite portée, l’appel tout prêt. Champs : role, ou permission (while prend le plus petit rôle qui la donne), scope, reason (obligatoire), days (7 par défaut, 90 au plus). Ceux qui peuvent l’accorder sont prévenus (À traiter, courriel, et leurs agents sur while://access/events) ; sans réponse en 72 heures, elle expire. Une demande identique encore ouverte est rendue telle quelle (200), et 10 demandes au plus restent ouvertes (429). Notée au journal (famille access).

Corps : role, permission, scope, days, reason (obligatoire), renew, for.

Lire une demande d’accès

GET /v1/teams/{slug}/access-requests/{id}

Rend sa phrase, qui peut l’accorder, sa décision et ce que vous pouvez en faire. Visible par qui l’a faite et par qui donne des accès sur sa portée (access.grant) ; sinon 404.

Annuler sa demande d’accès

DELETE /v1/teams/{slug}/access-requests/{id}

Seulement tant qu’elle est ouverte.

Paramètres : reason.

Accorder une demande d’accès

POST /v1/teams/{slug}/access-requests/{id}/grant

Une personne qui peut donner ce droit l’accorde, au plus ce qui est demandé, et la demande devient une attribution bornée. Jamais sa propre demande, jamais par un agent ni un compte de service (403). Champs : role (ses permissions parmi celles demandées), scope (contenue dans celle demandée), until ou days (au plus la durée demandée, 90 jours, toujours une échéance), reason (obligatoire). Une demande de niveau admin va aux propriétaires : un admin de la portée qui n’est pas propriétaire la refuse, ou donne le rôle lui-même, et une autre personne le valide. Accorder un renouvellement prolonge l’attribution. Noté au journal.

Corps : role, scope, until, days, reason (obligatoire).

Refuser une demande d’accès

POST /v1/teams/{slug}/access-requests/{id}/deny

Une personne qui pourrait l’accorder la refuse, avec une raison, qui part à qui a demandé.

Corps : reason (obligatoire).

Lister les comptes de service

GET /v1/teams/{slug}/service-accounts

Rend chaque compte de service de l’organisation : sa phrase, ses droits, ses clés (préfixe, échéance, dernier usage) et qui l’utilise. Qui voit les membres (org.view) les voit tous ; sinon, ceux qu’il gère (access.grant sur leur portée). Un compte lit le sien par sa clé.

Créer un compte de service

POST /v1/teams/{slug}/service-accounts

Crée une identité non humaine (tableau de bord, script, agent autonome), sans droit ni clé. Il faut être une personne, ou son agent, avec access.grant sur sa portée : l’organisation, ou le projet donné. Ses droits se donnent ensuite par une attribution (qui : compte_service:<nom>), par une autre personne que celles qui l’utilisent, puis une clé. Noté au journal.

Corps : name (obligatoire), description, reason (obligatoire), project.

Lire un compte de service

GET /v1/teams/{slug}/service-accounts/{account}

Rend sa phrase, ses droits en phrases et ses clés.

Supprimer un compte de service

DELETE /v1/teams/{slug}/service-accounts/{account}

Ses attributions sont retirées (au journal), ses clés partent et son nom se libère.

Paramètres : reason.

Désactiver un compte de service

POST /v1/teams/{slug}/service-accounts/{account}/disable

Ses clés ne passent plus, ses droits ne comptent plus, et ses sessions SQL sont coupées.

Corps : reason (obligatoire).

Réactiver un compte de service

POST /v1/teams/{slug}/service-accounts/{account}/reactivate

Ses clés repartent. Il faut avoir déjà chacun de ses droits.

Corps : reason (obligatoire).

Créer une clé de compte de service

POST /v1/teams/{slug}/service-accounts/{account}/keys

Crée une clé whcs_…, montrée une seule fois, toujours à échéance : 90 jours au plus, un an pour un admin de la portée du compte. La clé donne à qui la tient tout ce que le compte peut faire : il faut gérer le compte (access.grant sur sa portée) et avoir déjà chacun de ses droits. Elle agit avec exactement ses attributions, sur l’API et le MCP, dans l’en-tête Authorization: Bearer. Le journal note la création, jamais le secret.

Corps : name, until, days, reason.

Tourner une clé

POST /v1/teams/{slug}/service-accounts/{account}/keys/{key}/rotation

Crée une nouvelle clé, aux mêmes règles qu’une création. L’ancienne vaut encore le temps du chevauchement : 24 heures par défaut, de 0 à 168.

Corps : name, until, days, overlap_hours.

Révoquer une clé

DELETE /v1/teams/{slug}/service-accounts/{account}/keys/{key}

Elle est refusée dès la requête suivante. La raison va au journal.

Paramètres : reason.

Lister qui valide quoi

GET /v1/teams/{slug}/approvers

Rend, pour chaque ressource (organisation, projets, bases, connexions), son propriétaire et ses approbateurs. Sans désignation, valident le propriétaire de la ressource et les admins de l’organisation ; une base hérite de son projet.

Désigner un approbateur

POST /v1/teams/{slug}/approvers

Désigne une personne ou un groupe comme approbateur d’une ressource. Réservé aux admins.

Corps : resource_type (obligatoire), resource, subject_type (obligatoire), subject (obligatoire).

Retirer un approbateur

DELETE /v1/teams/{slug}/approvers/{id}

Retire une désignation d’approbateur. Réservé aux admins.

Revoir les accès

GET /v1/teams/{slug}/access/review

Réservé aux admins. Rend qui lit, met en ligne et valide chaque ressource, les jetons à revoir et les agents branchés. Un agent hérite des droits de sa personne. Un jeton est « à revoir » quand il n’expire pas, qu’il n’a pas servi depuis 30 jours, ou qu’il a plus de droits que sa personne (ils sont alors plafonnés).

Lire une demande de validation

GET /v1/approvals/{id}

Rend la demande : son niveau, son empreinte, son expiration, ses approbateurs, et ce que vous pouvez en faire.

Lire le corps d’un appel retenu

GET /v1/approvals/{id}/body

Le corps d’un appel retenu (« valider chaque appel »), déchiffré pour une personne qui peut décider cette demande, connectée à while : jamais l’agent qui l’a faite, jamais un jeton. Son texte en UTF-8 (null s’il est binaire), borné à 32 000 caractères (truncated au-delà), son type, sa taille et son empreinte (la même que celle de la demande). Chaque lecture va au journal de l’organisation, sans le contenu. 404 pour qui ne peut pas décider, et une fois la demande tranchée ou expirée.

Valider une demande

POST /v1/approvals/{id}/approve

Une demande se tranche par une autre personne, approbatrice de la ressource, connectée à while : jamais sa propre demande, ni celle de son agent (même compte, 403). Un jeton (d’agent, OAuth ou de compte de service) ne tranche ni ne force jamais : 403, raison « agent », avec le lien de la demande à transmettre à sa personne (action.lien). Refuser et passer en force demandent un motif (400 sans motif) ; passer en force est réservé aux admins de l’organisation et émet l’événement sh.while.approval.forced. Une demande déjà tranchée ou expirée rend 409.

Corps : reason.

Refuser une demande

POST /v1/approvals/{id}/reject

Une demande se tranche par une autre personne, approbatrice de la ressource, connectée à while : jamais sa propre demande, ni celle de son agent (même compte, 403). Un jeton (d’agent, OAuth ou de compte de service) ne tranche ni ne force jamais : 403, raison « agent », avec le lien de la demande à transmettre à sa personne (action.lien). Refuser et passer en force demandent un motif (400 sans motif) ; passer en force est réservé aux admins de l’organisation et émet l’événement sh.while.approval.forced. Une demande déjà tranchée ou expirée rend 409.

Corps : reason.

Passer une demande en force

POST /v1/approvals/{id}/force

Une demande se tranche par une autre personne, approbatrice de la ressource, connectée à while : jamais sa propre demande, ni celle de son agent (même compte, 403). Un jeton (d’agent, OAuth ou de compte de service) ne tranche ni ne force jamais : 403, raison « agent », avec le lien de la demande à transmettre à sa personne (action.lien). Refuser et passer en force demandent un motif (400 sans motif) ; passer en force est réservé aux admins de l’organisation et émet l’événement sh.while.approval.forced. Une demande déjà tranchée ou expirée rend 409.

Corps : reason.

Lister les demandes en attente

GET /v1/approvals

Rend les demandes des projets que vous voyez, et celles de vos organisations selon votre rôle. Un jeton de projet ne voit que celles de son projet.

Paramètres : organization.

Lister les demandes de validation d’une organisation

GET /v1/teams/{slug}/approvals

Les demandes de validation de cette organisation que votre identité voit, en attente et tranchées, les plus récentes d’abord (50 au plus) ; chacune comme GET /v1/approvals/{id} la rend : qui l’a tranchée (decided_by), quand (decided_at), son motif (decision_reason). Une demande passée sans réponse est rendue expirée.

Journal

Le journal de l’organisation : le lire, l’exporter, le vérifier et l’envoyer à un SIEM.

Lire le journal de l’organisation

GET /v1/teams/{slug}/activity-log

Qui a fait quoi, par quoi, sur quoi, du plus récent au plus ancien. Le journal s’écrit en ajout seul, chaîné par empreinte, et se garde 13 mois par défaut. Un admin de l’organisation lit tout ; un membre lit les entrées des projets qu’il voit et ses propres actions ; un jeton de projet, son projet. Les appels des connexions et les lectures SQL n’y laissent que des métadonnées : la requête normalisée sans ses valeurs, jamais de résultat.

Paramètres : tab, person, via, family, project, resource, since, until, cursor, limit.

Exporter le journal en JSON Lines

GET /v1/teams/{slug}/activity-log.jsonl

Une entrée par ligne, avec les mêmes filtres que la lecture ; il faut le droit d’exporter le journal, plus large que celui de le lire. L’export est noté au journal.

Paramètres : tab, person, via, family, project, resource, since, until, cursor.

Exporter le journal vérifiable

GET /v1/teams/{slug}/activity-log/verifiable.jsonl

Toute la chaîne de l’organisation, revérifiable hors de while. En JSONL : un en-tête (l’ancre de départ, les caviardages), chaque entrée avec son rang (seq), son empreinte, la précédente et le contenu haché, puis une fin, la tête et sa signature Ed25519. ?depuis=<rang> donne la suite d’un export précédent. Il faut log.export sur l’organisation entière.

Paramètres : since.

Lire les clés publiques du journal

GET /journal/o/{organization}/cles.json

Rend chaque version des clés publiques du journal d’une organisation, l’ancienne comprise, par l’identifiant stable de l’organisation.

Lister les flux vers un SIEM

GET /v1/teams/{slug}/activity-log/streams

Réservé aux admins. Rend chaque flux du journal vers un SIEM et son état : curseur, dernier envoi, retard, erreurs.

Ajouter un flux vers un SIEM

POST /v1/teams/{slug}/activity-log/streams

Une personne admin, par sa session, avec l’offre Entreprise. Corps : { name, url, since, delay_threshold_minutes }, avec une url en https, publique, et since à maintenant ou à start. while envoie des lots JSONL signés comme les alertes (Standard Webhooks) ; le rang fait foi. La clé de signature n’est rendue qu’une fois.

Corps : name, url, since, delay_threshold_minutes.

Retirer un flux vers un SIEM

DELETE /v1/teams/{slug}/activity-log/streams/{stream}

Réservé aux admins, dans leur session : un jeton ne retire pas un flux.

Vérifier la chaîne du journal

GET /v1/teams/{slug}/activity-log/verification

Toute personne qui lit le journal. Rend ok, ou la première entrée qui ne colle pas. Après une purge de conservation, la chaîne repart de l’empreinte de la dernière entrée purgée (since_purge).

Régler la conservation du journal

PUT /v1/teams/{slug}/activity-log/retention

Réservé aux admins : de 1 à 120 mois, 13 par défaut. La purge passe chaque jour.

Corps : month (obligatoire).

Lire la conservation des appels des connexions

GET /v1/teams/{slug}/activity-log/call-retention

Rend la durée, en jours, pendant laquelle le journal des appels des connexions est gardé.

Régler la conservation des appels des connexions

PUT /v1/teams/{slug}/activity-log/call-retention

Il faut org.admin. De 90 jours à 5 ans, 400 jours par défaut ; la durée vaut aussi pour les appels déjà gardés.

Corps : days (obligatoire).

Consommation et quotas

Ce que consomment les applis, ce que coûte un mois, et les limites.

Lire la consommation d’un projet

GET /v1/projects/{slug}/usage

Rend la consommation du mois (CPU, mémoire, requêtes, constructions, bases, caches), son coût, la projection des alertes et le budget. per_month_euros est ce que coûterait un mois normal à ce rythme : les bases et les caches à leur réservation d’aujourd’hui, l’appli à sa consommation moyenne des 7 derniers jours (ou depuis sa mise en ligne), sur un mois entier, sans le forfait de l’offre, qui est celui de l’organisation. Le coût suit les prix de l’organisation, ceux de son abonnement (sa grille, son niveau, ses remises) ou, sans abonnement, la grille de référence à son niveau par défaut ; il est donné à ce jour et projeté, et detail le donne par ligne de la grille. pricing donne ces prix : price_list, subscription, level, levels[], resources[] { key, label, unit, price_cents, by, text, negocie?, prix_grille_centimes? }, options[], discounts[] { target, text }, included[] et examples.

Lire le relevé mensuel

GET /v1/teams/{slug}/statement

Rend les quantités du mois qu’un modèle de prix facturerait, sans les prix : le calcul consommé (vCPU·h, Gio·h) des applis, des caches et des bases ; ce que les bases réservent (taille, haute disponibilité, disque) ; le stockage objet ; les requêtes ; les constructions ; les connexions avec et sans clé, et leurs appels ; les développeurs, lecteurs, membres et comptes de service ; les réglages (SSO, droits fins, conservation). Le relevé est provisoire pendant le mois, recalculé chaque heure, et clos une heure après sa fin. Réservé aux admins de l’organisation.

Paramètres : month, recompute.

Lire la consommation de l’organisation

GET /v1/teams/{slug}/usage

Réservé aux admins de l’organisation. Le montant principal est euros_per_month, en euros HT : ce que coûterait un mois normal à ce rythme, le même que la page Consommation et l’outil MCP get_organization. per_month le détaille : le forfait de l’offre, les bases et les caches à leur réservation d’aujourd’hui, les applis à leur consommation moyenne des 7 derniers jours, sur un mois entier ; calculation dit comment. since_start est ce qui est dû depuis le 1er du mois, ou depuis la création (since), c’est-à-dire ce qui sera facturé pour ce mois. estimated_month_end est la projection des alertes de budget, et per_app détaille chaque appli, brique par brique. Les lignes de per_month et de since_start sont en centimes HT, avec leur devise.

Lire les quotas

GET /v1/teams/{slug}/quotas

Réservé aux admins de l’organisation. Pour chaque ressource : la limite, par défaut ou relevée, l’usage, et si elle est atteinte. Une limite se relève sur demande à l’équipe while. Au-delà d’une limite, la création est refusée : 409 pour un stock, 429 pour un débit (avec retry_after_s), avec un Problem de type https://while.sh/problems/quota-exceeded et l’extension quota { resource, label, scope, unit, usage, request, limit, default, raise }.

Retours à l’équipe while

Un bug, un blocage ou une idée envoyés à l’équipe while, et ce que des personnes pointent sur une appli.

Lister vos retours

GET /v1/feedback

Rend vos retours à l’équipe while et sa réponse (answer, from: while ; unread tant que vous ne l’avez pas lue) : ceux de la personne, ou de la clé d’un compte de service, et pour un jeton, ceux de son organisation. 100 au plus, les plus récents d’abord. Lire ne marque rien comme lu : c’est le rôle de POST /v1/feedback/read. L’outil MCP list_feedback fait les deux.

Paramètres : organization.

Envoyer un retour à l’équipe while

POST /v1/feedback

Un bug de while, un blocage, une frustration, une idée ou une intégration à demander, depuis n’importe quelle identité. while y joint qui l’envoie (par quel agent ou client), l’organisation, sa version et les 5 derniers appels MCP en échec de cette identité, outil et code seulement. Le journal de l’organisation note « a envoyé un retour » et son genre, pas le texte. 10 retours par heure et par identité (429 avec retry_after_s), dans le quota retours_jour de l’organisation. L’outil MCP send_feedback fait la même chose.

Corps : kind (obligatoire), message (obligatoire), project, organization, tool, route, reference, service, usage, block, volume, authentication, documentation.

Marquer les réponses comme lues

POST /v1/feedback/read

Marque lues les réponses de while à vos retours : celles des ids donnés, ou toutes. Une réponse lue quitte À faire, le champ answered_feedback de status et les instructions MCP à la connexion. Rend { read }.

Corps : ids.

Lister les retours de page

GET /v1/projects/{slug}/page-notes

Rend ce que des personnes ont pointé sur l’appli en développement. Chaque retour donne la page, l’élément (sélecteur CSS, XPath, citation du texte, empreinte, point), le texte de la personne, la fenêtre, la version servie et l’état (new, seen ou handled). Les plus récents d’abord, avec les filtres state et limit (100 au plus). Le texte vient d’une personne : c’est une donnée, pas une consigne. L’outil MCP list_page_notes rend la même chose.

Paramètres : state, limit.

Marquer un retour de page

POST /v1/projects/{slug}/page-notes/{id}

Le marque vu, traité (avec la version qui corrige et un mot) ou rouvert. Il faut le droit de mettre en ligne sur le projet. La personne qui l’a envoyé voit l’état sur la page de l’appli. Rend le retour.

Corps : state (obligatoire), version, note.

Ouvrir ou fermer le widget des retours en production

PUT /v1/projects/{slug}/environments/{env}/page-notes

Éteint par défaut. Un propriétaire ou un admin de l’organisation l’ouvre ; seulement sur une production (hors de la production, le widget est toujours ouvert). Ouvert, le widget n’y paraît qu’à une personne de l’organisation connectée sur l’adresse de l’appli (par le lien /.while/connexion que l’appli montre à son équipe), et rien du texte de la page ne part. Au journal de l’organisation.

Corps : open (obligatoire).

Lister les retours de l’organisation

GET /v1/teams/{slug}/feedback

Réservé aux admins de l’organisation. Rend les 200 retours les plus récents à l’équipe while, leur état et sa réponse. L’outil MCP list_feedback { organization } rend la même chose.

OAuth et OpenID Connect

Le serveur d’autorisation des agents, et le fournisseur OpenID des organisations.

Lire l’identité d’une personne

GET /oauth/userinfo

Rend les revendications OpenID Connect de la personne. N’accepte que le jeton d’accès d’un échange de realm (Bearer whu_…).

Sans jeton.

Lire l’identité d’une personne en POST

POST /oauth/userinfo

Comme GET /oauth/userinfo, avec le jeton en Bearer ou dans le corps (access_token).

Sans jeton.

Ouvrir la page de consentement

GET /oauth/authorize

L’adresse d’autorisation des clients OAuth (RFC 6749) : la page de consentement de l’interface, à une session (sans elle, la connexion, qui y ramène). Un membre qui ouvre une appli interne de son organisation repart aussitôt vers son realm, avec son code.

Sans jeton.

Enregistrer un client OAuth

POST /oauth/register

Enregistre dynamiquement un client public (RFC 7591). PKCE est obligatoire.

Sans jeton.

Corps : client_name, redirect_uris (obligatoire), token_endpoint_auth_method.

Obtenir un jeton OAuth

POST /oauth/token

Échange un code d’autorisation, avec son code_verifier PKCE, ou fait tourner un jeton de rafraîchissement. Un jeton de rafraîchissement déjà utilisé qui revient révoque toute la session. Le client confidentiel du realm d’une organisation (realm-org-<n>, ouvert par while) s’authentifie en client_secret_basic ou client_secret_post, et reçoit un id_token ES256 sans jeton de rafraîchissement.

Révoquer un jeton OAuth

POST /oauth/revoke

Révoque un jeton (RFC 7009). Révoquer un jeton de rafraîchissement révoque toute sa session.

Sans jeton.

Lire les métadonnées du serveur OAuth

GET /.well-known/oauth-authorization-server

Rend les métadonnées du serveur d’autorisation OAuth 2.1 de while (RFC 8414), aussi servies sous /.well-known/openid-configuration.

Sans jeton.

Découvrir la configuration OpenID Connect

GET /.well-known/openid-configuration

Rend les métadonnées OAuth des agents, et celles du fournisseur OpenID des realms des organisations : userinfo, jetons d’identité ES256, portées openid, email et profile.

Sans jeton.

Lire les métadonnées de la ressource MCP

GET /.well-known/oauth-protected-resource

Rend les métadonnées de la ressource protégée MCP (RFC 9728) : ses serveurs d’autorisation et ses droits.

Sans jeton.

Prochaines étapes

Branchez votre agent, il s’occupe du reste

À coller dans votre agent

Lis while.sh/agents, puis mets mon appli en ligne avec while.

Copier les instructions

Pas besoin de compte pour essayer : votre appli est en ligne tout de suite. Pour la garder, connectez-vous.

Un projet pour votre entreprise ? Parlez à l’équipe : hello@while.sh