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

# Webhooks

> Configuration des webhooks pour recevoir des notifications en temps réel sur les événements de recouvrement.

<Info>**Tester en direct** — Essayez ces endpoints dans la [documentation interactive Scalar](https://api.finkare.io/docs).</Info>

## Lister les webhooks

**Scope requis :** `webhooks:manage`

```bash theme={null}
curl -H "X-API-Key: fk_live_xxx" \
  https://api.finkare.io/api/v1/webhooks
```

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": "e5f6a7b8-c1d2-9012-ef01-345678901bcd",
      "url": "https://api.acme.fr/webhooks/finkare",
      "events": ["payment.received", "invoice.status_changed"],
      "description": "Webhook production ACME",
      "enabled": true,
      "created_at": "2026-03-01T10:00:00Z",
      "last_triggered_at": "2026-04-07T18:30:00Z"
    }
  ],
  "requestId": "req_wh_list_01",
  "timestamp": "2026-04-08T10:00:00Z"
}
```

***

## Créer un webhook

**Scope requis :** `webhooks:manage`

<ParamField body="url" type="string" required>
  URL de destination (HTTPS obligatoire)
</ParamField>

<ParamField body="events" type="array" required>
  Événements à écouter (voir tableau ci-dessous)
</ParamField>

<ParamField body="description" type="string" optional>
  Description du webhook
</ParamField>

<ParamField body="enabled" type="boolean" optional default="true">
  Activer ou désactiver le webhook
</ParamField>

```bash theme={null}
curl -X POST https://api.finkare.io/api/v1/webhooks \
  -H "X-API-Key: fk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.votre-entreprise.fr/webhooks/finkare",
    "events": [
      "payment.received",
      "invoice.status_changed",
      "workflow.action_triggered"
    ],
    "description": "Webhook principal production"
  }'
```

```json theme={null}
{
  "success": true,
  "data": {
    "id": "e5f6a7b8-c1d2-9012-ef01-345678901bcd",
    "url": "https://api.votre-entreprise.fr/webhooks/finkare",
    "events": ["payment.received", "invoice.status_changed", "workflow.action_triggered"],
    "enabled": true,
    "secret": "whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6",
    "createdAt": "2026-04-08T10:00:00Z"
  },
  "message": "Webhook created. Save the secret - it will not be shown again.",
  "requestId": "req_wh_create_01",
  "timestamp": "2026-04-08T10:00:00Z"
}
```

<Warning>
  Le **secret de signature** (`whsec_...`) est retourné **une seule fois**. Sauvegardez-le immédiatement. Il est utilisé pour vérifier l'authenticité des payloads via HMAC-SHA256.
</Warning>

***

## Événements disponibles

| Événement                   | Description                                              |
| --------------------------- | -------------------------------------------------------- |
| `invoice.created`           | Nouvelle facture créée ou importée                       |
| `invoice.updated`           | Facture mise à jour (metadata/notes)                     |
| `invoice.status_changed`    | Changement de statut d'une facture                       |
| `payment.received`          | Paiement reçu (total ou partiel)                         |
| `payment.failed`            | Échec de paiement                                        |
| `workflow.started`          | Workflow de recouvrement démarré                         |
| `workflow.action_triggered` | Action de recouvrement déclenchée (email, SMS, appel...) |
| `workflow.completed`        | Workflow terminé (payé ou clôturé)                       |
| `lrar.sent`                 | LRAR envoyée                                             |
| `lrar.delivered`            | LRAR distribuée (AR reçu)                                |
| `debtor.contacted`          | Débiteur contacté (email, SMS, appel)                    |
| `debt.paid`                 | Dossier marqué comme payé (preuve validée)               |
| `cascade.action`            | Action de cascade exécutée par Bob (pause, accélération) |
| `judicial.started`          | Procédure judiciaire initiée                             |
| `anr.created`               | Avis de Non Recouvrement généré                          |

***

## Récupérer un webhook

**Scope requis :** `webhooks:manage`

```bash theme={null}
curl -H "X-API-Key: fk_live_xxx" \
  https://api.finkare.io/api/v1/webhooks/e5f6a7b8-c1d2-9012-ef01-345678901bcd
```

***

## Mettre à jour un webhook

**Scope requis :** `webhooks:manage`

```bash theme={null}
curl -X PUT https://api.finkare.io/api/v1/webhooks/e5f6a7b8-c1d2-9012-ef01-345678901bcd \
  -H "X-API-Key: fk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["payment.received", "invoice.status_changed", "debt.paid"],
    "description": "Webhook v2 — ajout événement debt.paid"
  }'
```

***

## Supprimer un webhook

**Scope requis :** `webhooks:manage`

```bash theme={null}
curl -X DELETE https://api.finkare.io/api/v1/webhooks/e5f6a7b8-c1d2-9012-ef01-345678901bcd \
  -H "X-API-Key: fk_live_xxx"
```

Retourne `204 No Content`.

***

## Régénérer le secret

Génère un nouveau secret et invalide l'ancien.

**Scope requis :** `webhooks:manage`

```bash theme={null}
curl -X POST https://api.finkare.io/api/v1/webhooks/e5f6a7b8-c1d2-9012-ef01-345678901bcd/rotate-secret \
  -H "X-API-Key: fk_live_xxx"
```

```json theme={null}
{
  "success": true,
  "data": {
    "secret": "whsec_nouveau_secret_genere_ici_abc123def456"
  },
  "message": "Secret rotated. Save the new secret - it will not be shown again."
}
```

***

## Historique des deliveries

Consultez les tentatives de livraison (succès et échecs) pour un webhook.

**Scope requis :** `webhooks:manage`

```bash theme={null}
curl -H "X-API-Key: fk_live_xxx" \
  https://api.finkare.io/api/v1/webhooks/e5f6a7b8-c1d2-9012-ef01-345678901bcd/deliveries
```

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": "del_001",
      "eventType": "payment.received",
      "status": "delivered",
      "attempts": 1,
      "responseStatus": 200,
      "lastAttemptAt": "2026-04-07T18:30:00Z",
      "createdAt": "2026-04-07T18:30:00Z"
    },
    {
      "id": "del_002",
      "eventType": "invoice.status_changed",
      "status": "failed",
      "attempts": 3,
      "responseStatus": 500,
      "lastAttemptAt": "2026-04-07T19:15:00Z",
      "nextRetryAt": "2026-04-07T19:45:00Z",
      "createdAt": "2026-04-07T19:00:00Z"
    }
  ],
  "pagination": { "total": 2, "limit": 20, "offset": 0 }
}
```

### Politique de retry

En cas d'échec de livraison (timeout ou code HTTP >= 400), Finkare retente automatiquement avec un **backoff exponentiel** :

| Tentative | Délai       |
| --------- | ----------- |
| 1         | Immédiat    |
| 2         | 30 secondes |
| 3         | 5 minutes   |
| 4         | 30 minutes  |
| 5         | 2 heures    |

Après 5 tentatives échouées, le webhook est automatiquement désactivé. Vous recevez une notification par email.

***

## Vérification de signature

Chaque payload est signé via **HMAC-SHA256**. Vérifiez la signature pour garantir l'authenticité :

<CodeGroup>
  ```typescript Node.js theme={null}
  import crypto from 'crypto';

  function verifyWebhookSignature(
    payload: string,
    signature: string,
    secret: string,
  ): boolean {
    const expected = crypto
      .createHmac('sha256', secret)
      .update(payload)
      .digest('hex');
    return crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expected),
    );
  }

  // Dans votre handler Express
  app.post('/webhooks/finkare', (req, res) => {
    const signature = req.headers['x-finkare-signature'] as string;
    const isValid = verifyWebhookSignature(
      JSON.stringify(req.body),
      signature,
      process.env.FINKARE_WEBHOOK_SECRET!,
    );

    if (!isValid) {
      return res.status(401).json({ error: 'Invalid signature' });
    }

    // Traiter l'événement
    const { event, data } = req.body;
    console.log(`Événement reçu : ${event}`, data);

    res.status(200).json({ received: true });
  });
  ```

  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
      expected = hmac.new(
          secret.encode(),
          payload,
          hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(signature, expected)
  ```
</CodeGroup>

<Tip>
  Utilisez toujours `timingSafeEqual` (ou équivalent) pour la comparaison des signatures afin d'éviter les attaques timing.
</Tip>
