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

# Webhooks

> Recevoir les résultats en temps réel dès qu'une tâche est validée.

## Qu'est-ce qu'un webhook ?

Plutôt que de poller `/tasks/:id` toutes les N secondes, vous configurez une URL sur votre serveur. DLoopIQ envoie une requête HTTP POST dès qu'un événement se produit (tâche validée, tâche échouée, etc.).

***

## Configurer un webhook

```bash theme={null}
curl -X POST https://dloopiq.onrender.com/api/v1/webhooks/configs \
  -H "Authorization: Bearer <jwt_acheteur>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://votre-serveur.com/webhook/dloopiq",
    "events": ["task.completed", "task.failed"],
    "secret": "votre_secret_pour_verifier_la_signature"
  }'
```

Réponse :

```json theme={null}
{
  "id": "cmpkb4ghw00016qduu44p7n2q",
  "url": "https://votre-serveur.com/webhook/dloopiq",
  "events": ["task.completed", "task.failed"],
  "isActive": true,
  "createdAt": "2026-05-25T10:00:00Z"
}
```

***

## Événements disponibles

| Événement        | Déclencheur                             |
| ---------------- | --------------------------------------- |
| `task.completed` | Consensus atteint — résultat disponible |
| `task.failed`    | Pas assez de réponses concordantes      |
| `budget.low`     | Solde crédits \< 500¢ (\$5)             |

***

## Format du payload

### `task.completed`

```json theme={null}
{
  "event": "task.completed",
  "taskId": "cmpkb4ghw00016qduu44p7n2q",
  "type": "SENTIMENT_ANALYSIS",
  "result": "Positif",
  "confidence": 0.87,
  "totalResponses": 3,
  "costCents": 20,
  "validatedAt": "2026-05-25T10:05:00Z"
}
```

### `task.failed`

```json theme={null}
{
  "event": "task.failed",
  "taskId": "cmpkb4ghw00016qduu44p7n2r",
  "type": "BINARY_CHOICE",
  "reason": "NO_CONSENSUS",
  "totalResponses": 3,
  "failedAt": "2026-05-25T10:10:00Z"
}
```

***

## Vérifier la signature

Chaque requête webhook inclut un header `x-dloopiq-signature` avec un HMAC-SHA256 calculé sur le corps de la requête.

```
x-dloopiq-signature: sha256=abc123def456...
```

Voir le guide complet → [Vérification des signatures](/guides/webhooks-signing)

***

## Gérer les webhooks

### Lister vos webhooks

```bash theme={null}
curl https://dloopiq.onrender.com/api/v1/webhooks/configs \
  -H "Authorization: Bearer <jwt_acheteur>"
```

### Mettre à jour

```bash theme={null}
curl -X PATCH https://dloopiq.onrender.com/api/v1/webhooks/configs/:id \
  -H "Authorization: Bearer <jwt_acheteur>" \
  -H "Content-Type: application/json" \
  -d '{ "isActive": false }'
```

### Supprimer

```bash theme={null}
curl -X DELETE https://dloopiq.onrender.com/api/v1/webhooks/configs/:id \
  -H "Authorization: Bearer <jwt_acheteur>"
```

***

## Réponse attendue

Votre endpoint doit répondre avec un **HTTP 2xx** dans les **10 secondes**. En cas d'échec (timeout ou 5xx), DLoopIQ effectue **1 retry** après 30 secondes.

```typescript theme={null}
// Express — endpoint webhook minimal
app.post('/webhook/dloopiq', express.raw({ type: 'application/json' }), (req, res) => {
  // 1. Vérifier la signature (voir guide)
  const isValid = verifySignature(req.body, req.headers['x-dloopiq-signature'], SECRET)
  if (!isValid) return res.status(401).send('Invalid signature')

  // 2. Traiter l'événement
  const event = JSON.parse(req.body.toString())

  if (event.event === 'task.completed') {
    console.log(`Tâche ${event.taskId} validée : ${event.result} (${event.confidence})`)
    // → Stocker en DB, notifier votre équipe, déclencher fine-tuning, etc.
  }

  // 3. Répondre rapidement
  res.status(200).json({ received: true })
})
```

<Warning>
  Traitez le webhook de manière **asynchrone** — répondez 200 immédiatement, puis traitez en arrière-plan. Un traitement > 10s entraîne un retry et des doublons.
</Warning>
