# API REST : déployer depuis la CI et automatiser

> Référence de l’API REST de Vimonto Deploy : jetons et portées, chaque endpoint avec exemples, tâches, erreurs, limites de requêtes, déploiement GitHub Actions.

L’API de Vimonto Deploy permet aux scripts et aux pipelines CI de travailler avec vos organisations en HTTPS et JSON : lister les serveurs et les sites, lancer un déploiement et le suivre, créer et supprimer des bases de données, exécuter des sauvegardes et des recettes. Chaque requête est authentifiée par un jeton d’API personnel que vous créez dans votre compte.

Un jeton agit en votre nom. Il ne peut faire que ce que votre [rôle](https://ops.vimonto.com/docs/fr/organization/members-and-roles) permet, uniquement sur les serveurs auxquels vous avez accès, et uniquement dans les portées que vous lui avez données.

![La page des jetons d’API avec une liste de jetons et leurs portées](https://ops.vimonto.com/docs-media/fr/account-api-tokens.webp?v=161e760d "Les jetons d’API de votre compte")

## Créer un jeton d’API

1. Ouvrez le menu du compte en haut à droite et choisissez **Paramètres du compte** → **Jetons d’API**.
2. Cliquez sur **Nouveau jeton**.
3. Saisissez un **Nom** qui indique où il est utilisé, par exemple « GitHub Actions ».
4. Sous **Portées**, cochez ce que le jeton peut faire (voir ci-dessous).
5. Sous **Expire**, choisissez **Dans 30 jours**, **Dans 90 jours** (par défaut), **Dans 365 jours** ou **Jamais**.
6. Cliquez sur **Créer le jeton**.

Le jeton n'est affiché qu'une fois, sous **Ton nouveau jeton**. Cliquez sur **Copier** et conservez-le en lieu sûr, par exemple dans les secrets de votre CI ; Vimonto Deploy n'en garde qu'une empreinte (hash) et ne peut plus l'afficher. Un jeton ressemble à `12|` suivi d'une longue suite de lettres et de chiffres.

La liste affiche pour chaque jeton son nom, sa dernière utilisation, sa date d'expiration et ses portées. Cliquez sur **Révoquer** pour supprimer un jeton : les scripts et pipelines qui l'utilisent cessent aussitôt de fonctionner. Supprimer votre compte révoque tous vos jetons.

### Portées

| Portée | Valeur | Permet |
|---|---|---|
| **Lecture** | `read` | Voir les serveurs, sites, déploiements, bases de données, sauvegardes, recettes et tâches. |
| **Déployer** | `deploy` | Lancer des déploiements et les suivre : trouver un site par domaine ou identifiant, lister et voir les déploiements et voir les tâches. Pensé pour les pipelines CI. |
| **Écriture** | `write` | Tout ce que permet **Lecture**, plus lancer des déploiements, créer et supprimer des bases de données, et exécuter des sauvegardes et des recettes. |

Un jeton avec seulement **Déployer** peut retrouver le site qu'il déploie (voir [trouver un site](#trouver-un-site)), mais ne peut pas lister les serveurs ni les sites : donnez à un pipeline CI uniquement cette portée, et utilisez **Lecture** ou **Écriture** pour les scripts qui ont besoin de plus.

La portée est le premier contrôle. Votre rôle est le second : lancer un déploiement, modifier des bases de données et exécuter des sauvegardes ou des recettes nécessite le rôle de propriétaire, d'administrateur, de manager ou de développeur. Le jeton d'un lecteur peut lire, mais chaque modification est refusée avec `403`.

## URL de base et authentification

Tous les endpoints se trouvent sous `/api/v1` à l'adresse où vous utilisez Vimonto Deploy. Dans les exemples ci-dessous, c'est `https://deploy.example.com/api/v1` ; remplacez `deploy.example.com` par votre propre adresse.

Envoyez le jeton comme jeton bearer dans l'en-tête `Authorization`, et demandez du JSON :

```bash
curl https://deploy.example.com/api/v1/user \
  -H "Authorization: Bearer $VIMONTO_TOKEN" \
  -H "Accept: application/json"
```

Seuls les jetons ouvrent l’API : être connecté à l'application dans votre navigateur ne suffit pas. Les corps de requête sont en JSON (`Content-Type: application/json`). Les dates sont au format ISO 8601 avec un fuseau horaire. Les messages des réponses, comme les erreurs, sont dans la langue choisie dans votre compte ; si vous n'en avez pas choisi, dans la langue de l'en-tête `Accept-Language` de la requête (anglais, néerlandais, allemand, français ou italien), sinon en anglais.

### Organisations, serveurs et sites dans l'URL

Les endpoints d'organisation commencent par `/orgs/{organization}`, où `{organization}` est le slug de l'organisation : la première partie de son adresse dans l'application (`https://deploy.example.com/acme/…` a le slug `acme`). `GET /user` liste les slugs que votre jeton peut atteindre.

Les serveurs, les sites et tout ce qui se trouve en dessous sont désignés par leur identifiant numérique, le même nombre que dans l'adresse de l'application : `/acme/servers/12/sites/34` dans l'application devient `/orgs/acme/servers/12/sites/34` dans l’API. Un élément enfant doit appartenir à son parent : le site 34 sur le serveur 12 ne fonctionne que si le site est sur ce serveur, sinon l’API répond `404`.

Une organisation dont vous n'êtes pas membre, et un serveur auquel vos [équipes](https://ops.vimonto.com/docs/fr/organization/teams) ne vous donnent pas accès, répondent `404`, pas `403`.

## Endpoints

| Méthode | Chemin | Portées |
|---|---|---|
| `GET` | `/user` | toutes |
| `GET` | `/orgs/{organization}/servers` | read, write |
| `GET` | `/orgs/{organization}/servers/{server}` | read, write |
| `GET` | `/orgs/{organization}/servers/{server}/sites` | read, write |
| `GET` | `/orgs/{organization}/servers/{server}/sites/{site}` | read, write |
| `GET` | `/orgs/{organization}/sites` | read, deploy, write |
| `GET` | `/orgs/{organization}/servers/{server}/sites/{site}/deployments` | read, deploy, write |
| `GET` | `/orgs/{organization}/servers/{server}/sites/{site}/deployments/{deployment}` | read, deploy, write |
| `POST` | `/orgs/{organization}/servers/{server}/sites/{site}/deployments` | deploy, write |
| `GET` | `/orgs/{organization}/servers/{server}/databases` | read, write |
| `POST` | `/orgs/{organization}/servers/{server}/databases` | write |
| `DELETE` | `/orgs/{organization}/servers/{server}/databases/{database}` | write |
| `GET` | `/orgs/{organization}/servers/{server}/backups` | read, write |
| `POST` | `/orgs/{organization}/servers/{server}/backups/{backup}/run` | write |
| `GET` | `/orgs/{organization}/recipes` | read, write |
| `POST` | `/orgs/{organization}/recipes/{recipe}/run` | write |
| `GET` | `/orgs/{organization}/tasks/{task}` | read, deploy, write |

Un jeton a besoin de l'une des portées indiquées. Les chemins ci-dessous omettent le préfixe `https://deploy.example.com/api/v1`.

### Obtenir l'utilisateur du jeton

`GET /user` renvoie à qui appartient le jeton, le nom et les portées du jeton, et les organisations qu'il peut atteindre avec votre rôle dans chacune. Toutes les portées peuvent l'appeler : c'est un bon premier test pour un jeton.

```bash
curl https://deploy.example.com/api/v1/user \
  -H "Authorization: Bearer $VIMONTO_TOKEN" -H "Accept: application/json"
```

```json
{
  "data": {
    "id": 7,
    "name": "Sam de Vries",
    "email": "sam@example.com",
    "token": {
      "name": "GitHub Actions",
      "scopes": ["deploy"]
    },
    "organizations": [
      { "slug": "acme", "name": "Acme", "role": "developer" }
    ]
  }
}
```

### Lister les serveurs

`GET /orgs/{organization}/servers` renvoie les serveurs de l'organisation auxquels vous avez accès, triés par nom. `GET /orgs/{organization}/servers/{server}` renvoie un seul serveur.

```bash
curl https://deploy.example.com/api/v1/orgs/acme/servers \
  -H "Authorization: Bearer $VIMONTO_TOKEN" -H "Accept: application/json"
```

```json
{
  "data": [
    {
      "id": 12,
      "name": "web-1",
      "type": "app",
      "status": "active",
      "provider": "hetzner",
      "region": "fsn1",
      "size": "cx32",
      "ip_address": "203.0.113.10",
      "private_ip_address": "10.0.0.2",
      "ssh_port": 22,
      "user": "vimonto",
      "php_version": "8.4",
      "database": "mysql-8.4",
      "ubuntu_version": "24.04",
      "timezone": "UTC",
      "tags": ["production"],
      "created_at": "2026-09-01T09:30:00+00:00"
    }
  ]
}
```

| Champ | Valeurs |
|---|---|
| `type` | `app`, `web`, `worker`, `database`, `cache`, `meilisearch`, `loadbalancer` |
| `status` | `creating`, `waiting`, `provisioning`, `active`, `failed`, `disconnected`, `deleting` |
| `provider` | Le [fournisseur cloud](https://ops.vimonto.com/docs/fr/connections/server-providers), ou `custom` pour un [VPS personnalisé](https://ops.vimonto.com/docs/fr/servers/custom-vps). |
| `database` | `mysql-8.4`, `mysql-8.0`, `mariadb-11.4`, `mariadb-10.11`, `postgres-18`, `postgres-17`, `postgres-16`, ou `null` |

Les secrets tels que les mots de passe et les clés ne font jamais partie d'une réponse.

### Lister les sites

`GET /orgs/{organization}/servers/{server}/sites` renvoie les sites du serveur, triés par domaine. `GET /orgs/{organization}/servers/{server}/sites/{site}` renvoie un seul site.

```json
{
  "data": {
    "id": 34,
    "server_id": 12,
    "domain": "shop.example.com",
    "aliases": ["www.shop.example.com"],
    "preview_domain": "kalme-rivier-4821.on-deploy.link",
    "framework": "laravel",
    "status": "installed",
    "php_version": "8.4",
    "repository": "acme/shop",
    "branch": "main",
    "quick_deploy": true,
    "zero_downtime": true,
    "isolated": false,
    "current_release": "20261007143012",
    "deployed_at": "2026-10-07T14:31:40+00:00",
    "created_at": "2026-09-01T10:02:11+00:00"
  }
}
```

`framework` vaut `laravel`, `symfony`, `statamic`, `wordpress`, `phpmyadmin`, `php`, `nextjs`, `nuxt`, `html`, `other` ou `loadbalancer`. `status` vaut `installed` quand le site est prêt ; pendant une modification, il vaut `installing`, `updating` ou `removing`, et `failed` en cas d'échec. `quick_deploy` correspond au déploiement à chaque push ; `isolated` indique si le site tourne sous son propre utilisateur Linux. Le fichier d'environnement du site et son URL de déploiement ne sont jamais renvoyés.

### Trouver un site

`GET /orgs/{organization}/sites` renvoie les sites de tous les serveurs auxquels vous avez accès, triés par domaine, chacun avec son `server` (identifiant et nom). Les autres champs sont les mêmes que ci-dessus. Affinez la liste avec des paramètres de requête :

| Paramètre | Correspond à |
|---|---|
| `domain` | Le domaine du site, sans tenir compte de la casse. |
| `id` | L'identifiant du site. |
| `server` | L'identifiant ou le nom du serveur. |

```bash
curl "https://deploy.example.com/api/v1/orgs/acme/sites?domain=shop.example.com" \
  -H "Authorization: Bearer $VIMONTO_TOKEN" -H "Accept: application/json"
```

```json
{
  "data": [
    {
      "id": 34,
      "server_id": 12,
      "server": { "id": 12, "name": "web-1" },
      "domain": "shop.example.com",
      "status": "installed",
      "branch": "main"
    }
  ]
}
```

Un jeton avec uniquement la portée **Déployer** peut aussi l'appeler, pour trouver le site qu'il déploie, mais doit alors passer `domain` ou `id` ; sans l'un des deux, la réponse est `422`. Si rien ne correspond, `data` est une liste vide.

### Lister les déploiements

`GET /orgs/{organization}/servers/{server}/sites/{site}/deployments` renvoie les déploiements du site, les plus récents en premier, 25 par page (voir [pagination](#pagination)). `GET …/deployments/{deployment}` en renvoie un seul.

```json
{
  "data": {
    "id": 581,
    "site_id": 34,
    "status": "succeeded",
    "trigger": "api",
    "branch": "main",
    "commit": {
      "hash": "9f2c4e1a7b3d5f60812a9c4e7d1b3a5c7e9f1a2b",
      "author": "Sam de Vries",
      "message": "Add checkout page"
    },
    "release": "20261007143012",
    "task_id": 9120,
    "started_at": "2026-10-07T14:30:12+00:00",
    "finished_at": "2026-10-07T14:31:40+00:00",
    "created_at": "2026-10-07T14:30:11+00:00"
  }
}
```

| Champ | Valeurs |
|---|---|
| `status` | `queued`, `running`, `succeeded`, `failed`, `cancelled` |
| `trigger` | `manual`, `push`, `url` (l'URL de déploiement), `rollback`, `api` |
| `commit` | `null` tant que le code n'a pas été récupéré. |
| `release` | Le répertoire de la release, ou `live` pour un site sans déploiement sans interruption ; `null` tant que le déploiement n'a pas commencé. |

### Lancer un déploiement

`POST /orgs/{organization}/servers/{server}/sites/{site}/deployments` déploie la branche du site, exactement comme **Déployer maintenant** dans l'application. Il ne prend pas de corps. Il répond `202 Accepted` avec le nouveau déploiement ; suivez-le avec son `task_id`.

```bash
curl -X POST https://deploy.example.com/api/v1/orgs/acme/servers/12/sites/34/deployments \
  -H "Authorization: Bearer $VIMONTO_TOKEN" -H "Accept: application/json"
```

```json
{
  "data": {
    "id": 582,
    "site_id": 34,
    "status": "queued",
    "trigger": "api",
    "branch": "main",
    "commit": null,
    "release": null,
    "task_id": 9121,
    "started_at": null,
    "finished_at": null,
    "created_at": "2026-10-07T15:02:45+00:00"
  }
}
```

- `409` quand un déploiement du site est déjà en cours ; le `task_id` de la réponse est la tâche en cours.
- `422` quand le site n'est pas encore prêt à être déployé ou n'a pas de dépôt.

Consultez [les déploiements](https://ops.vimonto.com/docs/fr/sites/deployments) pour savoir ce que fait un déploiement.

### Lister et créer des bases de données

`GET /orgs/{organization}/servers/{server}/databases` renvoie les bases de données du serveur, triées par nom.

```json
{
  "data": [
    { "id": 3, "server_id": 12, "name": "shop", "status": "installed", "created_at": "2026-09-01T10:05:00+00:00" }
  ]
}
```

`POST /orgs/{organization}/servers/{server}/databases` crée une base de données sur le serveur. Le corps a un seul champ :

| Champ | Règles |
|---|---|
| `name` | Obligatoire. 1 à 63 lettres, chiffres et tirets bas, unique sur le serveur. |

```bash
curl -X POST https://deploy.example.com/api/v1/orgs/acme/servers/12/databases \
  -H "Authorization: Bearer $VIMONTO_TOKEN" -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"name": "shop_staging"}'
```

```json
{
  "data": { "id": 4, "server_id": 12, "name": "shop_staging", "status": "installing", "created_at": "2026-10-07T15:10:00+00:00", "task_id": 9122 }
}
```

La réponse est `202` : la base de données est créée par une tâche sur le serveur. Son `status` vaut `installing`, puis `installed`, ou `failed` si la tâche échoue. Le serveur doit être actif et avoir une base de données installée, sinon la réponse est `422`.

### Supprimer une base de données

`DELETE /orgs/{organization}/servers/{server}/databases/{database}` supprime la base de données sur le serveur. Il répond `202` avec la base de données, désormais `removing`, et la tâche qui la retire dans `task_id`, à l'intérieur de `data` comme pour chaque endpoint qui lance une tâche :

```json
{
  "data": { "id": 4, "server_id": 12, "name": "shop_staging", "status": "removing", "created_at": "2026-10-07T15:10:00+00:00", "task_id": 9123 }
}
```

> [!WARNING]
> Cela supprime la base de données et toutes ses données sur le serveur. Impossible de revenir en arrière.

Consultez [les bases de données](https://ops.vimonto.com/docs/fr/servers/databases).

### Lister les sauvegardes et en lancer une

`GET /orgs/{organization}/servers/{server}/backups` renvoie les configurations de sauvegarde du serveur, triées par nom, chacune avec ses 20 dernières sauvegardes. `size` est en octets.

```json
{
  "data": [
    {
      "id": 2,
      "name": "Nightly",
      "databases": ["shop"],
      "frequency": "nightly",
      "schedule": "0 0 * * *",
      "retention": 7,
      "storage": "Backups bucket",
      "backups": [
        {
          "id": 140,
          "status": "succeeded",
          "size": 48213504,
          "databases": ["shop"],
          "task_id": 9050,
          "created_at": "2026-10-07T00:00:02+00:00",
          "finished_at": "2026-10-07T00:01:15+00:00"
        }
      ]
    }
  ]
}
```

`frequency` vaut `minute`, `hourly`, `nightly`, `weekly`, `monthly`, `reboot` ou `custom` ; `schedule` est l'expression cron. Le `status` d'une sauvegarde vaut `running`, `succeeded` ou `failed`.

`POST /orgs/{organization}/servers/{server}/backups/{backup}/run`, avec l'identifiant d'une configuration de sauvegarde, lance une sauvegarde immédiatement. Il ne prend pas de corps et répond `202` :

```json
{ "data": { "backup_id": 141, "task_id": 9124 } }
```

`409` quand une sauvegarde de cette configuration est déjà en cours. Consultez [les sauvegardes](https://ops.vimonto.com/docs/fr/servers/backups).

### Lister les recettes et en exécuter une

`GET /orgs/{organization}/recipes` renvoie les recettes de l'organisation, triées par nom. `run_as` vaut `root` ou `server_user`.

```json
{
  "data": [
    { "id": 5, "name": "Install htop", "run_as": "root", "script": "apt-get install -y htop", "updated_at": "2026-09-20T08:12:00+00:00" }
  ]
}
```

`POST /orgs/{organization}/recipes/{recipe}/run` exécute une recette sur un ou plusieurs serveurs.

| Champ | Règles |
|---|---|
| `servers` | Obligatoire. Une liste d'identifiants de serveurs, au moins un. |
| `notify` | Facultatif, `true` ou `false` (par défaut). `true` vous envoie un rapport par e-mail quand tous les serveurs ont terminé. |

```bash
curl -X POST https://deploy.example.com/api/v1/orgs/acme/recipes/5/run \
  -H "Authorization: Bearer $VIMONTO_TOKEN" -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"servers": [12, 13], "notify": false}'
```

```json
{
  "data": {
    "run_id": 77,
    "servers": [
      { "server_id": 12, "task_id": 9125 },
      { "server_id": 13, "task_id": 9126 }
    ]
  }
}
```

Chaque serveur indiqué doit exister, vous être accessible et être actif. Sinon, la recette ne s'exécute nulle part et la réponse est `422` : `errors.servers` liste les identifiants des serveurs qui n'existent pas ou auxquels vous n'avez pas accès, ou nomme les serveurs qui ne sont pas actifs. La réponse liste chaque serveur avec sa propre tâche. Consultez [les recettes](https://ops.vimonto.com/docs/fr/more/recipes).

### Suivre une tâche

Les travaux longs (un déploiement, une base de données, une sauvegarde, une recette sur un serveur) s'exécutent comme une tâche en arrière-plan, la même que celle que vous voyez dans l'[activité](https://ops.vimonto.com/docs/fr/organization/activity). `GET /orgs/{organization}/tasks/{task}` renvoie son état et sa sortie :

```json
{
  "data": {
    "id": 9121,
    "type": "site.deploy",
    "name": "Deploying to shop.example.com",
    "status": "running",
    "step": "Run deploy script",
    "progress": 30,
    "error": null,
    "server_id": 12,
    "output": "Cloning into 'releases/20261007150246'...\n",
    "started_at": "2026-10-07T15:02:46+00:00",
    "finished_at": null,
    "created_at": "2026-10-07T15:02:45+00:00"
  }
}
```

`status` vaut `queued`, `running`, `succeeded`, `failed` ou `cancelled` ; les trois derniers sont définitifs. `progress` va de 0 à 100. Quand une tâche échoue, `error` en donne la raison. Interrogez la tâche toutes les quelques secondes jusqu'à ce que le statut soit définitif.

## Pagination

Seule la liste des déploiements est paginée, avec 25 déploiements par page, les plus récents en premier. Demandez une autre page avec `?page=2`. La réponse contient `links` et `meta` à côté de `data` :

```json
{
  "data": [ … ],
  "links": {
    "first": "https://deploy.example.com/api/v1/orgs/acme/servers/12/sites/34/deployments?page=1",
    "last": "https://deploy.example.com/api/v1/orgs/acme/servers/12/sites/34/deployments?page=4",
    "prev": null,
    "next": "https://deploy.example.com/api/v1/orgs/acme/servers/12/sites/34/deployments?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 4,
    "path": "https://deploy.example.com/api/v1/orgs/acme/servers/12/sites/34/deployments",
    "per_page": 25,
    "to": 25,
    "total": 92
  }
}
```

`meta` contient aussi une liste `links` pour des boutons de page. Suivez `links.next` jusqu'à ce qu'il vaille `null`. Toutes les autres listes renvoient tout en une seule réponse.

## Erreurs

Les erreurs sont du JSON avec un `message` :

```json
{ "message": "shop.example.com is not ready to deploy yet." }
```

| Statut | Signification |
|---|---|
| `401` | Pas de jeton, un jeton erroné ou révoqué, ou un jeton expiré : `{"message": "Unauthenticated."}` |
| `403` | Le jeton n'a pas la portée (`"Invalid ability provided."`), ou votre rôle ne permet pas la modification (`"Your role does not allow this."`). |
| `404` | L'organisation, le serveur, le site ou un autre élément n'existe pas, ou vous n'y avez pas accès. Ne vous fiez pas au `message`. |
| `409` | Le même travail est déjà en cours. La réponse contient le `task_id` de la tâche en cours. |
| `422` | La requête ne peut pas être traitée : champs invalides, ou une raison comme un serveur pas encore prêt. |
| `429` | Trop de requêtes ; voir [limites de requêtes](#limites-de-requetes). |

Un `409` ressemble à ceci :

```json
{ "message": "Deploying to shop.example.com is already running.", "task_id": 9121 }
```

Un `422` pour des champs invalides les liste sous `errors` :

```json
{
  "message": "The name field format is invalid.",
  "errors": {
    "name": ["The name field format is invalid."]
  }
}
```

## Limites de requêtes

Chaque jeton peut faire 120 requêtes par minute. Chaque réponse contient les en-têtes `X-RateLimit-Limit` et `X-RateLimit-Remaining`. Au-delà de la limite, l’API répond `429` avec un en-tête `Retry-After` : le nombre de secondes à attendre.

Quand vous suivez une tâche, une requête toutes les quelques secondes suffit largement.

## Déployer depuis GitHub Actions

Ce workflow déploie une fois les tests réussis et échoue quand le déploiement échoue. Créez un jeton avec uniquement la portée **Déployer** et ajoutez-le aux secrets du dépôt sous le nom `VIMONTO_TOKEN` (**Settings** → **Secrets and variables** → **Actions** sur GitHub). Renseignez votre propre adresse, le slug de l'organisation, l'identifiant du serveur et celui du site.

```yaml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    # needs: tests   # ne déployer qu'après la réussite de votre job de tests
    steps:
      - name: Deploy to production
        env:
          VIMONTO_TOKEN: ${{ secrets.VIMONTO_TOKEN }}
          API: https://deploy.example.com/api/v1/orgs/acme
          SERVER: 12
          SITE: 34
        run: |
          set -euo pipefail
          auth=(-H "Authorization: Bearer $VIMONTO_TOKEN" -H "Accept: application/json")

          # Lancer le déploiement ; curl échoue sur 4xx/5xx, par exemple 409 si un déploiement est déjà en cours.
          task=$(curl -sS --fail-with-body -X POST "${auth[@]}" \
            "$API/servers/$SERVER/sites/$SITE/deployments" | jq -r '.data.task_id')
          echo "Deploy started, task $task"

          # Suivre la tâche jusqu'à la fin.
          while true; do
            sleep 5
            response=$(curl -sS --fail-with-body "${auth[@]}" "$API/tasks/$task")
            status=$(echo "$response" | jq -r '.data.status')
            echo "Status: $status ($(echo "$response" | jq -r '.data.step // ""'))"
            case "$status" in
              succeeded) exit 0 ;;
              failed|cancelled)
                echo "$response" | jq -r '.data.error // "", .data.output'
                exit 1 ;;
            esac
          done
```

Désactivez **Déployer à chaque push** pour le site quand la CI le déploie. Sinon, un push lance déjà un déploiement de lui-même, et la requête du pipeline reçoit alors `409` parce qu'un déploiement est déjà en cours.

### Ou utilisez la CLI

La [CLI de Vimonto Deploy](https://ops.vimonto.com/docs/fr/more/cli) regroupe ces requêtes en une seule commande : `deploy deploy shop.example.com --watch` lance le déploiement, suit la tâche et se termine avec son résultat. En CI, elle lit le jeton dans `DEPLOY_TOKEN`.

### Ou utilisez l'URL de déploiement

Quand vous avez seulement besoin de lancer un déploiement, l'**URL de déploiement** secrète du site est plus simple : un seul `POST` sans jeton, et rien à suivre. Elle n'indique pas à votre pipeline si le déploiement a réussi. Consultez [les déploiements](https://ops.vimonto.com/docs/fr/sites/deployments).

## Questions fréquentes

### Un jeton est-il lié à une seule organisation ?

Non. Un jeton atteint toutes les organisations dont vous êtes membre, avec votre rôle dans chacune. `GET /user` les liste. Créez un compte distinct si un pipeline ne doit atteindre qu'une seule organisation.

### Que deviennent mes jetons quand mon rôle change ?

Un jeton suit toujours votre rôle et votre accès aux serveurs actuels. Quand vous perdez une autorisation ou quittez une organisation, le jeton la perd aussi, immédiatement.

### Puis-je créer des serveurs ou des sites via l’API ?

Pas encore. L’API permet de lire les serveurs, sites, déploiements, bases de données, sauvegardes, recettes et tâches ; de déployer ; de créer et supprimer des bases de données ; et d'exécuter des sauvegardes et des recettes.

### Où trouver l'identifiant du serveur et du site ?

Dans la barre d'adresse de l'application : sur les pages d'un site, l'adresse est `/{organization}/servers/{server}/sites/{site}/…`. `GET /orgs/{organization}/servers` et `GET …/servers/{server}/sites` les listent aussi, et `GET /orgs/{organization}/sites?domain=shop.example.com` trouve un site et l'identifiant de son serveur à partir de son domaine.
