Applications
148 routes. Chemins relatifs à https://api.nexus-engine.eu/api/v1.
GET
/capacites
Les capacités des applications EN VIGUEUR pour l’espace du membre : plafond d’envois sans personne devant l’écran (par application, sur 24 heures), liens et durée de l’accord permanent de partage.
RéponseCapacitesDto { envoisSansPersonne: { parApplication24h }, partage: { liensParAccord, joursMax } }
NoteRéglé par l’exploitant au back-office ; lecture seule, rien de secret. Un poste qui ne sait pas la lire garde ses défauts.
GET
/projects/:id/application
L’application que ce projet fabrique, s’il en fabrique une — c’est ce qui fait de lui un ATELIER. `null` est une réponse normale et le cas ordinaire.
RéponseApplicationDto | null
GET
/applications
Mes applications, avec MON installation sur CET appareil (nom local, icône, favori, dernière ouverture). Jamais celles d’un autre membre.
RéponseApplicationDto[]
NoteL’installation est résolue d’après l’en-tête x-nexus-appareil : sans en-tête (clé d’API, vue web), elle est nulle — ce qui ne veut pas dire « rien n’est installé ».
POST
/applications
session de l’application seulement
Créer une application : son projet d’atelier, son rattachement à la collection Applications et l’application elle-même, en une opération REPRENABLE. Rejouée avec le même requestId et le même contenu, elle ne crée rien de plus.
CorpsrequestId (identifiant de la demande, fabriqué avant l’appel), nom (facultatif), premierMessage (facultatif : messageId + texte — ce que la personne venait d’écrire, gardé dès la création), sourceApplicationId (facultatif : l’application du catalogue dont celle-ci est la COPIE)
Réponse{ statut: "creee" | "reprise", operation: ApplicationOperationDto }
NoteFermée aux clés d’API : elle crée un PROJET, et une clé posée dans un CI n’en crée pas à la chaîne. OUVERTE À UN AGENT LOCAL depuis le 2 octobre 2026 (décision de Pierre : tout agent, d’un projet ou l’assistant, crée une application pour la personne — outil `nexus_creer_application`), journalisée à son nom. Rejouée avec un AUTRE contenu : 409. La COPIE d’une application de son PROPRE espace : 409 `COPIE_DANS_SON_ESPACE` (5 octobre 2026) — dans son espace, une application s’ouvre à l’équipe par l’audience, jamais par une copie, qui ferait un projet de plus par collègue.
POST
/applications/pdf
LE SERVICE PDF DES APPLICATIONS : fusionner des PDF en un seul, ou poser un tampon / un filigrane (texte, image, « Page {page} sur {pages} »).
Corps{ operation: "fusionner", pdfs: [base64, …] (2 à 20) } ou { operation: "tamponner", pdf: base64, tampon: { texte | image: { base64, mime }, position?, pages?, opacite?, taille?, couleur?, margePt? } }
Réponse{ pdfBase64, pages, octets }
NoteRien n’est lu ni gardé : des octets entrent, des octets sortent, le contenu n’est ni stocké ni journalisé. 16 Mo au plus pour les PDF d’un appel, 2 000 pages, 2 Mo pour l’image d’un tampon ; 30 appels par minute. Un fichier qui n’est pas un PDF lisible : 422 ILLISIBLE ; un PDF chiffré : 422 CHIFFRE (on ne retire pas une protection) ; un texte hors de l’alphabet latin : 422 TEXTE (la police du tampon est Helvetica — employer une image). Le pont des applications y mène par `nexus.studio.fusionner` et `.tamponner` ; la page vers PDF, elle, se fait sur le poste (`nexus.studio.pdf`).
POST
/applications/base
session de l’application seulement
Créer une APPLICATION DE NEXUS (Message, Agent…) dans l’espace de l’éditeur, déjà marquée de base — ou rendre celle qui porte déjà ce manifeste. Appelée par la publication de base, jamais à la main.
CorpsmanifesteId (l’identifiant du manifeste), nom
Réponse{ statut: "creee" | "existante", applicationId }
NoteTrois conditions, dans cet ordre : l’espace ÉDITEUR — atteint depuis N’IMPORTE QUEL espace quand la requête porte une preuve juste, qui la fait servir au nom de Nexus (`deleguerAuNomDeNexus`, 5 octobre 2026) ; sans elle, 403 `PAS_L_EDITEUR` —, la preuve de la clé de l’éditeur (403 `PREUVE_ABSENTE`), une preuve juste — donc une personne en session (403 `PREUVE_REFUSEE`). Le geste d’une PERSONNE (`ROUTES_SESSION_SEULE`). Idempotente : rejouée, elle rend la même application.
GET
/applications/operations/:id
Où en est une création : l’étape atteinte, la discussion ouverte, le problème éventuel. C’est ce qui permet de reprendre depuis un autre écran.
RéponseApplicationOperationDto
PATCH
/applications/operations/:id
Ce que le poste annonce d’une création en cours : l’étape, la discussion qu’il a ouverte, ce qui bloque.
Corpsetape, discussionId, probleme (code de la taxonomie), diagnostic (déjà expurgé), premierMessageEnvoye (le messageId qui vient d’être confié à un agent)
RéponseApplicationOperationDto
NoteUne étape ne RECULE jamais : un accusé en retard est ignoré, pas appliqué.
GET
/applications/installations/:id/machines
L’accord VIVANT par lequel cette installation fait employer par Nexus les accès SSH de machines du coffre (`nexus.machines`) : les machines nommées que la personne voit encore, l’échéance. `null` sans accord.
Réponse{ accord: AccordMachinesDto | null }
NoteL’installation est celle de qui appelle ; ailleurs : 404. Les machines sont relues sous la portée d’aujourd’hui : `retirees` compte celles qui ne le sont plus.
PUT
/applications/installations/:id/machines
session de l’application seulement
ACCORDER à une application l’emploi des accès SSH de machines NOMMÉES du coffre — exécuter, copier un flux vers une autre machine ou un dossier du poste. L’application ne voit jamais une clé : c’est Nexus qui l’emploie. Remplace l’accord précédent.
CorpsserverIds (1 à 500, des machines que la personne VOIT)
Réponse{ accord: AccordMachinesDto }
NoteLe geste d’une personne (`ROUTES_SESSION_SEULE`). La version installée doit déclarer `machines` au manifeste (409 `NON_DECLARE`). Une machine hors de portée refuse tout l’accord (404). 90 jours. Journal `application.machines-accordees` ; les administrateurs sont prévenus (APPLICATION_ALERT).
DELETE
/applications/installations/:id/machines
RÉVOQUER l’accord d’emploi des machines : effet immédiat, pour l’emploi suivant.
Réponse{ revoques }
NoteOuvert à une clé : couper se fait de partout. Journal `application.machines-revoquees`.
GET
/applications/installations/:id/machines/:serverId/acces
Les accès SSH d’une machine de l’accord que Nexus sait employer pour l’application — des fiches, jamais un secret.
Réponse{ acces: AccesDeMachineDto[] }
NoteMachine hors de l’accord ou plus à portée : 403 `HORS_ACCORD` ; sans accord vivant : 403 `NON_ACCORDE`.
GET
/applications/installations/:id/machines/:serverId/acces/:credentialId/secret
session de l’application seulement
La clé d’un accès d’une machine de l’accord, pour le processus principal du POSTE qui l’emploie le temps d’un geste — jamais rendue à la page de l’application.
Réponse{ secret, acces, machine }
NoteLe geste du poste, sous la session de la personne (`ROUTES_SESSION_SEULE`). Hors du plafond des secrets par membre — le périmètre est la liste nommée à l’accord — mais garde d’emballement par accord (429 `PLAFOND`). Chaque emploi : journal `secret.employe-par-application`, qui nomme l’application.
GET
/applications/installations/:id/ouverture
Tout ce qu’il faut pour OUVRIR une installation : la version pointée, son manifeste validé et les capacités accordées. Le poste ne nomme que l’installation ; le reste vient d’ici.
Réponse{ applicationId, nom, versionId, versionLibelle, manifeste, capacites }
NoteUne seule lecture, et elle fait autorité : une fenêtre ouverte sur un manifeste composé par l’écran se servirait elle-même sa politique de contenu et ses domaines autorisés. L’installation est celle de qui appelle ; ailleurs : 404.
GET
/applications/:id/versions
Les versions FIGÉES d’une application, de la plus récente à la plus ancienne.
RéponseApplicationVersionDto[]
GET
/applications/:id/versions/:versionId
UNE version figée d’une application, par son identifiant.
RéponseApplicationVersionDto
NoteMême lecture que la liste : une application qu’on ne voit pas, ou une version d’une autre application, répond 404.
POST
/applications/:id/versions
Figer une version : le manifeste est VALIDÉ par le serveur avant d’entrer en base, et une version figée ne se remplace jamais.
Corpsmanifeste (brut, validé ici), empreinte (SHA-256 du bundle), note, versionNexus (pour juger nexusMin)
RéponseApplicationVersionDto
NoteOuverte à un agent de l’atelier : c’est le geste de qui fabrique. Deux fois le même numéro : 409. Le manifeste ne porte AUCUNE commande, et le schéma n’a pas de champ pour ça.
POST
/applications/:id/installations
Installer ou mettre à jour sur CE poste : on vérifie d’abord (version, compatibilité des données, capacités accordées), on bascule ensuite.
CorpsversionId, capacites (celles que la personne accorde)
RéponseApplicationInstallationDto
NoteUn retour vers une version qui ne saurait pas lire les données déjà écrites est BLOQUÉ (409) : l’installée reste utilisable. Sans en-tête d’appareil : 400.
PATCH
/applications/installations/:id
Ce que le détenteur change chez lui : nom local, icône, favori, et la date d’ouverture sur laquelle le lanceur trie.
CorpsnomLocal, iconeLocale, favorite, ouverte
RéponseApplicationInstallationDto
DELETE
/applications/installations/:id
Désinstaller de ce poste.
NoteN’efface AUCUNE donnée : ce que l’application a écrit vit sur le poste, et l’effacer est un autre geste. Désinstaller et supprimer ses données sont deux opérations distinctes.
GET
/applications/statistiques
Les statistiques des applications que je MAINTIENS : installations, personnes actives sur 1, 7 et 30 jours, ouvertures par jour sur 90 jours, temps passé, versions en service, canal d’essai, personnes parties, tendance, et une phrase qui tranche.
RéponseStatistiquesApplicationsDto
NoteDes AGRÉGATS, jamais un nom ni un espace. L’usage vient de ce que les postes mesurent déjà (une application sous les yeux de la personne). L’original et ses copies dans tous les espaces ; votre propre usage compté à part (`horsVous`). Un chiffre non mesuré vaut null (« — »), jamais 0 ; `mesureDepuis` dit le premier jour mesuré. Maintenir = en être l’auteur, ou administrer l’espace éditeur pour une application de base.
GET
/applications/:id/statistiques
Les statistiques d’une seule application que je maintiens.
RéponseStatistiquesApplicationDto
NoteMême juge que la liste. Une application que je ne maintiens pas : 404, jamais 403.
GET
/applications/:id
La fiche d’une application : état, dernière version, mode, et mon installation ici.
RéponseApplicationDto
NoteLisible par son propriétaire ou par un membre de son atelier. Ailleurs : 404, jamais 403.
GET
/applications/:id/suppression
L’aperçu de la suppression d’une application : ce qui part, ce qui reste, et si je peux la supprimer.
RéponseApplicationSuppressionDto
NoteSes versions, ses installations (dont celles des autres membres), sa publication au catalogue, son atelier ; les copies reprises ailleurs RESTENT. Une application d’une autre entreprise : 404.
DELETE
/applications/:id
session de l’application seulement
Supprimer une application, en construction ou diffusée — avec son atelier.
Réponse{ ok, nom, installationsRetirees, secretsATourner }
NoteLe PROPRIÉTAIRE ou un administrateur de l’espace (403 sinon). Le geste d’une PERSONNE (`ROUTES_SESSION_SEULE`). Passe par le moteur des projets : secrets à tourner signalés, journal qui nomme ce qui part. Les copies reprises par d’autres espaces continuent de marcher, sans mise à jour.
PATCH
/applications/:id
Renommer une application, changer son icône ou sa couleur, la retirer du lanceur.
Corpsnom, icone, couleur, retiree
RéponseApplicationDto
NoteLe PROPRIÉTAIRE seul. Un membre de l’atelier la lit sans la modifier (403) ; qui ne la voit pas reçoit 404.
PUT
/applications/:id/audience
Qui a le droit de se servir de cette application : son auteur seul, tout l’espace de travail, ou des membres nommés.
Corpsportee ("AUTEUR" | "ESPACE" | "CHOISIS"), membreIds (nommés, 200 au plus)
Réponse{ application: ApplicationDto, ecartes: string[] }
NoteUN PUT, et non un PATCH : l’écran montre la liste entière, et ce qu’il envoie est ce que la personne voit. Le PROPRIÉTAIRE seul décide ; le service rejuge et NOMME qui a été écarté, plutôt que de laisser croire que tout est passé. Le journal porte la portée et le NOMBRE, jamais la liste des noms — une ligne d’audit qui recopie un annuaire devient elle-même une fuite. PUBLIER (ESPACE ou CHOISIS) exige une version STABLE dont le serveur a le programme (409 `NON_PUBLIABLE`) ; ESPACE publié par qui n’est pas administrateur reste EN ATTENTE (`publicationEnAttente`) jusqu’à la décision d’un administrateur. Dépublier (AUTEUR) ne se refuse jamais.
GET
/applications/publications/en-attente
Les applications que leur auteur veut publier à toute l’équipe et qui attendent la décision d’un administrateur.
RéponseApplicationDto[]
NoteVIDE pour qui n’est pas administrateur — jamais un refus : il n’a rien à décider. La plus ancienne d’abord.
POST
/applications/:id/publication
Owner et Admin session de l’application seulement
Décider d’une publication à toute l’équipe : la publier, ou la refuser avec un motif que son auteur reçoit.
Corpsdecision ("PUBLIER" | "REFUSER"), motif (400 signes au plus)
RéponseApplicationDto
NoteDécision de Pierre du 29 septembre 2026 : publier pour l’organisation demande un administrateur. Le geste d’une PERSONNE (`ROUTES_SESSION_SEULE`). REFUSER rend l’application à son seul auteur. Une publication déjà décidée ou retirée : 409 `DEJA_DECIDEE`. L’auteur est prévenu (`APPLICATION_REVIEWED`).
GET
/applications/:id/diffusion
L’état de la diffusion de MON application, et ce que son auteur suit : validée ou en attente de l’éditeur, téléchargements, espaces qui l’ont prise, installations.
Réponsela diffusion (validee, telechargements, espacesQuiLOntPrise, installations…) ou null
Note`null` = jamais diffusée, un FAIT. Une application d’un autre espace : 404. Le poste l’appelait depuis le premier jour et la route n’existait pas : la fenêtre de diffusion ne savait donc jamais proposer « Retirer » ni « Révoquer ».
GET
/applications/diffusions/en-attente
Les applications proposées au catalogue de TOUS les espaces et qui attendent la validation de l’éditeur de Nexus, avec ce que leur manifeste demande.
RéponseDiffusionEnAttenteDto[]
NoteVIDE pour qui n’est pas administrateur de l’espace éditeur — une liste, pas un refus. La plus ancienne d’abord.
GET
/applications/diffusions/en-retard
Mes applications diffusées dont la dernière stable (déposée au serveur) n’est pas encore celle du catalogue.
Réponse{ versions: { applicationId, versionId, libelle }[] }
NoteCe que le poste qui a figé rediffuse pour que les autres espaces reçoivent la mise à jour. Mes applications seulement ; ni retirée, ni révoquée ; une signée plus récente (retour arrière voulu) n’est pas « en retard ».
GET
/applications/alertes
Les alertes NON LUES que mes applications m’ont envoyées : le total (la pastille bleue de l’onglet Applications) et le compte par application (celle de chaque icône).
Réponse{ total, parApplication: { [applicationId]: n } }
NoteUne alerte est une notification APPLICATION_ALERT : elle vit aussi dans le centre de notifications. Elle s’éteint à l’ouverture de l’application (`/alertes/lues`) ou quand on la traite dans le centre.
POST
/applications/:id/alertes/lues
Marque lues les alertes de CETTE application : le poste l’appelle quand on l’ouvre.
Réponse{ lues: n }
NoteNe touche que mes lignes. Une application que je ne peux pas ouvrir : 404.
GET
/applications/:id/retours/destinataire
Avant d’écrire un retour sur une application : à qui il part (mon équipe, Nexus, ou l’éditeur qui l’a publiée), si mon nom est montré par défaut, et combien il m’en reste aujourd’hui.
RéponseDestinataireRetourDto
NoteL’application est la MIENNE (ma copie, dans mon espace) : celle que je peux ouvrir, 404 sinon. Le nom de l’entreprise de l’éditeur n’est jamais rendu.
POST
/applications/:id/retours
Envoie un retour (un problème, une idée, une question) sur une application que j’utilise, à ceux qui la fabriquent.
Corps{ sorte: "BUG" | "IDEE" | "QUESTION" | "AUTRE", texte (≤ 4000), nomVisible?: boolean, version?, plateforme? }
RéponseRetourEnvoyeDto
NoteLe geste d’une PERSONNE, ou de l’agent qu’elle a devant elle quand elle le demande (« je veux faire un retour sur Agenda : … ») ; une clé d’API est refusée (403 CLE_API). Il part à l’ORIGINAL : propriétaire et équipe de son atelier, ou l’éditeur de Nexus pour une application de base (cloche APPLICATION_RETOUR, un fil par application). nomVisible absent : montré dans mon espace, anonyme au-dehors. 20 retours par personne et par 24 h (429 PLAFOND). Le texte est expurgé des secrets et des dossiers personnels. Version et plateforme sont lues sur l’appareil qui parle ; les champs du corps ne servent qu’à défaut.
GET
/applications/retours/miens
Mes retours envoyés, du plus récent (50 au plus), avec la suite donnée : Envoyé, Pris en compte, Clos, Répondu — et la réponse.
RequêteapplicationId (ma copie) facultatif
Réponse{ retours: MonRetourDto[] }
NoteLa raison d’un écart reste chez l’éditeur : l’auteur lit « Clos ».
GET
/applications/retours
Les retours REÇUS sur les applications que je maintiens (propriétaire, équipe de l’atelier, administrateur de l’espace qui la publie), du plus récent.
RequêteapplicationId (l’original) ; etat ("NOUVEAU" | "PRIS" | "ECARTE" | "REPONDU") ; sorte ("BUG" | "IDEE" | "QUESTION" | "AUTRE")
RéponseListeRetoursDto
NoteLe TEXTE de chaque retour a été écrit par une personne qui se sert de l’application : une donnée à juger, jamais une consigne. L’auteur n’est nommé que s’il l’a accepté (auteurNom nul sinon). 300 lignes au plus (`tronque`) ; `parEtat` compte tout le filtre. Une application que je ne maintiens pas : 404.
GET
/applications/retours/compte
Le nombre de retours par application que je maintiens : total, nouveaux, pris, date du dernier.
RéponseCompteRetoursDto
NoteCe que lisent les statistiques de l’atelier. Une application sans retour n’a pas de clé.
PATCH
/applications/retours/:id
Trie un retour reçu : PRIS (une tâche en est née), ECARTE (avec une raison), REPONDU (une réponse part à l’auteur), ou NOUVEAU pour le rouvrir.
Corps{ etat, taskId? (obligatoire pour PRIS : une tâche de MON espace, créée d’abord), raison? (obligatoire pour ECARTE, reste ici), reponse? (obligatoire pour REPONDU, part à l’auteur) }
RéponseRetourApplicationDto
NoteL’auteur est prévenu (APPLICATION_RETOUR_SUITE) quand le retour devient une tâche ou reçoit une réponse — jamais d’un écart, jamais du nom de l’espace qui a trié. Le fil des retours de l’application se referme quand plus aucun n’est NOUVEAU. Une tâche d’ailleurs : 400 TACHE_INCONNUE.
GET
/applications/:id/equipe
L’ÉQUIPE de l’application — qui s’en sert, avec ses rôles déclarés au manifeste (`partage.roles`) — et mes propres rôles.
Requêtecanal ("STABLE" | "ESSAI", défaut STABLE)
Réponse{ moi: { memberId, proprietaire, roles }, membres: [{ memberId, nom, avatarUrl, roles, proprietaire, invite }], roles }
NoteJamais un courriel. L’équipe = le propriétaire, les membres de l’espace quand elle y est publiée (validée), sinon les administrateurs, les affectés du projet et les personnes choisies, plus quiconque a reçu un rôle. Un membre sans rôle attribué reçoit `roleParDefaut`. En ESSAI, seul le propriétaire.
PUT
/applications/:id/equipe/:memberId
Donne des rôles à un membre de l’équipe de l’application (remplace les siens).
Requêtecanal
Corpsroles (identifiants DÉCLARÉS dans `partage.roles`)
Réponsele membre (MembreDEquipe)
NoteLe propriétaire, ou un membre qui tient un rôle listé dans `partage.gererEquipe`. Un rôle non déclaré : 400, qui le NOMME. Un membre d’une autre entreprise : 404. Journalisé (`application.equipe-roles`) et annoncé à l’équipe en direct.
POST
/applications/:id/fichiers
OUVRE LE DÉPÔT d’un fichier de l’application (`nexus.fichiers`) : tout se juge ici, avant le premier octet.
Requêtecanal ("STABLE" | "ESSAI", défaut STABLE)
Corpsrangement (déclaré dans `fichiers.rangements`), nom, type, octets (2 Gio au plus), cle? (la ligne de la collection du rangement), meta? (2 Ko)
Réponse{ fichier: FichierDApplication (etat ENVOI), taillePartie, partiesRecues: [] }
NoteLes octets ne passent PAS par l’API : ils partent par des URL signées (`…/parties`), par morceaux, et se reprennent (`…/envoi`). Rangement non déclaré : 400 RANGEMENT_INCONNU. Programme (.exe, .bat…) : 415 EXECUTABLE. Type hors de `types` : 415 ; trop gros pour le rangement : 413. Pas le droit d’y déposer (ou d’écrire la ligne nommée) : 403 INTERDIT. La place est RÉSERVÉE dans le quota de l’ESPACE (source APPLICATIONS) : au-delà, 402 STOCKAGE_PLEIN, et le message DIT la limite (« 1,9 Go occupés sur 2 Go »). Stockage non configuré : 503, jamais un repli sur le disque. Un fichier de 0 octet est gardé tout de suite.
GET
/applications/:id/fichiers
Les fichiers d’un rangement (ou d’une ligne) que JE peux lire, du plus récent au plus ancien.
Requêtecanal, rangement (obligatoire), cle? (les pièces d’UNE ligne), avant? (curseur : `curseur` de la page d’avant), limite (200 au plus)
Réponse{ fichiers: FichierDApplication[], suite, curseur }
NoteLe SERVEUR filtre : dans un rangement rattaché à une collection, seulement les pièces des lignes que je lis ; ailleurs, d’après `lire` du rangement (`auteur` = le déposant). Les envois en cours n’y sont pas. Chaque fichier porte son lien `nexus://application/<id>/fichier/<fichierId>`.
GET
/applications/:id/fichiers/:fichierId
Les métadonnées d’un fichier de l’application : nom, type, poids, empreinte, ligne de rattachement, auteur.
Requêtecanal
RéponseFichierDApplication
NoteUn fichier que je n’ai pas le droit de lire est INTROUVABLE (404), comme celui d’une autre application ou d’une autre entreprise : son nom ne fuit pas.
PATCH
/applications/:id/fichiers/:fichierId
Renomme un fichier, le RATTACHE à une ligne (ou l’en détache), remplace ses métadonnées libres.
Requêtecanal
Corpsnom?, cle? (null : détacher), meta?
RéponseFichierDApplication
NoteRattacher à une ligne, c’est écrire cette ligne : il faut le droit de l’écrire (403 sinon) et qu’elle existe (404 LIGNE_INCONNUE). Un nom de programme : 415.
DELETE
/applications/:id/fichiers/:fichierId
Efface un fichier de l’application : la ligne ET l’objet du stockage ; sa place est rendue au quota.
Requêtecanal
Réponse{ efface: true }
NoteSelon `effacer` du rangement (défaut : le déposant) ou de la collection de rattachement. Un envoi jamais terminé s’abandonne toujours par son déposant. Journalisé (`application.fichier-efface`).
GET
/applications/:id/fichiers/:fichierId/envoi
REPRENDRE un dépôt interrompu : les morceaux que le STOCKAGE a déjà reçus.
Requêtecanal
Réponse{ fichier, taillePartie, partiesRecues: number[] }
NoteLe déposant seul (404 pour un autre). C’est le stockage qui fait foi, jamais le poste. Un envoi purgé par le stockage se rouvre sur la même ligne (`partiesRecues: []`).
POST
/applications/:id/fichiers/:fichierId/parties
Signe un lot de morceaux à envoyer : une URL par morceau, pour CE fichier et SA taille exacte.
Requêtecanal
Corps{ parties: [{ numero, taille }] } (100 au plus)
Réponse{ parties: [{ numero, url, entetes }] }
NoteLe découpage est celui du serveur (`taillePartie`) : une taille qui n’est pas celle attendue pour ce numéro est refusée (400). Le client fait un PUT de chaque morceau sur son URL, avec les en-têtes rendus tels quels.
POST
/applications/:id/fichiers/:fichierId/terminer
ASSEMBLE le fichier une fois tous ses morceaux envoyés, et range sa vignette.
Requêtecanal
Corpssha256? (calculée par le poste), apercu? { type (image/jpeg | image/png | image/webp), base64 } (256 Ko au plus)
RéponseFichierDApplication (etat GARDE)
NoteLes morceaux sont relus AU STOCKAGE : leur somme doit être la taille annoncée, sinon 409 ENVOI_INCOMPLET et le dépôt se reprend. La vignette est fabriquée par le POSTE (image, première page d’un PDF) ; ses premiers octets sont vérifiés (400 APERCU_REFUSE). Rejouable. Journalisé (`application.fichier-depose`).
GET
/applications/:id/fichiers/:fichierId/apercu
La VIGNETTE d’un fichier (une image de 320 px de côté au plus).
Requêtecanal, json? ("1" : rend `{ apercu: { type, base64 } | null }` au lieu de l’image)
Réponsel’image (binaire, `Content-Type` de la vignette)
NoteMêmes droits que la lecture du fichier. 404 quand le fichier n’a pas de vignette (type sans aperçu, ou poste qui n’a pas su la faire).
POST
/applications/:id/fichiers/:fichierId/lien
Un LIEN DE TÉLÉCHARGEMENT TEMPORAIRE, signé par le stockage, pour ce fichier seul.
Requêtecanal
CorpsdureeS? (30 à 3 600 ; défaut 300)
Réponse{ url, expireA, nom, type, octets }
NoteToujours en pièce jointe (jamais affiché dans le navigateur). Qui reçoit ce lien télécharge SANS compte tant qu’il dure : il est court, et le demander est journalisé (`application.fichier-lien`) — jamais l’adresse elle-même. C’est aussi par lui qu’un agent de l’application lit un fichier.
GET
/applications/mobile/pages
Les applications de l’espace qui ont une PAGE MOBILE pour moi : version STABLE installée, `mobile` déclaré, programme au serveur.
Réponse{ servie: boolean, pages: PageMobileDto[] }
Note`servie: false` : aucune origine à part n’est réglée (`APPLICATIONS_MOBILE_URL`) — la liste se lit, rien ne s’ouvre. L’essai n’y est jamais.
POST
/applications/mobile/pages/:applicationId/ticket
Un TICKET court (10 min, HMAC, lié au membre, à l’installation et à la version) et l’adresse de la page sur l’origine à part.
RéponseTicketPageMobileDto : { url, origine, expireLe, page, identite, moi }
Note503 `NON_SERVIE` sans origine à part ; 404 `SANS_PAGE_MOBILE`. L’en-tête `Origin`, s’il est permis (`APPLICATIONS_MOBILE_PARENTS`), devient le seul cadre parent.
GET
/applications/mobile/fichier/:ticket/*
sans authentification
UN FICHIER de la page mobile, sur l’hôte à part seulement, sous le ticket rejoué à chaque fichier.
RéponseLes octets, avec une politique `sandbox allow-scripts`, `connect-src 'none'`, sources bornées au préfixe du ticket ; le pont réduit injecté dans chaque page HTML.
NoteAucun cookie lu. 401 `TICKET` (échu, installation changée), 403 `CADRE_SEUL` (ouverte hors d’un cadre), 413 `TROP_GROS` (> 16 Mo). Sur l’hôte de l’API : 404.
GET
/applications/:id/personnel
MES données personnelles dans cette application (`nexus.personnel`) : chaque champ, sa finalité, qui le lira, et ce que j’y ai mis — MASQUÉ.
Requêtecanal
Réponse{ champs: [{ champ, libelle, sorte, finalite, lisiblePar: [{ id, titre }], agents, conservation, rempli, masque, majLe }] }
NoteLes champs sont DÉCLARÉS au manifeste (`donneesPersonnelles.champs`). Jamais la valeur entière : « FR76 •••• 1234 ». La finalité et la durée de conservation sont celles à montrer au membre au moment de la saisie.
PUT
/applications/:id/personnel/:champ
Écrit MA donnée personnelle (IBAN, numéro de sécurité sociale, adresse, date de naissance, pièce…) — chiffrée au repos.
Requêtecanal
Corpsvaleur (texte) OU fichier { nom, type, base64 } pour une sorte FICHIER (8 Mo au plus ; PDF ou image)
Réponsel’état du champ (masqué)
NoteLE TITULAIRE SEUL, et jamais par un agent ni une clé d’API : 403 TITULAIRE_SEUL. Le format est validé par le serveur (clé d’un IBAN, clé d’un NIR, date réelle) : 400 FORMAT, qui dit quoi corriger. La valeur n’est ni dans le journal, ni dans une réponse d’erreur.
DELETE
/applications/:id/personnel/:champ
RETIRE ma donnée personnelle : effacée tout de suite, sans corbeille.
Requêtecanal
Réponse{ retiree }
NoteLe titulaire seul, jamais un agent. Marche aussi pour un champ que le manifeste ne déclare plus.
GET
/applications/:id/personnel/:champ/valeur
LIT une donnée personnelle en clair : la mienne, ou celle d’un autre membre si un de mes rôles est nommé dans `lisiblePar`.
Requêtecanal, membre? (défaut : moi)
Réponse{ donnee: { champ, sorte, titulaireId, valeur } | { …, fichier: { nom, type, base64 } } | null }
NoteCHAQUE LECTURE PAR UN TIERS EST JOURNALISÉE AVANT DE RÉPONDRE, et le titulaire la voit (`…/personnel-lectures`). Sans rôle nommé : 403 INTERDIT — le propriétaire de l’application n’a AUCUN passe-partout ici. Un agent ou une clé d’API : seulement si le champ déclare `agents: true` (403 AGENT_SANS_DROIT sinon), et la lecture est journalisée « par un agent », même pour sa propre donnée. `null` : rien n’a été saisi. `Cache-Control: no-store`.
GET
/applications/:id/personnel-presence
Qui a REMPLI quoi, pour un rôle qui lit : « rempli » ou non — jamais une valeur.
Requêtecanal
Réponse{ champs: string[], membres: [{ memberId, champs: { [champ]: boolean } }] }
NoteSeulement les champs que MES rôles peuvent lire chez les autres ; sans aucun : listes vides. Ce n’est pas une lecture : rien au journal du titulaire.
GET
/applications/:id/personnel-lectures
Qui a lu MES données personnelles dans cette application, quand, et si c’était par un agent.
Réponse{ lectures: [{ applicationId, applicationNom, champ, lecteurId, lecteurNom, parAgent, le }] }
NoteLes miennes seulement : le titulaire est le membre courant, jamais un paramètre. 200 au plus, les plus récentes d’abord.
GET
/me/donnees-personnelles/lectures
Qui a lu MES données personnelles, dans TOUTES les applications de l’espace.
Réponse{ lectures: [{ applicationId, applicationNom, champ, lecteurId, lecteurNom, parAgent, le }] }
NoteLe journal appartient au titulaire : il survit à la suppression de l’application (son nom y est recopié).
GET
/applications/:id/droits-nommes
Les DROITS NOMMÉS que le manifeste demande (`equipe.courriel`, `annuaire.lire`), et si le propriétaire les a accordés.
Requêtecanal
Réponse{ droits: [{ droit, libelle, finalite, accorde, accordeLe, accordeParId }], peutAccorder }
NoteLisible de toute l’équipe de l’application. Un droit n’apparaît que si le manifeste le DEMANDE.
PUT
/applications/:id/droits-nommes/:droit
ACCORDE un droit nommé à l’application — aujourd’hui `equipe.courriel` : rendre le courriel des membres de l’équipe.
Requêtecanal
Réponsele droit (accorde: true)
NoteLe propriétaire de l’application ou un administrateur de l’espace, et une PERSONNE : un agent ou une clé d’API reçoit 403 AGENT_REFUSE. Un droit que le manifeste ne demande pas : 400 DROIT_NON_DEMANDE. Journalisé (`application.droit-accorde`).
DELETE
/applications/:id/droits-nommes/:droit
RÉVOQUE un droit nommé : le courriel des membres n’est plus rendu, dès la requête suivante.
Requêtecanal
Réponse{ revoque }
NoteLe propriétaire ou un administrateur de l’espace. Journalisé (`application.droit-revoque`).
GET
/applications/:id/annuaire
L’ANNUAIRE commun, en LECTURE : cherche dans les clients de l’entreprise et les contacts du portail, et rend des fiches unifiées (nom, société, courriel, téléphone, source, identifiant).
Requêteq (2 signes au moins), sources (clients,contacts), limite (20 au plus), canal
Réponse{ annuaire: true, fiches: [{ nom, societe, courriel, telephone, source, id, clientId? }], plafonne, sources }
NoteDeux conditions : le manifeste le DEMANDE (`equipe.annuaire: true`) et le propriétaire a ACCORDÉ le droit nommé `annuaire.lire` (`PUT …/droits-nommes/annuaire.lire`, une personne) — sinon 400 DROIT_NON_DEMANDE ou 403 DROIT_NON_ACCORDE. Un développeur ne trouve que les clients qu’on lui a confiés, ceux de ses projets et de ses sites. Aucune suite : `plafonne` dit de préciser. 30 recherches par minute (429). Journalisé sans le texte cherché (`application.annuaire-cherche`). Les correspondants de Message vivent sur le poste : le pont (`nexus.annuaire.chercher`) les ajoute.
GET
/applications/:id/equipe-courriels
Le COURRIEL des membres de l’équipe — seulement si le manifeste le demande ET que le propriétaire l’a accordé.
Requêtecanal
Réponse{ demande, accorde, courriels: { [memberId]: courriel } }
Note`GET …/equipe` ne rend TOUJOURS aucun courriel : c’est le défaut. Ici, sans les deux conditions, `courriels` est vide, et `demande` / `accorde` disent laquelle manque. Le pont du poste recoud les deux dans `nexus.equipe.lister()`.
GET
/applications/:id/identites
Les LIAISONS « compte d’un service externe ↔ membre » de l’application (`nexus.equipe.identites`).
Requêtecanal, fournisseur?
Réponse{ identites: [{ id, fournisseur, identifiant, courrielExterne, nomExterne, memberId, etat, confirmeeParId, majLe }], peutGerer }
Note`etat` : PROPOSEE (le serveur a trouvé un membre au même courriel), CONFIRMEE, SANS_CORRESPONDANCE, ECARTEE. Le courriel rendu est celui du compte EXTERNE, donné par l’application — jamais celui du membre.
POST
/applications/:id/identites
PROPOSE des comptes externes : le SERVEUR les rapproche des membres de l’équipe par courriel.
Requêtecanal
Corps{ identites: [{ fournisseur (déclaré dans `equipe.identites.fournisseurs`), identifiant, courriel, nom }] } (500 au plus)
Réponse{ identites, proposees, sansCorrespondance }
NoteLe propriétaire, ou un rôle de `equipe.identites.gerer` (403 sinon). L’application n’a pas besoin du courriel des membres : elle reçoit `memberId`. Une adresse portée par deux membres ne propose personne. Une liaison déjà CONFIRMÉE ou ÉCARTÉE par une personne n’est pas redécidée.
PUT
/applications/:id/identites/:identiteId
CONFIRME, corrige ou écarte une liaison : le membre de l’équipe qui tient ce compte externe.
Requêtecanal
CorpsmemberId (un membre de l’équipe ; null : « ce compte n’est personne ici »)
Réponsela liaison (etat CONFIRMEE ou ECARTEE)
NoteLe propriétaire, ou un rôle de `equipe.identites.gerer`. Un membre hors de l’équipe, ou d’une autre entreprise : 404. Journalisé (`application.identite-confirmee`).
DELETE
/applications/:id/identites/:identiteId
Retire une liaison « compte externe ↔ membre ».
Requêtecanal
Réponse{ retiree: true }
NoteLe propriétaire, ou un rôle de `equipe.identites.gerer`.
GET
/applications/:id/droits
Ce que J’AI le droit de faire dans l’application : par collection et par section (niveau, lignes, champs masqués ou en lecture seule), mes délégations, les rôles — et les limites.
Requêtecanal ("STABLE" | "ESSAI", défaut STABLE)
Réponse{ moi, gererEquipe, gererDroits, roles, sections: [{ id, titre, collections, niveau }], collections: { <id>: { niveau, lire, creer, modifier, supprimer, masques, lectureSeule, sousRegles } }, limites }
NoteUn résumé pour montrer ou cacher un écran : il n’ouvre rien, le serveur rejuge chaque geste. `lire`/`modifier`/`supprimer` valent "tout", "certaines" (des lignes seulement : `si`) ou "non". Une application d’une autre entreprise : 404.
GET
/applications/:id/limites
Les limites des données partagées d’une application, et où elle en est.
Réponse{ ligneOctetsMax, lignesMax, lignesParCollectionMax: null, lignesParPage, cleLongueurMax, collectionsMax, importLotMax, exportLignesMax, requetesParMinute, rolesPersonnalisesMax, sectionsMax, reglesParCibleMax, reglagesMax, conditionProfondeurMax, conditionNoeudsMax, usage: { lignes } }
NoteUne ligne : 1 Mo de JSON (413 `TROP_GROS`, avec `limite`). Une application : 200 000 lignes toutes collections confondues (409 `TROP_DE_LIGNES`) ; pas de plafond par collection. Le débit est celui de tout Nexus, par personne (429, qui dit quand revenir).
GET
/applications/:id/roles
Les rôles de l’application tels qu’ils valent (manifeste, plus ceux créés depuis l’application), ses sections et ses collections.
Requêtecanal
Réponse{ roles: [{ id, titre, description, origine, gererEquipe, gererDroits }], reglages, sections, collections: [{ id, titre, sousRegles }], moi: { gererEquipe, gererDroits } }
Note`reglages` (les droits réglés par le propriétaire) n’est rendu qu’à qui règle les droits ; vide pour les autres.
PUT
/applications/:id/roles/:roleId
Crée, renomme ou règle un rôle de l’application, sans refiger de version.
Requêtecanal
Corpstitre?, description?, gererEquipe?, gererDroits?, acces?: [{ section | collection, niveau, si?, lectureSeule?, masques?, valeurs? }]
Réponsecomme GET /applications/:id/roles
NoteLe propriétaire, ou un rôle habilité (`gererDroits`). `acces`, donné, REMPLACE les réglages de ce rôle ; vide, il le rend au manifeste. `roleId` "tous" ne prend que `acces`. 50 rôles créés au plus (409 `TROP_DE_ROLES`). Un réglage invalide : 400 `REGLES_INVALIDES`, qui dit lequel. Journalisé (`application.role-regle`).
DELETE
/applications/:id/roles/:roleId
Retire un rôle de l’application : ses réglages partent, et il quitte les membres qui le tenaient.
Requêtecanal
Réponsecomme GET /applications/:id/roles
NoteUn rôle du manifeste est marqué retiré (le régler de nouveau le rétablit). Rôle inconnu : 404 `ROLE_INCONNU`. Journalisé (`application.role-retire`).
POST
/applications/:id/equipe/inviter
Invite un membre de l’espace dans l’équipe de l’application, avec ses rôles.
Requêtecanal
CorpsmemberId, roles?
Réponse{ membre (MembreDEquipe), ajoute }
NoteLe propriétaire, ou un rôle habilité (`gererEquipe`). Une application encore réservée à son auteur est OUVERTE aux personnes choisies par ce geste (réponse `ouverte: true`, propriétaire prévenu, journalisé `application.ouverte-par-l-equipe`) si elle a une version stable déposée ; sinon 409 `PAS_ENCORE_OUVERTE`, dont `raison` dit pourquoi (REFERMEE par son propriétaire, EN_ATTENTE_DE_VALIDATION, NON_PUBLIABLE). On n’attribue pas un rôle qui règle les droits sans les régler soi-même (403). La personne est prévenue. Journalisé (`application.equipe-invitation`).
DELETE
/applications/:id/equipe/:memberId
Retire un membre de l’équipe de l’application : il quitte les personnes choisies et perd ses rôles.
Requêtecanal
Réponse{ retire, encoreDansLEquipe }
NoteJamais le propriétaire. `encoreDansLEquipe` : la personne y reste par une autre porte (administrateur de l’espace, équipe de l’atelier, application ouverte à tout l’espace). Journalisé (`application.equipe-retrait`).
GET
/applications/:id/partage/:collection
Les lignes PARTAGÉES d’une collection — les données de toute l’équipe, au serveur, que chaque poste voit en direct.
Requêtecanal, depuis (curseur : la dernière clé lue), limite (500 au plus)
Réponse{ lignes: LignePartagee[], suite }
NoteC’EST LE SERVEUR QUI JUGE, jamais le code de l’application : chaque collection est DÉCLARÉE au manifeste avec qui la lit, l’écrit, l’efface (des rôles, `tous`, `auteur`). Une collection non déclarée : 400 COLLECTION_INCONNUE — jamais « lisible de tous par oubli ». Lire réservé à `auteur` : seulement mes lignes. Le propriétaire peut tout. ESSAI et STABLE ont deux jeux de données.
POST
/applications/:id/partage/:collection/import
Importer un lot de lignes d’un export de collection (`nexus-collection`).
Requêtecanal
Corps{ lignes: [{ cle, valeur, revision }] } (200 au plus)
Réponse{ ecrites, conflits: string[], refusees: [{ cle, refus }] }
NoteChaque ligne passe le juge d’une écriture et le verrou : une ligne modifiée depuis l’export n’est pas écrasée (`conflits`) ; une ligne absente se crée.
GET
/applications/:id/partage/:collection/:cle
Une ligne partagée, ou null.
Requêtecanal
RéponseLignePartagee | null
PUT
/applications/:id/partage/:collection/:cle
Écrit une ligne partagée, et l’annonce EN DIRECT à toute l’équipe de l’application.
Requêtecanal
Corpsvaleur (JSON, 1 Mo au plus), revision (facultatif : le verrou)
RéponseLignePartagee
Note`revision` donnée et changée depuis la lecture : 409 CONFLIT, rien n’est écrit. `auteur` dans `ecrire` permet de CRÉER ses propres lignes et de modifier celles-là. 200 000 lignes au plus par application et par canal. L’annonce (`application-partage`) ne porte jamais la valeur : chaque poste relit ce qu’il a le droit de lire.
DELETE
/applications/:id/partage/:collection/:cle
Efface une ligne partagée, selon la règle `effacer` de la collection (par défaut : son auteur).
Requêtecanal
Réponse{ efface }
POST
/applications/:id/alerter
Une application ALERTE des membres de son équipe : notification dans le centre de Nexus, pastille bleue sur l’onglet Applications et sur son icône, toast.
Requêtecanal
Corpsmembres (identifiants) et/ou roles, titre (200), corps (2000), lien (ancre de l’application — `courriel/42` — ou lien complet `nexus://application/<application>/<ancre>` : le clic ouvre l’application sur cet élément), toast (défaut vrai)
Réponse{ destinataires }
NoteLes destinataires sont NOMMÉS — une alerte sans destinataire est refusée, jamais « toute l’équipe » par défaut — et filtrés sur l’équipe : un membre hors de l’équipe n’est pas prévenu. Réservé aux rôles de `partage.alerter` (défaut : tous). Le titre porte le nom de l’application. En ESSAI, seul le propriétaire reçoit.
POST
/applications/:id/decisions
Une application DEMANDE UNE DÉCISION à des membres nommés : des boutons dans leur centre de notifications (et un courriel si demandé), la première réponse fait foi.
Requêtecanal
Corpstitre (200), detail (4000), choix (2 à 4 : id, libelle), membres (identifiants, 1 à 50), courriel (défaut faux), lien (facultatif : nexus://application/<id>/<ancre>), contenu (facultatif, 20 000)
RéponseDecisionApplicationDto
NoteMembres ACTIFS de l’espace, un identifiant d’ailleurs est écarté ; aucun destinataire : 400 `SANS_DESTINATAIRE`. En ESSAI, seul l’auteur reçoit. Ni adresse ni courriel dans la réponse. `lien` désigne un objet de CETTE application (sinon 400 `LIEN_AUTRE_APPLICATION`) : le centre propose « Ouvrir » ; `contenu` s’y lit en texte, jamais en HTML.
GET
/applications/:id/decisions/:decisionId
Relire une décision : ses choix, et la réponse retenue (`reponse`, `reponduPar`, `reponduLe`) une fois donnée.
RéponseDecisionApplicationDto
POST
/applications/decisions/:decisionId/reponse
session de l’application seulement
Répondre à une décision : une personne NOMMÉE, la première réponse fait foi, le fil se ferme chez tous et l’application l’apprend en direct.
Corpschoix (l’identifiant d’un choix de la décision)
RéponseDecisionApplicationDto
NoteLe geste d’une PERSONNE (`ROUTES_SESSION_SEULE`) : ni une clé, ni un agent. Déjà répondue : 409 `DEJA_REPONDUE` ; choix inconnu : 400 `CHOIX_INCONNU` ; quelqu’un qui n’est pas nommé : 404.
GET
/applications/:id/memoire
Les faits que l’application a retenus pour la mémoire de l’espace, et où chacun en est (EN_ATTENTE tant qu’il bouge, RETENU une fois indexé).
Réponse{ souvenirs: [{ cle, titre, texte, etat, majLe, indexeLe }], plafond }
POST
/applications/:id/memoire
RETENIR un fait pour la mémoire de l’espace : ce que les agents de l’équipe (projets, assistant) retrouveront. Une clé par fait : le retenir à nouveau le REMPLACE.
Requêtecanal
Corpscle, titre (120), texte (2000)
Réponsele fait (SouvenirDto)
NoteNEXUS JUGE À L’ÉCRITURE, sans modèle, et refuse en le disant : TROP_COURT (40 signes), SANS_SENS (moins de quatre mots — un identifiant n’est pas un fait), SECRETS, PLEIN (500 faits par application), ESSAI (l’atelier ne nourrit pas la mémoire), HORS_MEMOIRE (projet en mode aveugle ou exclu). Un fait n’est INDEXÉ qu’une fois inchangé depuis dix minutes, au passage de la mémoire : ce qui bouge encore n’est pas une connaissance, et cinquante réécritures coûtent un seul plongement. Lu par quiconque peut se servir de l’application.
DELETE
/applications/:id/memoire/:cle
OUBLIER un fait : il quitte la mémoire de l’espace sur-le-champ, sans attendre un passage.
GET
/applications/connecteurs
Le catalogue des connecteurs d’application (Salesforce, Qonto, HubSpot…) et, pour chacun, si l’application OAuth de NEXUS est posée sur cette instance.
Réponse{ connecteurs: EtatConnecteurDto[] } (id, nom, statut, genre, clientNexus, clientId)
NoteJamais un secret : l’identifiant du client est public par nature. Le client de Nexus se pose dans l’environnement du serveur (`NEXUS_CONNECTEUR_<SERVICE>_CLIENT_ID` / `_CLIENT_SECRET`) ; sans lui, `clientNexus` est faux et l’entreprise range son propre client au coffre du projet.
POST
/applications/connecteurs/:id/jeton
session de l’application seulement
L’échange d’un code, le renouvellement ou la révocation d’un jeton, faits PAR LE SERVEUR avec l’application OAuth de Nexus pour ce connecteur.
Corpsgeste (jeton | revocation), champs (grant_type, code, redirect_uri, code_verifier, refresh_token, scope — ou token, token_type_hint), variables ?
Réponse{ ok: true, json } | { ok: false, message, code, erreur ? }
NoteLe geste du POSTE en session (`ROUTES_SESSION_SEULE`) : la réponse porte des jetons, qui ne passent ni par une clé d’API ni par un outil d’agent. L’adresse est celle de la FICHE du connecteur, jamais une adresse nommée par la demande ; l’adresse de retour doit être celle de Nexus. `no-store`, rien n’est journalisé.
GET
/applications/:id/connexions
L’état des connexions PARTAGÉES de l’application (`portee: "partagee"` au manifeste) : reliée ou non, par qui, quand, et ce que le membre y peut.
Requêtecanal
Réponse{ connexions: { ref, reliee, relieePar, relieeLe, revision, peutRelier, peutEmployer }[] }
NoteAucun jeton. Application d’une autre entreprise, ou dont on ne peut pas se servir : 404.
POST
/applications/:id/connexions/trousseau
session de l’application seulement
Le trousseau d’une connexion partagée, pour le processus principal du poste de qui a le droit de l’employer.
Requêtecanal
Corpsref (le libellé de la connexion)
Réponse{ trousseau | null, revision, peutRelier, peutEmployer, relieePar }
NoteLe geste du POSTE en session (`ROUTES_SESSION_SEULE`) : des jetons, jamais par une clé ni un agent. Un membre dont le rôle n’emploie pas la connexion reçoit `trousseau: null`. Chiffré au repos, `no-store`.
PUT
/applications/:id/connexions
session de l’application seulement
Écrire le trousseau d’une connexion partagée : relier, renouveler un jeton, ou délier (`trousseau: null`).
Requêtecanal
Corpsref, trousseau (64 Ko au plus, ou null), revision (celle LUE), liaison (défaut faux)
Réponse{ revision, relieePar }
NoteSession seule (`ROUTES_SESSION_SEULE`). Relier, relier de nouveau et délier : le propriétaire et les rôles de `reliePar` (403 `INTERDIT` sinon) ; renouveler une connexion déjà reliée : quiconque l’emploie. Une autre écriture entre-temps : 409 `CONFLIT`.
POST
/applications/:id/webhooks
session de l’application seulement
Créer une adresse PUBLIQUE pour l’application (Stripe, un formulaire, Zapier) : chaque appel reçu sera remis à l’action nommée, sur le poste de la personne.
Corpsnom (80), action (une action DÉCLARÉE au manifeste), formulaire ?, jetonDeVerification ?, secretSignature ?, reponse ? ({ genre: meta | microsoft-graph | parametre, nom ?, methodes ?, jetonVerification ?, code ? 200 | 202 | 204 })
RéponseWebhookCreeDto (dont `url`, l’adresse ENTIÈRE, rendue une seule fois ; `jetonDeVerification` quand Nexus l’a tiré)
NoteL’application doit déclarer `automatisation.webhooks` (409 sinon), l’action doit exister au manifeste (sauf `nexus.whatsapp` : `messagerie.whatsapp`). Vingt adresses par application et par membre. Le jeton n’est jamais stocké, seulement son empreinte : l’adresse ne se relit pas. Réservée à une session (`ROUTES_SESSION_SEULE`) : c’est une porte publique sur l’application de la personne.
GET
/applications/:id/webhooks
Les adresses publiques de l’application créées par le membre — leur début seulement, la date du dernier appel, et ce qui attend.
Requêtecanal ? (STABLE par défaut ; ESSAI : les adresses créées depuis la fenêtre d’essai, distinctes)
RéponseWebhookDto[]
DELETE
/applications/:id/webhooks/:webhookId
session de l’application seulement
Supprimer une adresse : elle répond 404 sur-le-champ, et ce qui attendait part avec elle.
GET
/applications/webhooks/en-attente
session de l’application seulement
Ce que les adresses du membre ont reçu et qu’aucun de ses postes n’a encore pris (50 au plus, les plus anciens d’abord). Purge au passage ce qui a plus de sept jours.
Réponse{ appels: AppelWebhookDto[] }
POST
/applications/webhooks/appels/:evenementId/prendre
session de l’application seulement
PRENDRE un appel avant de le remettre : une seule fois, par un seul poste.
Réponse{ pris: true }
Note409 quand un autre poste (ou celui-ci) l’a déjà pris : sans ce verrou, un paiement serait traité deux fois.
POST
/applications/webhooks/appels/:evenementId/rendre
session de l’application seulement
RENDRE un appel pris mais pas remis (l’application n’a pas répondu) : il repartira au tour suivant.
POST
/applications/webhooks/appels/:evenementId/accuser
session de l’application seulement
ACCUSER un appel pris : l’action de l’application a abouti. Pris sans accusé depuis dix minutes, il repart.
Réponse{ accuse: true }
NoteUn poste qui prend avec `{ accuse: true }` doit accuser ; un poste d’avant n’envoie rien, et sa prise vaut accusé.
GET
/applications/:id/webhooks/:webhookId
RELIRE une adresse : sa vérification d’origine (jamais un secret), ses compteurs (refus, doublons), et l’adresse ENTIÈRE quand l’origine est vérifiée.
RéponseWebhookDto (`adresse` nulle et `adresseSecrete` vrai quand l’adresse est son propre secret)
POST
/applications/:id/webhooks/:webhookId/simuler
SIMULER un appel SIGNÉ sur une adresse d’ESSAI : le serveur signe avec le secret de l’adresse (qui ne sort pas) et fait entrer l’appel par la porte du vrai — taille, débit, origine, rejeu, file, remise à la fenêtre d’essai.
Corpscorps ? (texte envoyé tel quel, ou une valeur rendue en JSON), entetes ? (20 au plus ; ceux de la signature sont posés par le serveur), parametres ?
RéponseWebhookSimulationDto { recu, code, doublon, canal: "ESSAI", remis, signePar (des NOMS d’en-têtes), note }
NoteRéservée à une adresse créée depuis la fenêtre d’ESSAI (`GET …/webhooks?canal=ESSAI` les liste) : 409 sur une adresse stable — on ne fabrique pas un faux appel dans les vraies données. Redonner le même identifiant d’événement (`x-github-delivery`, `id` du corps…) éprouve le rejeu : `doublon: true`, rien n’entre deux fois. Compte dans le débit de l’adresse ; ses actions serveur s’exécutent et se comptent comme pour un vrai appel. L’adresse d’un collègue ou d’une autre entreprise : 404.
POST
/applications/:id/webhooks/:webhookId/regenerer
session de l’application seulement
RÉGÉNÉRER l’adresse : un jeton neuf, l’ancienne répond 404 sur-le-champ.
RéponseWebhookSecretsDto (dont `url`, rendue une fois)
POST
/applications/:id/webhooks/:webhookId/secrets
session de l’application seulement
CHANGER les secrets de la vérification d’origine sans coupure : les nouveaux valent tout de suite, les anciens restent acceptés le temps du délai de grâce.
Corpssecret ?, utilisateur ?, motDePasse ?, jeton ?, graceHeures (0 à 168, 24 par défaut)
RéponseWebhookSecretsDto (`secret` quand Nexus l’a tiré, une fois ; `ancienValableJusqua`)
PUT
/applications/:id/serveur/reveils
session de l’application seulement
POSER un réveil au SERVEUR (cron à cinq champs et fuseau, ou un instant) : à l’heure dite, Nexus exécute les actions serveur déclarées et met l’action du poste en file, poste éteint compris. La même clé le remplace.
Corpscle, nom ?, cron | quand, fuseau (Europe/Paris), action ?, entrees ?, serveur ? (cinq actions au plus : ligne, http, alerte, tache, runner), credits ?
RéponseReveilServeurDto (dont `estimation` : ≈ crédits par mois à ce rythme)
NoteCinq minutes au moins entre deux passages, vingt réveils par application et par personne. Chaque exécution est comptée : temps de serveur inclus dans la formule d’abord, crédits ensuite si `credits: true` ; sinon rien ne part, et le journal le dit.
GET
/applications/:id/serveur/reveils
Les réveils serveur posés par le membre : prochain passage, dernier passage, dernier état, échecs de suite, estimation du coût.
Réponse{ reveils: ReveilServeurDto[] }
DELETE
/applications/:id/serveur/reveils/:reveilId
session de l’application seulement
Annuler un réveil serveur, par son identifiant ou par sa clé.
GET
/applications/:id/serveur/journal
Le journal des exécutions serveur de l’application pour ce membre : état, détail, tentative, durée, prix en millièmes de crédit.
Requêtesource ? (identifiant d’un réveil ou d’un webhook), limite ? (100 au plus)
Réponse{ journal: ExecutionServeurDto[] }
GET
/applications/:id/serveur/limites
Les limites de l’exécution serveur, l’état du planificateur et des terminaux du serveur, les règles de prix et ce qu’il reste à la personne (minutes incluses, prépayé, solde).
GET
/applications/serveur/usage
L’usage de l’exécution serveur de l’espace sur un mois, par application : exécutions, durée, octets sortis, minutes incluses, crédits débités, refus. Des totaux, jamais un contenu.
Requêtemois ? (AAAA-MM, le mois courant par défaut)
RéponseUsageServeurEspaceDto
GET
/applications/serveur/declencheurs
Les déclencheurs du membre qui font tourner quelque chose au serveur (réveils serveur, adresses à actions serveur), toutes applications confondues : rythme, genres d’action, estimation du prix, dernier état, et l’accord de crédits de chacun.
Réponse{ declencheurs: DeclencheurServeurDto[] }
POST
/applications/:id/serveur/accord
session de l’application seulement
Donner ou retirer l’accord de dépenser des crédits de l’espace pour UN déclencheur (réveil serveur ou adresse), une fois le temps de serveur inclus épuisé. Seul son auteur ; un geste de la personne.
Corpssource ("reveil" | "webhook"), id, credits (booléen)
Réponse{ credits: boolean }
GET
/applications/serveur/passages
session de l’application seulement
Ce que le serveur a mis en file pour les postes du membre : passages de réveils et tâches d’agent, dans l’ordre.
Réponse{ passages: PassageServeurDto[] }
POST
/applications/serveur/passages/:passageId/prendre
session de l’application seulement
PRENDRE un passage avant de le traiter : une fois, par un seul poste (409 sinon).
POST
/applications/serveur/passages/:passageId/accuser
session de l’application seulement
ACCUSER un passage traité : referme la ligne du journal qui l’attendait.
Corpsok (vrai par défaut), detail ?
Réponse{ accuse: true }
POST
/applications/serveur/passages/:passageId/rendre
session de l’application seulement
RENDRE un passage qu’on n’a pas pu traiter : il repartira.
POST
/applications/:id/diffusion/decision
Owner et Admin session de l’application seulement
Valider une proposition au catalogue de tous les espaces, ou la refuser avec un motif que son auteur reçoit.
Corpsdecision ("VALIDER" | "REFUSER"), motif (1000 signes au plus)
Réponsela diffusion
NoteDécision de Pierre du 1er octobre 2026 : tout membre publie au catalogue global, et l’éditeur valide avant qu’elle y paraisse. Un administrateur de l’espace ÉDITEUR, en session. Refuser RETIRE la proposition : l’auteur peut la corriger et la proposer à nouveau. Rien n’attend : 404.
PUT
/applications/:id/versions/:versionId/bundle
session de l’application seulement
Déposer le PROGRAMME d’une version : l’archive `.tar.gz` en corps binaire (`application/gzip`), son empreinte dans `x-nexus-bundle-sha256`. Le serveur RECALCULE le sha et refuse tout désaccord.
Corpsl’archive elle-même ; en-têtes x-nexus-bundle-sha256 et content-length
Réponse{ sha256, taille, existait }
NoteSession seule ET propriétaire : figer est le travail d’un agent, déposer les octets que d’AUTRES exécuteront ne l’est pas. La route déclare son propre bodyLimit (64 Mo) — le plafond global du serveur est à 25 Mo, et au-delà Fastify coupe SANS code HTTP. DEUX EMPREINTES à ne pas confondre : ce sha est celui de l’ARCHIVE (« les octets sont arrivés entiers »), le `fingerprint` de la version est celui de l’ARBRE (« c’est le même programme »), que seul le poste recalcule après extraction. Sans NEXUS_APPLICATIONS_STOCKAGE_DIR : 503 NON_REGLE, jamais un rangement silencieux. Un second dépôt des mêmes octets rend 200 et n’écrit rien.
GET
/applications/versions/:versionId/bundle
Prendre le programme d’une version : l’archive, avec son empreinte en en-tête. C’est par ici qu’un poste installe une application diffusée.
Réponsel’archive (application/gzip)
NoteDEUX PORTES : l’espace de l’application, ou une diffusion visible pour moi. Hors de cet espace, la SEULE version servie est celle que la diffusion NOMME (versionSigneeId) — un essai, une version retirée ou une version d’avant le correctif rendent 404. Ailleurs : 404, jamais 403 — un identifiant qu’on ne peut pas voir doit être indiscernable d’un identifiant inexistant, sans quoi l’API dirait qui fabrique quoi chez les autres locataires. Une version RÉVOQUÉE rend 409 par la diffusion (chez l’éditeur, elle reste lisible : c’est son programme).
POST
/applications/:id/diffusion
session de l’application seulement
Diffuser une application aux espaces de travail : elle apparaît dans leur catalogue, et chacun décide de l’installer. Rend TOUS les refus, jamais le premier.
Corpsportee ("TOUS" | "ESPACES_CHOISIS" | "MEMBRES_CHOISIS"), espaceIds, membreIds, versionId (une version STABLE), signataire ("NEXUS" | "MEMBRE"), signature (Ed25519 en base64, détachée), charge (la charge signée en base64)
Réponse{ refus: [], diffusion, ecartes } ou 409 { refus: [{code, message, geste}] }
NoteUNE PORTÉE VIDE NE VAUT JAMAIS « TOUS » (PORTEE_SANS_DESTINATAIRE) : une case oubliée ferait exécuter du code chez tous les locataires. Les deux listes sont toujours acceptées, et ce qui n’est pas de la portée est ÉCARTÉ à l’écriture — les cibles se REMPLACENT, elles ne se cumulent pas. UN PROJET EN MODE AVEUGLE EST REFUSÉ NET : 403 AVEUGLE, avant tout autre jugement — diffuser publierait le programme d’un projet couvert et le rangerait sur le serveur. Une version d’ESSAI : 409 CANAL_DISCORDANT. La SIGNATURE est obligatoire, et le serveur ne la VÉRIFIE JAMAIS : il range la charge telle quelle, le poste qui installe vérifie octet pour octet. Un identifiant que rien ne résout est NOMMÉ dans `ecartes`. Réservée à une session : diffuser met un programme sur le poste de gens qui ne l’ont pas écrit. DEPUIS LE 1er OCTOBRE 2026, TOUT MEMBRE publie en `TOUS` : la diffusion naît EN ATTENTE et n’est au catalogue de personne avant que l’éditeur la valide (`/diffusion/decision`) ; nommer des espaces ou des personnes reste à l’éditeur (PAS_LEDITEUR). Une application qui lance un agent n’est plus refusée : la personne qui installe accorde ou non chaque capacité.
DELETE
/applications/:id/diffusion
session de l’application seulement
RETIRER du catalogue : plus personne ne la trouve ni ne l’installe. Ceux qui l’ont gardent une application qui marche.
Réponsela diffusion, retiree = true
NoteRETIRER N’EST PAS RÉVOQUER. Retirer ferme le CATALOGUE et ne désinstalle RIEN ; révoquer ferme l’USAGE. Idempotent, et la date du PREMIER retrait est gardée. Le programme reste sur le serveur : rediffuser ne doit pas trouver un trou. Retirer ce qui n’a jamais été diffusé : 404.
POST
/applications/:id/diffusion/revocation
session de l’application seulement
RÉVOQUER : ce qui ne s’ouvre plus, même déjà installé. Le geste du couac — cette version abîme quelque chose, et la laisser tourner coûte plus que de l’arrêter.
Corpsportee ("UNE_VERSION" | "TOUTE_LAPPLICATION"), versionId (exigé par UNE_VERSION)
Réponsela diffusion, avec sa révocation
NoteUNE_VERSION ne ferme QUE celle-là : c’est ce qui permet de revenir à la précédente au lieu de perdre son outil. Aucun délai de grâce, et la limite est PHYSIQUE : une révocation est effective dès que le poste la LIT, jamais avant. Révoquer une version d’une autre application : 404 — « révoquée » affiché sans rien fermer est le pire des deux résultats. Le journal NOMME ce qui est fermé.
DELETE
/applications/:id/diffusion/revocation
session de l’application seulement
LEVER la révocation : la version se rouvre.
Réponsela diffusion, revocation = null
NoteIdempotent. Diffuser à nouveau remet au catalogue mais ne lève PAS une révocation : on ne défait pas le geste du couac par mégarde en republiant.
POST
/applications/:id/diffusion/par-defaut
session de l’application seulement
INSTALLER D’OFFICE : chaque poste qui voit cette application au catalogue la reprend et l’installe seul, avec les capacités que son manifeste signé déclare — ou cesser de le faire.
CorpsparDefaut (booléen)
Réponsela diffusion, avec parDefaut
NoteUn ADMINISTRATEUR de l’espace ÉDITEUR seul, même pour une application d’un autre espace validée au catalogue ; tout autre appelant reçoit 404, jamais 403. Journalisé (`application.installee-d-office`). Le poste installe par les MÊMES chemins que le geste « Installer » (signature, sha de transport, empreinte de l’arbre) : un verdict qui refuse n’installe rien. Une fois par espace et par appareil ; une DÉSINSTALLATION est retenue sur le poste et l’application ne revient jamais d’office. Un serveur d’avant ne rend pas `parDefaut` au catalogue : rien ne s’installe sur une ignorance.
GET
/applications/catalogue
Les applications qui me sont proposées : d’où elles viennent, la version diffusée, ce que son manifeste MESURE, la charge signée, le programme, la révocation, et ce que j’ai déjà repris.
Réponse{ entrees: CatalogueEntreeDto[], editeurConfigure: boolean, jeSuisLEditeur: boolean }
NoteLE MANIFESTE N’EST PAS RENDU EN CLAIR : celui qui fait autorité est celui de la CHARGE, vérifié sur le poste ; l’API en rend les MESURES. Rendre les deux inviterait à installer d’après celui qui n’a pas été signé. `editeurConfigure` dit lequel des deux VIDES on regarde : « rien n’est diffusé » est un état du produit, « personne ne diffuse ici » en est un de la configuration. `repriseApplicationId` et `repriseVersionSourceId` sont ce qui distingue « installer », « déjà à jour » et « mise à jour ». `deMonEspace` (facultatif) : l’original est de l’espace qui lit — il s’ouvre depuis le lanceur, il ne se reprend pas. Une diffusion retirée n’y est pas ; une diffusion sans version signée non plus (elle ne désigne aucun programme). `jeSuisLEditeur` n’est PAS `editeurConfigure` : le premier dit que c’est MOI, le second que ce SERVEUR en a un — les confondre proposerait de diffuser à tous les locataires d’une instance qui en a un. L’écran masque son panneau de diffusion sur l’absence du champ, plutôt que d’offrir un bouton qui refusera. `parDefaut` (facultatif) : le poste l’installe d’office — absent, rien ne s’installe.
GET
/applications/diffusion/cibles
session de l’application seulement
Les espaces de travail qu’une diffusion peut viser : leur nom, et combien de membres ACTIFS.
RéponseCibleDiffusionDto[] — { espaceId, espaceNom, membres }
NoteRÉSERVÉE À L’ÉDITEUR, et 404 pour tout le monde d’autre — jamais 403 : c’est la seule lecture de ce dépôt qui traverse les espaces pour rendre des NOMS, et un 403 avouerait qu’il y a quelque chose à voir. Elle existe parce que les portées ESPACES_CHOISIS et MEMBRES_CHOISIS demandent des identifiants : sans elle, l’écran n’offrait qu’un champ où recopier un cuid, et la seule portée utilisable restait TOUS — celle qui porte le risque. Ni courriel, ni rôle, ni date. Session seule (la seconde serrure : un nom qui sort ne se reprend pas). Journalisée.
GET
/applications/diffusion/cibles/:id/membres
session de l’application seulement
Les membres ACTIFS d’UN espace, pour viser des personnes plutôt qu’un espace entier.
RéponseCibleMembreDto[] — { membreId, nom }
NoteMÊMES GARDES que la liste des espaces, et la liste ne se demande qu’espace par espace : la divulgation reste proportionnée au geste qu’on s’apprête à faire. AUCUN COURRIEL — le nom suffit à choisir, et une adresse est la donnée qu’on ne peut pas reprendre une fois sortie ; un membre sans nom est rendu « Sans nom », jamais sous son adresse. Les comptes désactivés n’y sont pas : proposer de diffuser à une cible qui n’ouvrira jamais rien n’aide personne.
GET
/applications/installations/ailleurs
Ce qui est installé sur un AUTRE de mes postes et pas ici : le nom local, l’icône, le favori, la version, les capacités accordées là-bas et ce que la version demande.
RéponseRepriseAilleursDto[] — { applicationId, nom, nomLocal, iconeLocale, favori, versionId, versionLibelle, entree, empreinte, capacites, demandees, ouverteLe }
NoteSANS APPAREIL ANNONCÉ (`x-nexus-appareil`), LA LISTE EST VIDE — jamais « tous mes postes » : le web n’a pas de disque, et lui proposer un geste impossible ne l’aiderait pas. STABLE seulement : seules les versions stables déposent leur archive au magasin, donc seules elles se rapatrient ici — offrir un essai promettrait une reprise qui échouerait après le clic. On ne propose pas non plus une application RETIRÉE du lanceur, ni une version dont le manifeste ne se lit pas sur ce serveur. Deux autres postes font UNE ligne : celle du plus récemment OUVERT, qui porte la personnalisation qu’on veut retrouver. On ne lit QUE ses propres installations — ce sont ses machines, ses noms et ses droits.
GET
/applications/:id/fiche
La FICHE DE PRÉSENTATION d'une application : accroche, description, prérequis, limites, bon à savoir, captures (en adresses data:), et si celui qui lit peut la modifier.
RéponseFicheApplicationDto — { applicationId, accroche, description, prerequis, limites, bonASavoir, captures: { id, dataUrl, legende }[], majLe, majParNom, redigeeParAgent, modifiable }
NoteLisible par qui VOIT l'application : son espace (la garde de la fiche), ou le catalogue qui la lui montre — la même visibilité que la fiche du rayon, sur la version VALIDÉE. Ailleurs : 404. Une fiche jamais écrite rend des textes vides et `majLe: null`, jamais un 404.
PUT
/applications/:id/fiche
Écrire la fiche de présentation de MON application (les cinq textes, remplacés en entier).
Corps{ accroche, description, prerequis, limites, bonASavoir }
RéponseFicheApplicationDto
NoteLe PROPRIÉTAIRE seul (403 `PAS_PROPRIETAIRE` sinon) — lui, ou un agent de sa session : la fiche retient alors `redigeeParAgent`. Du TEXTE : une ligne « - » est un point de liste, rien d'autre n'est interprété. Bornes : accroche 280, description 8 000, sections 3 000.
POST
/applications/:id/fiche/captures
Ajouter une CAPTURE d'écran à la fiche de MON application.
Corps{ dataUrl, legende }
RéponseFicheApplicationDto (201)
NoteUne image PNG, JPEG ou WebP en adresse data:, un mégaoctet au plus ; huit captures au plus (409 `TROP_DE_CAPTURES`). Propriétaire seul.
PATCH
/applications/:id/fiche/captures/:captureId
Changer la LÉGENDE d'une capture de la fiche.
Corps{ legende }
RéponseFicheApplicationDto
NotePropriétaire seul ; une capture d'une autre application : 404.
DELETE
/applications/:id/fiche/captures/:captureId
Retirer une capture de la fiche.
RéponseFicheApplicationDto
NotePropriétaire seul ; une capture d'une autre application : 404.
PUT
/applications/:id/fiche/captures/ordre
L'ORDRE des captures de la fiche.
Corps{ ids: string[] }
RéponseFicheApplicationDto
NoteLes identifiants donnés passent devant, dans cet ordre ; un identifiant inconnu est ignoré, une capture non nommée garde sa place derrière. Propriétaire seul.
GET
/applications/bundles/manquants
MES versions STABLES (90 derniers jours, 50 au plus) dont le serveur n’a pas reçu le programme : ce que le poste qui les a figées doit encore déposer.
Réponse{ versions: { applicationId, versionId, libelle }[] }
NoteLE RATTRAPAGE (2 octobre 2026). Le dépôt part une fois après un figeage ; raté (poste hors ligne, promotion d’essai), rien ne le refaisait, et un second ordinateur de la même personne ne pouvait pas installer. Le poste relit cette liste au démarrage puis toutes les dix minutes et dépose ce qu’il porte. Seulement les applications dont on est PROPRIÉTAIRE (seul le propriétaire dépose), jamais un essai.
POST
/applications/:id/installations/reprise
session de l’application seulement
Reprendre une application sur CE poste : l’installer ici en recopiant ce que l’autre poste avait — nom local, icône, favori, réglages et les MÊMES capacités.
Corpsaucun — et c’est la garde qui compte
RéponseApplicationInstallationDto (201)
NoteLA DEMANDE NE PORTE QUE L’IDENTIFIANT DANS L’ADRESSE : les capacités sont lues par le SERVEUR chez l’autre installation. Une route qui accepterait une liste serait un chemin pour s’accorder de nouveaux droits sous couvert de « reprise », et l’écran ne pourrait même pas le montrer. ON NE REPREND JAMAIS PLUS QUE CE QU’ON AVAIT, ni plus que ce que la version DÉCLARE : une capacité refusée là-bas reste refusée, une capacité que le manifeste ne déclare plus ne survit pas. SESSION SEULE : reprendre dix applications accorde dix jeux de droits sur une machine neuve — installer ACCORDE. Refus : 409 `DEJA_ICI` (pas une panne : c’est la réponse de qui reprend deux fois, et rien n’est écrasé), 404 `AUCUNE_AILLEURS`, 400 `SANS_APPAREIL`, 400 `MANIFESTE`. LE POSTE A DÉJÀ RAPATRIÉ ET VÉRIFIÉ le programme quand il appelle : le serveur ne bascule jamais vers une version qu’un poste n’a pas déclarée prête.
GET
/applications/:id/sauvegarde
session de l’application seulement
L’ÉTAT de MA sauvegarde de données pour cette application, SANS les octets : révision, empreinte, appareil qui a déposé, date, poids compressé. `?canal=STABLE|ESSAI`.
Réponse{ etat: { revision, empreinte, appareil, depuis, octets } | null }
NoteDÉCIDER NE DOIT RIEN COÛTER : une ouverture d’application lit cet état, et les octets ne partent que si la décision est de RESTAURER — les mettre ici ferait payer un transfert à chaque ouverture. `null` = rien au serveur, jamais « on ne sait pas ». `memberId` n’est un paramètre d’AUCUNE des trois routes : la sauvegarde lue est toujours celle de qui appelle, et un collègue qui a l’usage de la même application ne voit rien de la vôtre. SESSION SEULE, LECTURES COMPRISES — écart assumé au « lire reste ouvert » du reste du chantier : ce qu’on rend n’est pas un fait sur une application, c’est le contenu métier d’une personne.
GET
/applications/:id/sauvegarde/octets
session de l’application seulement
Les OCTETS de ma sauvegarde : le JSON des données gzippé, tel que le poste l’a déposé. Révision et empreinte en en-têtes.
Réponseles octets (application/gzip) ; x-nexus-sauvegarde-revision, x-nexus-sauvegarde-sha256
NoteEN CORPS BINAIRE, comme l’archive d’une version : base64 dans un JSON coûterait un tiers de plus sur une charge de huit mégaoctets. Aucune sauvegarde : 404 `ABSENTE`, jamais un corps vide. LE MODE AVEUGLE NE FERME PAS LA LECTURE — il ferme ce qui SORT : une sauvegarde déjà en base, sur un projet qu’on vient de classer, doit redescendre chez son propriétaire. Un déchiffrement qui échoue (clé du coffre changée) rend 409 `EMPREINTE` et JAMAIS « rien au serveur » : le poste pousserait par-dessus.
PUT
/applications/:id/sauvegarde
session de l’application seulement
DÉPOSER ma sauvegarde : les octets gzippés en corps binaire, le canal, la révision attendue et l’empreinte du contenu EN CLAIR en en-têtes.
Corpsles octets ; en-têtes x-nexus-sauvegarde-canal, -revision, -sha256, -appareil-nom
Réponse{ etat: { revision, empreinte, appareil, depuis, octets } }
Note`x-nexus-sauvegarde-revision` EST LE VERROU : la révision que le poste a LUE, `0` pour la première sauvegarde. Si le serveur a bougé depuis, 409 `CONFLIT` et RIEN n’est écrit — sans lui, deux postes qui poussent à la même minute verraient le second effacer le premier en silence. Le serveur DÉCOMPRESSE et recalcule l’empreinte (400 `EMPREINTE` sinon) : une archive tronquée en route ne doit pas devenir une sauvegarde qu’on croit bonne, et on ne le découvrirait autrement que sur le second ordinateur. Bornes DITES : huit mégaoctets compressés et soixante-quatre décompressés (413 `TROP_GROS` — huit mégaoctets de gzip fabriqués exprès se déplient en gigaoctets), cent-vingt-huit pour toutes les sauvegardes d’une PERSONNE (507 `QUOTA`). MODE AVEUGLE : 403 `AVEUGLE`, pour tout le monde. ON N’ÉCRASE JAMAIS SANS GARDER : la génération courante descend d’un cran, UNE seule. Les octets sont gzippés PUIS chiffrés sous la clé du coffre.
GET
/applications/:id/provenance
Ce qu’une copie sait de sa source, lu à l’ouverture pour honorer une révocation : révoquée ? retirée du catalogue ? de quelle version vient-elle ?
Réponse{ joignable, revocation, versionRevoqueeId, versionSourceId, retiree }
NoteSANS SOURCE, la réponse est « rien n’est fermé » — jamais 404 : le poste l’appelle sur TOUTES ses applications, et la plupart ne viennent d’aucun catalogue. `joignable` est toujours vrai ici (c’est le serveur qui répond) ; le POSTE le pose à faux quand il n’a pas pu demander, et l’application s’ouvre quand même — ce qui ferme une application installée est une RÉVOCATION, jamais une ignorance.
PUT
/me/application-cle
session de l’application seulement
Déclarer ma clé publique de signature d’applications (DER/SPKI en base64, sur une seule ligne). La PRIVÉE ne quitte jamais mon poste.
CorpsclePublique
Réponse{ clePublique }
NoteSa force est MOINDRE que celle de la clé de l’éditeur, qui est livrée avec l’application : qui contrôle ce serveur pourrait substituer celle-ci. La vérification prouve alors que le bundle n’a pas bougé depuis sa signature, pas que la personne nommée l’a signé — et l’écran le DIT. La remplacer est permis (perdre une clé privée arrive) : les diffusions signées avec l’ancienne cesseront de se vérifier, et le journal le dit pour expliquer les verdicts qui suivront.
GET
/me/application-cle
Ma clé publique de signature, ou `null` si je n’en ai déclaré aucune.
Réponse{ clePublique: string | null }
NoteOuvert : une clé PUBLIQUE n’a rien de secret. `null` = aucune n’est déclarée, donc rien à vérifier — jamais une chaîne vide.
GET
/bibliotheque/contenus
La bibliothèque de Nexus : ce que des personnes de TOUS les espaces ont diffusé (un tableau de Tableur, un AGENT de l’application Agent), sans les corps.
Requêteapplication (tableur par défaut, ou agent), q, miens=1
Réponse{ contenus: FicheBibliothequeDto[] }
NoteLa seule lecture entre entreprises, délibérée : on ne lit que ce qui a été publié, et la fiche ne porte que la provenance (nom de l’auteur, de son entreprise) — jamais un identifiant de membre ni un courriel.
GET
/bibliotheque/contenus/:id
Un contenu de la bibliothèque, corps compris — pour l’installer (chaque lecture compte une installation, sauf `compter=0`).
Requêtecompter=0 (une lecture qui n’installe pas : relire un agent avant de l’installer, ou la version neuve d’un agent installé)
RéponseContenuBibliothequeDto
POST
/bibliotheque/contenus
Diffuser un contenu à tous les espaces, ou republier le sien (`remplace`).
CorpsPublicationBibliothequeInput
RéponseFicheBibliothequeDto
NoteUne copie figée : installer pose un tableau neuf, republier ne touche à aucune installation. 2 Mo au plus, 500 contenus par entreprise. Republier la fiche d’un autre : 404. Un AGENT (`application: agent`) : le geste d’une PERSONNE (403 à une clé, à un agent) ; le corps passe un schéma strict (`DefinitionAgentDiffusee` — aucun périmètre, aucun identifiant), aucun secret (422 `SECRET` avec `champs`), le nom recopié dans `confirmation` (422 `CONFIRMATION`) ; titre, description et aperçu sont tirés du corps.
DELETE
/bibliotheque/contenus/:id
Retirer un contenu de la bibliothèque : son auteur, ou l’administration de son entreprise.
Réponse{ retire: true }
NotePour tout autre, introuvable (404), jamais « interdit ». Les copies déjà installées restent à ceux qui les ont.