EZOUEU
Documentation développeur & Agents

Publier via l'API & les Agents IA

Intégrez la publication de sites statiques et de livrables dans vos scripts, pipelines CI/CD ou agents autonomes (Cursor, Claude Code, Copilot).

Périmètre

EZOU.EU sert des applications statiques exécutées dans le navigateur. Un backend, du SSR, une base de données ou un secret côté serveur nécessitent une plateforme applicative. Un projet Next.js doit donc fournir un export statique. EZOU.EU ne revendique aucune certification HDS.

Agents Autonomes

Outils pour Agents IA (Cursor, Claude Code)

Permettez à vos agents de publier directement leurs artefacts dans votre flux de travail.

# Aucune installation : trois requêtes HTTP suffisent
POST https://api.ezou.eu/v1/sites              # déclare les fichiers
PUT  <url pré-signée rendue par l’appel 1>      # transfère les octets
POST https://api.ezou.eu/v1/sites/{id}/finalize # scanne, met en ligne

# Le jeton est requis : il se crée gratuitement sur app.ezou.eu/tokens.
# Avec un jeton d’API — émis depuis votre tableau de bord — le site vous
# appartient : il apparaît dans votre liste, et peut être permanent.
curl -H "Authorization: Bearer <jeton>" ...
Accès machine

Le jeton d’API

Requis. C’est lui qui rattache à votre compte ce que votre agent publie.

Sans jeton, l’API répond 401 account_required : la publication anonyme a existé — « sans compte, 24 h » — puis a été retirée. Le compte gratuit se crée avec une adresse email, sans mot de passe ni carte bancaire, et le jeton s’émet dans la foulée.

Avec un jeton, le site appartient au compte dès sa déclaration : il figure dans la liste des publications, peut être permanent, porter un sous-domaine choisi, et n’être supprimé que par son propriétaire.

# L’en-tête, sur n’importe quel appel de l’API
curl -H "Authorization: Bearer <votre jeton>" …

# Pour un serveur MCP, une variable d’environnement suffit
EZOU_TOKEN=<votre jeton>

L’obtenir

Dans le tableau de bord, onglet Jeton d’API. Il s’affiche une seule fois : le service n’en conserve que l’empreinte, jamais la valeur. Perdu, il faut en émettre un autre.

Un seul jeton par compte

En émettre un nouveau révoque le précédent : l’agent qui utilisait l’ancien reçoit alors 401 unauthorized jusqu’à ce qu’il reçoive le nouveau. La révocation est immédiate et gratuite ; elle ne touche pas aux sites déjà publiés, elle empêche d’en publier d’autres.

Émettre et révoquer sont des gestes réservés à une session humaine du tableau de bord. Un jeton ne peut pas se renouveler ni se révoquer lui-même — sans quoi un agent compromis couperait l’accès des autres.

Le connecteur MCP

Pour publier depuis une conversation Claude, le tableau de bord émet uneURL de connecteur : un serveur MCP distant, en HTTP streamable, dont le justificatif voyage dans le chemin. Elle expose onze outils —publish, list, status,set_options, update, versions,rollback, stage, promote,delete etsubprocessors — de quoi publier et gérer un lien après coup : remplacer le contenu sans changer l’adresse, corriger un seul fichier, revenir à une version précédente, la faire regarder en test avant qu’elle ne parte sur le domaine du client, changer qui peut l’ouvrir, allonger la durée, la rendre permanente.

update accepte patch: true : files ajoute ou remplace, delete retire, et tout le reste du site est conservé sans repasser par l’appel. status rend la liste des fichiers en ligne, de quoi savoir quoi corriger. La version précédente est archivée par défaut et reste récupérable par rollback ; keep_history: false l’efface. L’historique demande une offre payante : sur l’offre gratuite, chaque mise à jour efface la précédente.

publish et update acceptent aussi scan: true. Le contenu n’est pas analysé par défaut : c’est ce qui rend la mise en ligne immédiate. Demandée, l’analyse cherche secrets, NIR, IBAN et cartes, et refuse la publication en cas de secret. Une organisation peut l’imposer à tout son espace, et ce réglage-là ne se desserre pas depuis un appel.

set_options porte tous les réglages d’après-coup, et il en accepte plusieurs dans le même appel — les champs omis ne sont pas touchés :access (avec password ou domains),expiry et expires_at, slug etexact_slug, online pour suspendre un lien et le rétablir,keep_badge pour la mention « Publié avec ezou.eu », etdeploy: "direct" pour quitter le déploiement en deux temps. Il remplace les anciens set_access, set_expiry et set_slug : trois outils voisins pesaient dans la définition que chaque conversation relit, et « mets un mot de passe et garde-le une semaine » demandait deux appels.

La brancher, selon le client

La même URL sert les trois chemins : aucun paquet à installer, et rien d’autre à remplir que l’adresse.

# Claude.ai et Claude Desktop — « Ajouter un connecteur personnalisé »
# Champ « URL du serveur MCP distant » : collez l’URL, validez, c’est tout.

# Claude Code — une commande, transport HTTP
claude mcp add --transport http ezou "<votre URL de connecteur>"
# puis /mcp dans la conversation pour vérifier que les onze outils sont là

# Cursor et les autres clients MCP — ~/.cursor/mcp.json
{"mcpServers":{"ezou":{"url":"<votre URL de connecteur>"}}}

Ces trois formes écrivent le justificatif côté client, et c’est là qu’il fuit : une commande tapée entre dans l’historique du shell, un mcp.json de projet part au dépôt s’il n’est pas ignoré. Deux réflexes : passer l’URL par une variable d’environnement là où le client développe ${VAR}, et garder la configuration hors du dépôt. En cas de doute, révoquez : le tableau de bord réémet une URL en un geste, et l’ancienne cesse immédiatement de répondre.

Le justificatif est dans l’URL, et il est distinct du jeton d’API : ce dialogue n’offre aucun champ d’en-tête. Les deux se révoquent indépendamment, de sorte que couper le connecteur ne coupe pas vos agents en ligne de commande. Traitez cette URL comme un mot de passe : le service ne journalise jamais ce chemin, mais un client qui la stocke, lui, la conserve.

Par ce chemin, le contenu des fichiers arrive dans l’appel — un serveur distant ne lit pas votre disque. Le total est donc plafonné à 2 Mo, ce qui couvre largement une page ou un rapport autonome. Au-delà, passez par l’API : les octets vont directement au stockage sans transiter par le service.

Le privé, par défaut

Sur tous les chemins — interface, API, connecteur — l’accès par défaut estprivate, et c’est aussi ce que les outils recommandent à l’agent. L’adresse porte 130 bits d’entropie ; si vous choisissez un nom lisible, un suffixe aléatoire lui rend cette entropie. La limite reste celle d’une adresse : quiconque l’obtient peut l’ouvrir et la transmettre.

public ne rend pas un site indexable : le noindex est le défaut sur toutes les offres et tous les modes. « Public » veut dire « partageable par lien », pas « référencé ». Ce que le mode change, c’est qu’un nom choisi y reste tel quel — donc devinable.

Le référencement existe, mais il ne s’obtient par aucun appel : c’est une bascule par site, au tableau de bord, ouverte par l’offre Business. Elle exige un accès public — la bascule y fait passer le site, plutôt que d’indexer une adresse censée rester non listée — et une seule origine est référencée : le domaine personnalisé quand il est en service, l’adresse en.ezousite.eu sinon. Sur les offres Gratuit et Plus, la bascule n’existe pas et l’en-tête restenoindex, nofollow, noarchive, nosnippet : tant que le compte n’est pas Business, un sitemap.xml, un ping IndexNow ou deshreflang publiés sur un site ezou ne peuvent rien produire.

À traiter comme un mot de passe

Un jeton donne le droit de publier au nom du compte. Il n’a sa place ni dans un dépôt, ni dans un ticket, ni dans une conversation. Il ne permet en revanche pas de se connecter au tableau de bord, ni de changer d’offre.

API REST Standard

Flux de publication en 3 appels HTTP

1. Déclarer les fichiers · 2. Envoyer le contenu vers S3 · 3. Finaliser et scanner

# 1. Déclarer le site et les fichiers à envoyer
curl -X POST https://api.ezou.eu/v1/sites \
  -H "Authorization: Bearer $EZOU_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "expiry": "24h",
    "access": "private",
    "files": [{"path": "index.html", "size": 2048}]
  }'
# → {"site_id":"s_…", "upload":[{"path":"index.html","url":"https://…"}], "claim_token":"c_…"}

# 2. Envoyer chaque fichier directement vers son URL de stockage pré-signée
curl -T index.html "$UPLOAD_URL"

# 3. Déclencher le scan et mettre en ligne
curl -X POST https://api.ezou.eu/v1/sites/s_…/finalize \
  -H "Authorization: Bearer $EZOU_TOKEN"
# → {"url":"https://.ezousite.eu", "expires_at":"…", "pii_scan":{"status":"clean"}}

Paramètres & Options de publication

Tous les paramètres sont optionnels. Les valeurs par défaut appliquent la configuration la plus protectrice.

ParamètreValeurs acceptéesDéfautDescription
expiry24h · 7d · 30d · custom · permanent24hcustom exige expires_at dans le futur ; permanent et custom exigent un compte
accessprivate · public · password · email_domainprivateprivate = URL non listée de 130 bits, transmissible par son détenteur
slugchaîne personnaliséealéatoireNom choisi, sur toutes les offres ; un suffixe aléatoire de 12 caractères est ajouté
exact_slugtrue · falsefalseRetire le suffixe — l’adresse est alors exacte. Offres payantes (Plus et au-delà)
pii_modeoff · warn · blockoffPar défaut le contenu n'est PAS analysé. warn analyse et signale ; block refuse aussi IBAN, cartes et emails (offres payantes). La policy d'un compte peut imposer un mode, jamais le desserrer
modereplace · patchreplaceSur /update : replace remplace tout le site, patch ne touche que les fichiers déclarés et ceux de delete[]
keep_historytrue · falsetrueSur /update : la version précédente est archivée par défaut et reste récupérable par rollback. false l'efface, stockage compris, sans recours. Offres payantes ; l'offre gratuite n'a aucun historique et y efface toujours
stagetrue · falsefalseSur /update et /finalize-update : la nouvelle version ne part que sur l'adresse du service, le domaine personnalisé garde la sienne jusqu'à promote. Exige un domaine personnalisé vérifié — donc l'offre Business
Mention en pied de page

Les pages publiées depuis un compte gratuit portent une discrète mention « Publié avec ezou.eu » en bas à droite, que le visiteur peut masquer d'un clic. Elle est injectée à la volée dans la réponse : votre fichier stocké n'est jamais modifié. Les offres payantes ne la portent pas, et s'abonner la retire immédiatement des sitesdéjà en ligne, sans republication.

Préproduction

Déploiement en deux temps

Regarder la nouvelle version en ligne avant qu’elle ne parte sur le domaine du client.

Un site servi par un domaine personnalisé a deux adresses : celle du service, <slug>.ezousite.eu, et le domaine. En publication directe — le défaut — les deux servent la dernière version. Le deux temps les sépare : la nouvelle version ne va que sur l’adresse du service, et le domaine garde la sienne jusqu’à ce que vous la promouviez. Un domaine personnalisé vérifiéest donc requis : sans lui il n’y a pas de seconde adresse, et le geste est refusé. Le domaine s’attache depuis le tableau de bord, à partir de l’offre Business.

Il n’y a pas d’interrupteur « activer » : le premier stagefige la production sur la version qu’elle servait à cet instant. C’est le seul chemin d’entrée dans le mode — deploy-mode n’accepte quedirect, qui en sort.

# 1. Ouvrir une mise à jour qui ne touchera pas le domaine
curl -X POST https://api.ezou.eu/v1/sites/s_…/update \
  -H "Authorization: Bearer $EZOU_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"stage": true, "files": [{"path": "index.html", "size": 2048}]}'
# → {"version":4, "upload":[…]} — le refus arrive ICI, avant les transferts

# 2. Après les PUT : mettre en ligne EN TEST. La version est explicite
#    en deux temps — la tête a pu reculer, la déduire écraserait une archive.
curl -X POST https://api.ezou.eu/v1/sites/s_…/finalize-update \
  -H "Authorization: Bearer $EZOU_TOKEN" \
  -d '{"version": 4, "stage": true, "keep_history": true}'
# → {"url":"https://.ezousite.eu", "staged":true,
#    "live_version":3, "live_url":"https://client.example"}

# 3. Regarder, puis publier sur le domaine
curl -X POST https://api.ezou.eu/v1/sites/s_…/promote \
  -H "Authorization: Bearer $EZOU_TOKEN" -d '{"version": 4}'
# → {"live_version":4, "live_url":"https://client.example", "staged":false}
#    Sans "version", promote met en ligne ce qui est en test.

# Remettre une version archivée en test, ou quitter le deux temps
curl -X POST https://api.ezou.eu/v1/sites/s_…/stage       -d '{"version": 2}'
curl -X POST https://api.ezou.eu/v1/sites/s_…/deploy-mode -d '{"mode": "direct"}'
L’URL rendue est celle de la préproduction

Quand stage est demandé, le champ url de la réponse est l’adresse du service — celle que cet appel a changée — et jamais le domaine, qui n’a pas bougé. staged, live_version etlive_url disent ce que le domaine sert pendant ce temps. Un agent qui recopie url en annonçant « votre site est à jour » annonce une adresse où le changement n’est pas encore.

Trois autres réponses changent dans ce mode.GET /v1/sites/{id}/versions ajoute un champ serving par version — live, staging ou live+staging — parce que « active » ne dit plus quelle adresse sert quoi, et rend live_versionet staged à côté de current_version.GET /v1/sites/{id}/stats sort les vues de l’adresse de test des compteurs du public et les rend à part, dans staging_hits : vos allers-retours de validation ne gonflent pas la courbe que lit le client. Etrollback réaligne les deux adresses : c’est le geste d’urgence, pas la correction d’une préproduction — pour celle-ci, unstage vers une autre version suffit.

Depuis une conversation, ce sont les outils stage et promote,stage: true sur update, et deploy: "direct" surset_options.

Contrôles documentés

Accès, rétention et attestations

Chaque mécanisme a un périmètre précis et une limite à connaître.

Analyse DLP à la demande

Le contenu n’est pas analysé par défaut : la mise en ligne est immédiate. Demandez l’analyse avec pii_mode (ou scan depuis un agent) et elle recherche notamment secrets techniques, NIR, IBAN et cartes. Le mode avertissement signale les détections ; le mode bloquant refuse la publication selon le plan et la policy. Une organisation peut l’imposer à tout son espace. Sans analyse, le rapport rend skipped — ce qui n’est pasclean. L’analyse ne détecte pas tous les secrets ou toutes les données personnelles et ne remplace pas une revue humaine.

Chemins refusés

Certains chemins sont refusés à la déclaration, avant qu’un seul octet ne soit transféré : fichiers et dossiers cachés (.env,.git/, .ssh/, .aws/), clés privées (id_rsa, *.pem, *.key) et identifiants d’outils. La réponse est un 400 sensitive_file, etdetails.path nomme le fichier pour qu’un agent puisse le retirer et recommencer.

C’est un refus par nom, complémentaire du scan, qui inspecte lecontenu. Il s’applique de la même façon quelle que soit la voie utilisée — interface, API ou serveur MCP — et vaut aussi au moment de servir un fichier. Un client peut écarter ces fichiers de son côté ; ce n’est pas ce qui fait la garantie.

Niveaux d’accès

Le mode private produit une URL non listée et difficile à deviner. Toute personne qui obtient cette adresse peut toutefois l’ouvrir et la transférer. Utilisez un mot de passe pour vérifier un secret partagé ou une restriction par domaine email pour vérifier une adresse professionnelle.

Expiration et révocation

La durée est définie à la publication : 24 heures par défaut, ou 7 jours, 30 jours, une date choisie, ou permanent. Un propriétaire peut désactiver, réactiver ou supprimer une publication selon son état et son plan. Expiration de l’accès, suppression du stockage principal et disparition des sauvegardes éventuelles sont des événements distincts.

Journal et attestation d’effacement

Lors de la suppression, EZOU.EU émet une attestation horodatée et signée avec Ed25519. Vérifiée avec la clé que l’attestation embarque, la signature établit l’intégrité de la déclaration : elle n’a pas été modifiée depuis sa signature. Attribuer cette déclaration à EZOU.EU demande une étape de plus — comparer cette clé publique à celle publiée sur/.well-known/ezou-deletion-key et refuser si elle diffère. Même authentifiée, elle ne prouve pas, à elle seule, la destruction physique de toute copie possible ni l’effacement d’une copie téléchargée par un destinataire.

Points d'entrée de l'API v1

MéthodePoint d'entréeDescription
POST/v1/sitesCréer un site et obtenir les URLs d'upload pré-signées
PUT<upload_url>Envoyer un fichier directement vers le stockage S3 Scaleway
POST/v1/sites/{id}/finalizeMettre en ligne le site, après analyse DLP si elle a été demandée
GET/v1/sitesLister ses publications — de quoi retrouver un id perdu
GET/v1/sites/{id}Consulter l'état et les métadonnées d'un site
POST/v1/sites/{id}/updateNouvelle version sans changer l'adresse. mode:"patch" ne déclare que les fichiers ajoutés ou remplacés, et delete[] ceux à retirer
POST/v1/sites/{id}/finalize-updateMettre en ligne la nouvelle version (et l'analyser si pii_mode le demande)
GET/v1/sites/{id}/filesLister les fichiers d'une version — de quoi savoir quoi remplacer ou retirer
GET/v1/sites/{id}/statsConsultations : total, courbe par jour, et le détail par page, provenance, navigateur et pays (offres payantes). En deux temps, les vues de l'adresse de test sont rendues à part, dans staging_hits
GET/v1/sites/{id}/versionsLister l'historique des versions (offres payantes)
POST/v1/sites/{id}/rollbackRestaurer une version antérieure sans changer l'adresse ; en deux temps, elle réaligne les deux adresses
POST/v1/sites/{id}/stageDéploiement en deux temps : mettre une version EN TEST sur l'adresse du service ; le domaine personnalisé garde la sienne
POST/v1/sites/{id}/promotePublier sur le domaine personnalisé la version en test, ou une version précise
POST/v1/sites/{id}/deploy-modemode:"direct" quitte le deux temps ; le domaine sert de nouveau chaque publication
POST/v1/sites/{id}/accessChanger qui peut ouvrir le lien ; l'adresse ne bouge pas
POST/v1/sites/{id}/expiryChanger la durée de vie ; elle court à partir de maintenant
POST/v1/sites/{id}/slugRenommer le sous-domaine ; l'ancien reste réservé 30 jours
POST/v1/sites/{id}/disableSuspendre : l'adresse rend 404, rien n'est effacé, l'échéance court
POST/v1/sites/{id}/enableRétablir l'accès, même adresse et même contenu
POST/v1/sites/{id}/badgeAfficher ou retirer la mention « Publié avec ezou.eu » (keep_badge)
DELETE/v1/sites/{id}Supprimer le site et recevoir une attestation d'effacement signée
GET/v1/sites/{id}/proofTélécharger l'attestation d'effacement signée
GET/v1/subprocessorsConsulter la liste machine-readable des sous-traitants UE

Gestion des erreurs

Les réponses d'erreur fournissent un diagnostic clair et immédiatement actionnable.

CodeHTTPExplication & Résolution
pii_blocked422Contenu contenant une clé API, un mot de passe ou une fuite de données
sensitive_file400Chemin refusé avant tout transfert : fichier ou dossier caché, clé privée, identifiants d’outil. Le champ details.path nomme le fichier
unauthorized401Jeton d’API inconnu ou révoqué. Émettez-en un depuis le tableau de bord
mcp_payload_too_large413Connecteur MCP : le contenu arrive dans l’appel, plafonné à 2 Mo au total. Au-delà, publiez par l’API
file_kind_too_large413Plafond du TYPE de fichier, identique sur toutes les offres : images 10 Mo, documents 50 Mo (HTML, PDF, bureautique — leur contenu est analysé intégralement), vidéo, audio et archives 200 Mo
account_storage_full402Le compte a atteint le volume total inclus dans son offre, tous sites confondus (voir /tarifs). Les champs details.used_mb et details.limit_mb donnent les chiffres
file_too_large413Plafond par fichier de l’instance — technique, pas commercial : aucune offre ne le lève
missing_index400Un fichier index.html est obligatoire à la racine
account_required401Publier exige un compte. Émettez un jeton d’API depuis le tableau de bord, ou utilisez votre URL MCP
slug_not_allowed403Sous-domaine réservé ou nom non autorisé. Le champ details.reason distingue « ce nom est pris » de « ce nom est interdit »
slug_taken409Ce sous-domaine est déjà utilisé par un site en ligne
plan_required402Fonctionnalité de l’offre payante. Le champ details.field nomme ce qui a été refusé — access, pii_mode, slug, stage ou promote
version_conflict409Une autre mise à jour est passée pendant celle-ci : l'appliquer écraserait son travail. Repartez de la version courante
base_version_gone409La version dont la mise à jour partielle reprenait ses fichiers a disparu — historique rogné, restauration, purge. Relancez, ou envoyez tout le dossier en mode replace
unknown_path404Le chemin à remplacer ou à supprimer n'est pas dans le site. GET /v1/sites/{id}/files liste ce qui s'y trouve
empty_patch400Une mise à jour partielle doit changer quelque chose : au moins un fichier, ou au moins un chemin dans delete
invalid_json400Corps illisible, ou champ refusé : details.field le nomme et details.hint dit quoi faire. C'est la réponse des gestes de déploiement en deux temps quand le site n'a pas de domaine personnalisé vérifié, ou quand la version à finaliser n'est pas explicite
rate_limited429Trop de requêtes pour cette fenêtre
Scan de sécurité anti-fuites

En mode bloquant, une clé privée, un jeton secret ou un mot de passe détecté empêche la mise en ligne (pii_blocked). En mode avertissement, le verdict est signalé sans bloquer. Le rapport indique le type de détection et l’offset concerné sans afficher la valeur du secret.