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

# Webhooks

> Recevez en temps réel les changements de statut de vos transactions, retraits et transferts

Toutes les routes nécessitent votre clé API secrète (`Authorization: Bearer sk_...`) ou une session dashboard. Un point de réception (`webhook`) appartient à votre marchand ; un abonnement (`subscription`) rattache ce point de réception à un type d'événement précis — un même webhook peut avoir plusieurs abonnements.

```
Votre marchand
│
└── Webhook (url, environment, signing_secret)
    ├── Abonnement → transaction.success
    ├── Abonnement → settlement.success
    └── Abonnement → wallet_transfer.completed
```

## Catalogue d'events

| Event                       | Déclenchement                                                         |
| --------------------------- | --------------------------------------------------------------------- |
| `transaction.created`       | Une transaction (payin ou payout) vient d'être initiée                |
| `transaction.success`       | Une transaction a été confirmée avec succès                           |
| `transaction.failed`        | Une transaction a échoué                                              |
| `transaction.cancelled`     | Une transaction a été annulée                                         |
| `settlement.requested`      | Un retrait a été demandé                                              |
| `settlement.approved`       | Un retrait a été approuvé par un administrateur                       |
| `settlement.success`        | Un retrait a été exécuté avec succès                                  |
| `settlement.failed`         | Un retrait a échoué                                                   |
| `settlement.cancelled`      | Un retrait a été annulé (par vous ou refusé par un administrateur)    |
| `wallet_transfer.requested` | Un transfert interwallet a été demandé                                |
| `wallet_transfer.completed` | Un transfert interwallet a été complété                               |
| `wallet_transfer.rejected`  | Un transfert interwallet a été rejeté                                 |
| `webhook.test`              | Event de test, déclenché manuellement pour vérifier votre intégration |

<Note>
  Les events `settlement.*` sont en cours de fiabilisation côté plateforme — le contenu de `data` n'est pas garanti pour ces events précis pour le moment. Le catalogue reste correct, mais évitez de dépendre de la forme exacte de leur payload tant que ce point n'est pas stabilisé.
</Note>

## Format de l'enveloppe

Chaque event envoyé à votre URL partage la même structure :

```json theme={null}
{
  "event": "transaction.success",
  "data": { }
}
```

### `data` selon l'event

```json theme={null}
// transaction.success
{
  "event": "transaction.success",
  "data": {
    "id": "7c1a...",
    "reference": "TXN-2026-000456",
    "type": "PAYIN",
    "status": "SUCCESS",
    "amount": "25000.00",
    "net_amount": "24375.00",
    "currency": "XOF"
  }
}

// wallet_transfer.completed
{
  "event": "wallet_transfer.completed",
  "data": {
    "id": "1a2b...",
    "reference": "f4e5d6c7...",
    "status": "COMPLETED",
    "from_country": "BJ",
    "from_amount": "100000.00",
    "from_currency": "XOF",
    "to_country": "CM",
    "to_amount": "984.00",
    "to_currency": "XAF"
  }
}
```

## Créer et gérer vos webhooks

* [Créer un point de réception](/api-reference/webhooks/create)
* [Lister vos points de réception](/api-reference/webhooks/list)
* [Récupérer un point de réception](/api-reference/webhooks/get)
* [Modifier un point de réception](/api-reference/webhooks/update)
* [Supprimer un point de réception](/api-reference/webhooks/delete)
* [Créer un abonnement](/api-reference/webhooks/subscriptions-create)
* [Lister vos abonnements](/api-reference/webhooks/subscriptions-list)
* [Récupérer un abonnement](/api-reference/webhooks/subscriptions-get)
* [Modifier un abonnement](/api-reference/webhooks/subscriptions-update)
* [Supprimer un abonnement](/api-reference/webhooks/subscriptions-delete)

<Warning>
  Le secret de signature (`signing_secret`) n'est **jamais renvoyé en lecture** après coup, y compris par [Récupérer un point de réception](/api-reference/webhooks/get) — c'est un champ write-only. Si vous n'en fournissez pas un à la création, SasPay en **génère automatiquement un** pour vous, afin de garantir qu'il ne soit jamais vide (un secret vide rendrait la signature triviale à falsifier). Notez-le précieusement au moment de la création — ou lors d'une rotation, en envoyant un nouveau `signing_secret` via `PATCH`.
</Warning>

## Sécurité — vérifier la signature

Chaque requête envoyée à votre URL inclut deux headers, en plus du corps JSON de l'event :

```
POST <votre url>
Content-Type: application/json
X-Webhook-Signature: <hex sha256 HMAC, en minuscules>
X-Webhook-Event: <event_type, ex "transaction.success">

<le corps JSON exact sur lequel la signature a été calculée>
```

La signature est calculée côté serveur ainsi :

```python theme={null}
body = json.dumps(payload, sort_keys=True, default=str)
signature = hmac.new(signing_secret.encode(), body.encode(), hashlib.sha256).hexdigest()
```

Vérifiez-la côté client sur le **corps brut reçu**, jamais sur une re-sérialisation de `payload` que vous reconstruiriez vous-même — l'ordre des clés, le formatage des nombres ou des dates peuvent différer et casser la comparaison bit à bit :

```js theme={null}
const crypto = require("crypto");

function isValid(rawBody, signatureHeader, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader),
    Buffer.from(expected),
  );
}
```

## Historique de livraison

Chaque tentative de livraison (réussie, échouée, en cours de retry) est journalisée.

* [Lister l'historique de livraison](/api-reference/webhooks/logs-list)
* [Récupérer une livraison](/api-reference/webhooks/logs-get)
* [Renvoyer manuellement une livraison](/api-reference/webhooks/logs-resend)

<Note>
  Pas de filtre disponible sur cette vue marchand — récupérez la liste et filtrez côté client (la vue admin, elle, dispose de filtres).
</Note>

## Fiabilité

* **5 tentatives** par event : immédiat, puis +30s, +5min, +30min, +2h.
* **Timeout de 15s** par tentative.
* Après épuisement des 5 tentatives, la livraison passe en statut `FAILED` **définitif** — seul un [renvoi manuel](/api-reference/webhooks/logs-resend) peut la relancer, elle n'est plus retentée automatiquement.

<Note>
  Un abonnement ne peut être créé qu'une seule fois par couple (webhook, event\_type). Retenter d'abonner le même webhook au même `event_type` renvoie aujourd'hui une erreur serveur générique plutôt qu'un message dédié — vérifiez la liste de vos abonnements existants avant d'en créer un nouveau.
</Note>
