Webhooks API
Les webhooks vous permettent de recevoir des notifications en temps réel lorsque des événements se produisent dans DocFlow.
ℹ️ Les webhooks sont disponibles uniquement pour les plans Pro et Business.
Événements disponibles
| Événement | Description |
|---|---|
render.completed | Un render a été généré avec succès |
render.failed | Un render a échoué |
Endpoints
| Méthode | Endpoint | Description |
|---|---|---|
POST | /webhooks | Créer un webhook |
GET | /webhooks | Lister les webhooks |
PATCH | /webhooks/:id | Modifier un webhook |
DELETE | /webhooks/:id | Supprimer un webhook |
POST | /webhooks/:id/rotate-secret | Renouveler le secret |
Créer un webhook
POST /webhooks
Request Body
{
"url": "https://api.exemple.com/webhooks/docflow",
"events": ["render.completed", "render.failed"]
}| Paramètre | Type | Requis | Description |
|---|---|---|---|
url | String | Oui | URL HTTPS qui recevra les événements |
events | String[] | Oui | Liste des événements à écouter |
Response (201 Created)
{
"data": {
"id": "whk_abc123",
"url": "https://api.exemple.com/webhooks/docflow",
"events": ["render.completed", "render.failed"],
"secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxx",
"isActive": true,
"failCount": 0,
"createdAt": "2024-01-15T10:30:00Z"
}
}⚠️ Le secret (whsec_...) n'est affiché qu'une seule fois. Stockez-le en sécurité pour vérifier les signatures des webhooks.
Format des webhooks
Lorsqu'un événement se produit, DocFlow envoie une requête POST à votre URL avec le payload suivant :
Headers
Content-Type: application/json
X-DocFlow-Event: render.completed
X-DocFlow-Signature: sha256=xxxxxxxxxxxxxxxxxxxxxxxx
X-DocFlow-Timestamp: 1705316400Payload pour render.completed
{
"event": "render.completed",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"renderId": "rnd_xyz789",
"templateId": "tpl_abc123",
"templateName": "Contrat de travail",
"status": "completed",
"downloadUrl": "https://storage.docflow.io/renders/rnd_xyz789.pdf?token=...",
"fileSize": 256000,
"durationMs": 342,
"createdAt": "2024-01-15T10:30:00Z"
}
}Payload pour render.failed
{
"event": "render.failed",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"renderId": "rnd_xyz789",
"templateId": "tpl_abc123",
"templateName": "Contrat de travail",
"status": "failed",
"errorCode": "RENDER_FAILED",
"errorMsg": "Failed to render PDF: corrupted template file",
"createdAt": "2024-01-15T10:30:00Z"
}
}Vérification de signature
Pour vous assurer qu'un webhook provient bien de DocFlow, vérifiez la signature HMAC-SHA256.
Algorithme
- Récupérez le timestamp (
X-DocFlow-Timestamp) et la signature (X-DocFlow-Signature) - Construisez le message :
{timestamp}.{body} - Calculez le HMAC-SHA256 avec votre secret
- Comparez avec la signature reçue
Exemple Node.js
import crypto from 'crypto';
function verifyWebhook(payload, signature, timestamp, secret) {
const message = `${timestamp}.${payload}`;
const expectedSignature = 'sha256=' +
crypto.createHmac('sha256', secret)
.update(message)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Utilisation
const isValid = verifyWebhook(
req.body,
req.headers['x-docflow-signature'],
req.headers['x-docflow-timestamp'],
process.env.DOCFLOW_WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}Exemple Python
import hmac
import hashlib
def verify_webhook(payload, signature, timestamp, secret):
message = f"{timestamp}.{payload}"
expected = 'sha256=' + hmac.new(
secret.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)Réponse attendue
Votre endpoint doit retourner un code HTTP 2xx dans les 30 secondes. Tout autre code sera considéré comme un échec.
Politique de retry
En cas d'échec, DocFlow réessaie automatiquement jusqu'à 3 fois avec un délai exponentiel :
- 1ère tentative : immédiate
- 2ème tentative : après 1 minute
- 3ème tentative : après 5 minutes
- 4ème tentative : après 30 minutes
Après 5 échecs consécutifs, le webhook est automatiquement désactivé. Vous recevrez un email de notification.
Renouveler le secret
POST /webhooks/:id/rotate-secret
Génère un nouveau secret pour le webhook. L'ancien secret est immédiatement invalidé.
Response
{
"data": {
"id": "whk_abc123",
"secret": "whsec_yyyyyyyyyyyyyyyyyyyy",
...
}
}Bonnes pratiques
- Toujours vérifier la signature avant de traiter un webhook
- Vérifier que le timestamp n'est pas trop ancien (protection contre les replay attacks)
- Traiter les webhooks de façon idempotente (un même événement peut être reçu plusieurs fois)
- Répondre rapidement (moins de 30 secondes) et traiter en arrière-plan si nécessaire
- Logger les webhooks reçus pour le debugging