> ## Documentation Index
> Fetch the complete documentation index at: https://gccai.heqingsong.uk/llms.txt
> Use this file to discover all available pages before exploring further.

# Rappel à la fin de la tâche (Webhook)

> Indiquez une URL de rappel lors de la soumission d'une tâche de génération asynchrone : nous vous enverrons le résultat par POST une fois la tâche terminée (succès ou échec), sans interrogation nécessaire.

Lors de la soumission de tâches de génération asynchrones telles que vidéo / image / audio, vous pouvez indiquer une URL de rappel. **Une fois la tâche terminée (réussie ou échouée)**, nous enverrons activement le résultat par POST à votre URL, pour vous éviter d'interroger en continu.

## Démarrage rapide

Lors de la soumission d'une tâche, ajoutez un champ `webhook` au corps de la requête :

```bash theme={null}
curl -X POST https://votre-domaine-d-acces/v1/images/generations \
  -H "Authorization: Bearer VOTRE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-2",
    "prompt": "a red apple on a table",
    "size": "1024x1024",
    "webhook": "https://your-server.com"
  }'
```

Une fois la tâche terminée, nous enverrons une requête POST à **`votre URL + /callback`**.

<Note>
  Les autres points de terminaison de tâches asynchrones (vidéo, audio, etc.) fonctionnent de la même manière : ajoutez simplement le champ `webhook` au corps de la requête.
</Note>

## Règles d'URL

Le `webhook` que vous fournissez est l'**URL de base**, à laquelle nous ajoutons automatiquement `/callback` :

| Votre `webhook`                | Où nous envoyons réellement le POST    |
| ------------------------------ | -------------------------------------- |
| `https://your-server.com`      | `https://your-server.com/callback`     |
| `https://your-server.com/api`  | `https://your-server.com/api/callback` |
| `https://your-server.com/api/` | `https://your-server.com/api/callback` |

Votre serveur doit donc disposer d'un point de terminaison acceptant `POST .../callback`.

## Ce que vous allez recevoir

Le contenu envoyé est **exactement identique à ce que renvoie le point de terminaison « [Obtenir le statut d'une tâche](/fr/api-reference/tasks/status) »** : vous pouvez le traiter avec la même logique d'analyse.

<CodeGroup>
  ```json Succès (status: completed) theme={null}
  {
    "id": "task_01KV7FXR8BEYS1BWHJCT3JMCJ5",
    "status": "completed",
    "progress": 100,
    "created": 1781589029,
    "completed": 1781589058,
    "actual_time": 29,
    "cost": 0.006,
    "credits_cost": 0.06,
    "result": {
      "images": [{ "url": ["https://.../result.png"], "expires_at": 1781675458 }]
    }
  }
  ```

  ```json Échec (status: failed) theme={null}
  {
    "id": "task_xxx",
    "status": "failed",
    "progress": 100,
    "created": 1781589029,
    "completed": 1781589050,
    "error": { "message": "raison de l'échec...", "code": "" }
  }
  ```
</CodeGroup>

<Note>
  Pour les tâches vidéo, le résultat se trouve dans `result.videos`, et pour l'audio dans `result.audios`.
</Note>

<Warning>
  Nous n'envoyons que lorsqu'une tâche atteint un **état terminal** (`completed` / `failed`) ; nous n'envoyons pas pendant le traitement.
</Warning>

## Nouvelles tentatives et déduplication (important)

* **Nouvelles tentatives** : Si votre serveur ne renvoie pas `2xx` en environ 10 secondes, ou renvoie `5xx`, nous réessaierons automatiquement, jusqu'à **3 fois**, à des intervalles d'environ **10 s, 30 s et 60 s**. Si les 3 échouent, nous abandonnons (en environ 2 minutes).
* **Pas de nouvelle tentative** : Si votre point de terminaison renvoie `4xx` (considéré comme une URL / requête incorrecte), nous abandonnons immédiatement sans réessayer.
* **Déduplication** : Normalement, une tâche n'est envoyée qu'une seule fois. Mais dans des cas extrêmes (p. ex. un redémarrage de notre côté après l'envoi mais avant la confirmation), vous **pourriez recevoir des envois en double**. Veillez à **dédupliquer de manière idempotente par `id` (task\_id)** pour éviter un double traitement.

**Recommandations pour votre point de terminaison de réception :**

<Steps>
  <Step title="Renvoyez 2xx le plus tôt possible">
    Acceptez et mettez en file d'attente d'abord, puis traitez de manière asynchrone : ne nous faites pas attendre la fin de votre traitement.
  </Step>

  <Step title="Dédupliquez par id">
    Utilisez `id` (task\_id) comme clé d'idempotence pour éviter un double traitement.
  </Step>

  <Step title="Configurez et vérifiez la signature">
    En production, vérifiez l'origine des requêtes de rappel et rejetez les requêtes falsifiées.
  </Step>
</Steps>

## Exigences pour l'URL de rappel

Pour des raisons de sécurité, l'URL de rappel doit respecter les conditions suivantes :

| Exigence                | Description                                                                                                 |
| ----------------------- | ----------------------------------------------------------------------------------------------------------- |
| Accessible publiquement | **Ne peut pas** être une adresse interne / locale (p. ex. `127.0.0.1`, `10.x`, `192.168.x` seront rejetées) |
| Protocole               | `http` ou `https` (`https` recommandé)                                                                      |
| Port                    | Utilisez des ports standard (`80` / `443`) ; les ports non standard peuvent être bloqués                    |
| Domaine                 | Ne peut pas pointer vers notre propre domaine de service                                                    |

Les URL qui ne respectent pas ces exigences sont rejetées (aucun envoi, aucune nouvelle tentative).

## FAQ

<AccordionGroup>
  <Accordion title="J'ai soumis une tâche avec un webhook mais je n'ai rien reçu ?">
    Vérifiez point par point :

    1. La tâche est-elle **réellement terminée** ? Consultez les détails de la tâche : `status` est-il `completed` / `failed` (aucun envoi pendant le traitement) ?
    2. Votre URL est-elle **accessible publiquement** ? Pouvons-nous atteindre votre `/callback` ?
    3. Le port est-il un port standard (80 / 443) ? Les ports non standard peuvent être bloqués par les politiques de sécurité.
    4. Votre `/callback` a-t-il **renvoyé 2xx à temps** ? En cas de 4xx, nous abandonnons immédiatement.
    5. Utilisez-vous `https` ? Le certificat est-il valide ?
  </Accordion>

  <Accordion title="Pourquoi url dans le résultat est-il un tableau ?">
    Certains modèles produisent plusieurs images à la fois, donc `images[].url` peut être un tableau : traitez-le simplement comme un tableau.
  </Accordion>

  <Accordion title="Les liens de résultat expirent-ils ?">
    Si `result` contient `expires_at` (horodatage Unix), cela indique l'heure d'expiration du lien : transférez-le/sauvegardez-le rapidement.
  </Accordion>

  <Accordion title="Envoyez-vous le statut « en cours de traitement » ?">
    Non. Nous n'envoyons qu'une seule fois, lorsque la tâche réussit ou échoue définitivement.
  </Accordion>
</AccordionGroup>

## Exemple minimal de récepteur

```python Python theme={null}
from http.server import BaseHTTPRequestHandler, HTTPServer
import json

class H(BaseHTTPRequestHandler):
    def do_POST(self):
        n = int(self.headers.get("Content-Length") or 0)
        body = self.rfile.read(n)
        data = json.loads(body)
        print("Rappel de tâche reçu :", data["id"], data["status"])
        # TODO : dédupliquer par id, vérifier la signature, puis traiter
        self.send_response(200); self.end_headers()
        self.wfile.write(b'{"ok":true}')

HTTPServer(("0.0.0.0", 443), H).serve_forever()
```

Renvoyez `200` le plus tôt possible et exécutez votre logique de traitement de manière asynchrone en arrière-plan.
