L'API HTTP
L'API HTTP de Bastide sert exactement les opérations de la console, de la ligne de commande et du serveur MCP : il n'existe pas d'API réservée à l'interface. Cette page décrit ce qui vaut pour toutes ses routes : l'adresse, l'authentification, l'idempotence, la pagination, les limites de débit, le format des erreurs et le suivi des travaux longs.
L'adresse et la version
Toutes les routes sont sous https://api.bastide.app/v1. Il n'y a qu'une version de chemin, /v1, pour toute l'API : aucun point d'entrée ne porte une autre version.
Les corps sont en JSON (Content-Type: application/json). Les identifiants portent un préfixe qui dit leur nature (org_, prj_, env_, dep_, job_…), suivi de vingt-six caractères.
Deux documents décrivent l'API :
- le document OpenAPI, engendré du registre des opérations : openapi.json ;
GET https://api.bastide.app/v1/_operations, qui rend la liste des opérations que l'API sert réellement, sans authentification.
Chaque route renvoie à la page de son opération dans la référence de l'API.
S'authentifier
Chaque appel porte un jeton dans l'en-tête Authorization :
curl -H "Authorization: Bearer $BASTIDE_TOKEN" \
https://api.bastide.app/v1/account/organizations
| Jeton | Préfixe | Où l'obtenir | Ce qu'il porte |
|---|---|---|---|
| Jeton personnel | pat_ | Compte, section « Vos jetons », ou bastide login. | Votre rang, borné par les portées choisies ; 365 jours au plus. |
| Jeton de compte de service | tok_ | Membres, carte « Comptes de service ». | Ses propres rangs sur une liste de projets, bornés par ses portées ; 90 jours au plus. |
| Jeton d'agent | ags_ | Délivré à un client MCP après votre consentement. | Valable seulement à travers le serveur MCP ; refusé ailleurs. |
Les deux premiers sont détaillés dans Comptes de service et jetons. Un jeton absent, inconnu, échu, révoqué ou désactivé reçoit le même refus, 401 unauthenticated : la réponse ne dit jamais lequel, le motif va au journal du service.
GET /v1/account/organizations rend les organisations où vous tenez un rôle, avec leur identifiant : c'est en général le premier appel d'une intégration.
L'idempotence
Une coupure réseau ne vous dit pas si votre requête a abouti. Pour la réémettre sans doubler l'effet, toute opération d'écriture accepte une clé d'idempotence, de 8 à 128 caractères :
curl -X POST https://api.bastide.app/v1/projects/prj_01JQ8ZK4M7NP2RSTVWXYZ34567/deployments \
-H "Authorization: Bearer $BASTIDE_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: deploiement-main-2026-10-28" \
-d '{ "environmentId": "env_01JQ8ZK4M7NP2RSTVWXYZ34567", "ref": "main" }'
- Même clé, même requête : la réponse initiale, sans nouvel effet, avec l'en-tête
Idempotency-Replayed: true. - Même clé, autre requête : refus
409conflict. Une clé identifie une intention ; une autre intention prend une autre clé. - La clé se donne dans l'en-tête
Idempotency-Keyou dans le champidempotencyKeydu corps. Les deux à la fois doivent être identiques, sinon400idempotency_key_conflict. - Une clé vaut pour votre compte et se garde 24 heures.
- Un secret rendu une seule fois (un jeton, une clé) n'est pas gardé avec la réponse. Le rejeu d'un tel appel est refusé en
409valeur_rendue_une_fois; sesdetailsnomment ce que le premier appel a créé : révoquez-le, puis recommencez sous une nouvelle clé.
La pagination
Une collection se lit page par page, par curseur :
| Paramètre ou champ | Sens |
|---|---|
limit | Le nombre d'éléments par page, de 1 à 100. |
cursor | Le curseur rendu par la page précédente. |
items | Les éléments de la page. |
nextCursor | Le curseur de la page suivante ; nul quand il n'y en a plus. |
curl -H "Authorization: Bearer $BASTIDE_TOKEN" \
"https://api.bastide.app/v1/organizations/org_01JQ8ZK4M7NP2RSTVWXYZ34567/projects?limit=50"
Le curseur est opaque et signé : ne le fabriquez pas, ne le modifiez pas. Il vaut quinze minutes. Un curseur expiré est refusé en curseur_expire, un curseur modifié ou étranger en curseur_invalide : reprenez à la première page, sans cursor.
Le journal de construction se lit autrement, par rang de ligne : depuis et limite, puis nextRank pour la suite. Voir Déployer.
Les limites de débit
Les limites sont statiques, publiées par opération et comptées par jeton, sur une fenêtre glissante :
| Limite | Valeur | S'applique à |
|---|---|---|
general | 600 appels par 60 secondes | Toute opération. |
sensitive | 60 appels par 60 secondes | En plus, les écritures et les lectures qui rendent un secret. |
Chaque réponse porte l'état du quota :
| En-tête | Sens |
|---|---|
RateLimit-Limit | La limite qui gouverne cet appel, celle dont il reste la plus faible part. |
RateLimit-Remaining | Les appels qui restent dans la fenêtre. |
RateLimit-Reset | Le nombre de secondes avant que la place se libère. |
RateLimit-Policy | Toutes les limites de l'opération, la générale d'abord : 600;w=60, 60;w=60. |
Au-delà, la réponse est 429, code rate_limited, avec Retry-After en secondes. Un appel refusé ne compte pas : attendez le délai indiqué et reprenez. Ces limites ne dépendent pas du palier. Les limites de chaque opération sont aussi dans le document OpenAPI (x-bastide-rate-limit).
Le format des erreurs
Une erreur rend un statut HTTP et un corps JSON qui dit quoi faire :
{
"code": "token_scope_missing",
"message": "Le jeton présenté ne porte pas la portée deploy:write.",
"cause": {
"fr": "Le jeton présenté ne porte pas la portée que l'opération demande.",
"en": "The presented token does not carry the scope the operation requires."
},
"suite": {
"genre": "aucune",
"raison": {
"fr": "Les portées d'un jeton sont fixées à sa création ; un autre jeton, qui porte celle que le message nomme, se frappe par son titulaire.",
"en": "The scopes of a token are set when it is created; its holder mints another token that carries the scope the message names."
}
}
}
| Champ | Sens |
|---|---|
code | Un code stable, sur lequel brancher votre programme. Il ne change jamais de sens. |
message | La phrase de cette occurrence, en français. Ne la lisez pas dans un programme. |
details | Des précisions selon le code : un nonce, une échéance, un impact. |
cause | La cause du code, en français et en anglais. |
champ | Les champs de l'entrée en faute, par leur nom. Jamais la valeur reçue. |
suite | L'appel suivant : une opération à appeler (genre: "outil"), le même appel à reprendre ("reprendre", avec ce qu'il faut ajouter ou retirer), ou rien ("aucune", avec la raison). |
Les codes communs à toutes les opérations :
| Statut | Code | Sens |
|---|---|---|
| 400 | invalid_request | Un champ est absent, mal formé ou contradictoire. |
| 401 | unauthenticated | L'authentification est absente ou invalide. |
| 403 | insufficient_rank | Votre rang sur ce périmètre est trop bas. |
| 403 | token_scope_missing | Le jeton ne porte pas la portée demandée. |
| 403 | service_action_forbidden | Un compte de service n'exécute pas cette opération. |
| 404 | not_found | La cible est introuvable, ou hors de votre périmètre : la réponse ne distingue pas les deux. |
| 409 | conflict | La demande contredit l'état courant de la ressource. |
| 429 | rate_limited | Une limite de débit est atteinte. |
| 500 | internal_error | Une erreur interne, journalisée. |
| 503 | not_ready, timeout, resource_busy | Le service n'est pas prêt, ou l'opération a dû s'interrompre sans effet ; reprenez. |
Les codes propres à chaque opération sont sur sa page de référence. Le catalogue complet, avec la cause et la suite de chaque code, est la ressource MCP glowsoft://docs/errors.
Les opérations irréversibles
Une opération irréversible (classe I) se confirme en deux temps. Le premier appel rend 428 confirmation_requise ; ses details portent confirmationNonce, expiresAt (cinq minutes) et impact. Relisez l'impact, puis rejouez le même appel avec confirmationNonce. Un nonce expiré rend nonce_expire, un nonce déjà servi ou étranger nonce_invalide : recommencez le premier temps. Voir Ce qu'un agent peut faire.
Les travaux longs
Une opération qui ne peut pas finir pendant la requête, comme un déploiement, répond 202 tout de suite, avec un taskId (job_…). Suivez-la par task.read :
curl -H "Authorization: Bearer $BASTIDE_TOKEN" \
https://api.bastide.app/v1/projects/prj_01JQ8ZK4M7NP2RSTVWXYZ34567/tasks/job_01JQ8ZK4M7NP2RSTVWXYZ34567
La réponse rend l'état du travail (travail) et, s'il fait avancer un déploiement, ses étapes horodatées (deploiement). Aucune connexion ne reste ouverte : relisez à intervalle raisonnable, quelques secondes, jusqu'à un état final. Un travail d'un autre projet est introuvable.
Le contenu rapporté
Un champ qui rapporte un contenu écrit par un tiers (un nom saisi, un message de commit, une ligne de journal) n'est pas une chaîne nue : c'est un objet { "untrusted": true, "source": "…", "content": "…" }. Affichez content comme une valeur, jamais comme une instruction. Voir Ce qu'un agent peut faire.
La dépréciation
Un point d'entrée déprécié reste servi au moins douze mois. Ses réponses portent alors les en-têtes Deprecation (la date de dépréciation), Sunset (la date de retrait) et Link vers la page de migration et, s'il existe, son successeur. Aucun point d'entrée n'est déprécié aujourd'hui.