Configurer des Webhooks Biométriques Sans IP Fixe
Vos dispositifs biométriques n'ont pas besoin d'une IP fixe ni d'un serveur local. Apprenez à configurer des webhooks cloud qui envoient les données de présence en temps réel à n'importe quel endpoint — avec du code prêt pour la production en Node.js, Python et cURL.
Introduction
Votre pointeuse biométrique se trouve dans le hall d'accueil. Votre application tourne sur AWS. Le dispositif ne peut pas joindre votre serveur, et votre serveur ne peut pas joindre le dispositif. C'est le problème fondamental que chaque équipe rencontre quand elle essaie d'obtenir des données de présence en temps réel dans son application cloud.
La solution traditionnelle — un serveur local avec une IP fixe qui tourne 24h/24 — a été conçue pour un monde où les applications étaient hébergées sur site. En 2026, votre app tourne sur Vercel, Railway, Render ou un cluster Kubernetes. Vous avez besoin de webhooks, pas de boucles de polling.
Ce guide vous accompagne dans la configuration de webhooks biométriques avec PunchConnect. Du zéro à la réception d'événements de pointage en direct en moins de 15 minutes. Sans IP fixe. Sans VPN. Sans serveur local.
Étape 1 : Créer Votre Endpoint Webhook
Votre endpoint webhook est simplement une route HTTP normale qui accepte les requêtes POST :
Node.js (Express) :
Python (Flask) :
```python
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhook/attendance', methods=['POST'])
def handle_punch():
data = request.json
employee_id = data['employee_id']
timestamp = data['timestamp']
punch_type = data['punch_type']
print(f"Pointage reçu : Employé {employee_id} à {timestamp}")
# Votre logique : sauvegarder en BDD, notifier le manager, mettre à jour le SIRH...
return jsonify({"received": True}), 200
if __name__ == '__main__':
app.run(port=3000)
```
Déployez ceci sur n'importe quelle plateforme cloud — OVH, Scaleway, Railway, Render, AWS Lambda, un VPS. Cette URL devient votre callback.
const express = require('express');const app = express();app.use(express.json());app.post('/webhook/attendance', (req, res) => {const { event_id, employee_id, timestamp, device_serial, punch_type } = req.body;console.log(`Pointage reçu : Employé ${employee_id} à ${timestamp}`);// Votre logique : sauvegarder en BDD, notifier le manager, mettre à jour le SIRH...res.status(200).json({ received: true });});app.listen(3000, () => console.log('Webhook listener sur le port 3000'));
Étape 2 : Enregistrer Votre Webhook avec PunchConnect
Utilisez l'API PunchConnect pour indiquer au système où envoyer les événements :
Le champ secret est essentiel — PunchConnect l'utilise pour signer chaque payload webhook afin que vous puissiez vérifier l'authenticité.
curl -X POST https://api.punchconnect.com/v1/webhooks \-H "Authorization: Bearer VOTRE_CLE_API" \-H "Content-Type: application/json" \-d '{"url": "https://votre-app.railway.app/webhook/attendance","events": ["attendance.punch"],"secret": "votre-secret-webhook"}'
Étape 3 : Configurer Votre Dispositif
Ajoutez votre dispositif biométrique à PunchConnect via le tableau de bord. Le dispositif se connecte en sortant vers le cloud PunchConnect — pas de redirection de port, pas d'IP fixe, pas de VPN.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 260" fill="none" style="width:100%;max-width:800px;">
<text x="400" y="25" text-anchor="middle" fill="#94a3b8" font-size="17" font-weight="bold" font-family="system-ui">Architecture Traditionnelle vs. Webhook</text>
<text x="200" y="55" text-anchor="middle" fill="#ef4444" font-size="15" font-weight="bold" font-family="system-ui">❌ Traditionnel (IP Fixe)</text>
<rect x="50" y="70" width="120" height="50" rx="8" stroke="#ef4444" stroke-width="1.5" fill="none"/>
<text x="110" y="100" text-anchor="middle" fill="#ef4444" font-size="14" font-family="system-ui">Dispositif</text>
<line x1="170" y1="95" x2="210" y2="95" stroke="#ef4444" stroke-width="1.5" marker-end="url(#redArrFr)"/>
<rect x="210" y="70" width="140" height="50" rx="8" stroke="#ef4444" stroke-width="1.5" fill="none"/>
<text x="280" y="95" text-anchor="middle" fill="#ef4444" font-size="13" font-family="system-ui">Serveur Local</text>
<text x="280" y="110" text-anchor="middle" fill="#ef4444" font-size="11" font-family="system-ui">(IP fixe, 24/7)</text>
<text x="200" y="145" text-anchor="middle" fill="#64748b" font-size="12" font-family="system-ui">⚠️ Changement d'IP = perte</text>
<text x="600" y="55" text-anchor="middle" fill="#34d399" font-size="15" font-weight="bold" font-family="system-ui">✅ Webhook (Cloud)</text>
<rect x="460" y="70" width="120" height="50" rx="8" stroke="#34d399" stroke-width="1.5" fill="none"/>
<text x="520" y="100" text-anchor="middle" fill="#34d399" font-size="14" font-family="system-ui">Dispositif</text>
<line x1="580" y1="95" x2="620" y2="95" stroke="#34d399" stroke-width="1.5" marker-end="url(#greenArrFr)"/>
<rect x="620" y="70" width="140" height="50" rx="8" stroke="#34d399" stroke-width="1.5" fill="none"/>
<text x="690" y="90" text-anchor="middle" fill="#34d399" font-size="13" font-family="system-ui">PunchConnect</text>
<text x="690" y="105" text-anchor="middle" fill="#34d399" font-size="11" font-family="system-ui">(cloud, géré)</text>
<line x1="690" y1="120" x2="690" y2="160" stroke="#34d399" stroke-width="1.5" marker-end="url(#greenArrFr)"/>
<rect x="620" y="160" width="140" height="50" rx="8" stroke="#a78bfa" stroke-width="1.5" fill="none"/>
<text x="690" y="185" text-anchor="middle" fill="#a78bfa" font-size="13" font-family="system-ui">Votre App Cloud</text>
<text x="690" y="200" text-anchor="middle" fill="#a78bfa" font-size="11" font-family="system-ui">(n'importe où)</text>
<text x="600" y="240" text-anchor="middle" fill="#64748b" font-size="12" font-family="system-ui">✅ Sans IP fixe. Sans serveur local.</text>
<defs>
<marker id="redArrFr" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto"><polygon points="0 0, 10 3.5, 0 7" fill="#ef4444"/></marker>
<marker id="greenArrFr" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto"><polygon points="0 0, 10 3.5, 0 7" fill="#34d399"/></marker>
</defs>
</svg>
La configuration prend environ 5 minutes par dispositif dans le tableau de bord :
1. Ajouter le dispositif (numéro de série + modèle)
2. Pointer l'adresse serveur du dispositif vers l'endpoint PunchConnect
3. Le dispositif se connecte automatiquement et commence à pousser les données
4. PunchConnect transfère les événements à votre webhook enregistré
Étape 4 : Vérifier les Signatures Webhook
Ne faites jamais confiance à un webhook entrant aveuglément. PunchConnect signe chaque payload avec HMAC-SHA256 en utilisant votre secret webhook. Vérifiez toujours avant de traiter.
Ceci est particulièrement important pour la conformité RGPD — vous devez garantir l'intégrité et l'authenticité des données biométriques traitées. Avec PunchConnect, chaque événement est signé et traçable, ce qui facilite la documentation exigée par la CNIL.
const crypto = require('crypto');function verifyWebhookSignature(payload, signature, secret) {const expected = crypto.createHmac('sha256', secret).update(JSON.stringify(payload)).digest('hex');return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));}
Étape 5 : Gérer les Retries et l'Idempotence
PunchConnect retente les livraisons échouées avec backoff exponentiel : 1 min → 5 min → 15 min → 1 heure → 4 heures, pendant 72 heures maximum.
Implémentez toujours l'idempotence avec le champ `event_id` :
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 200" fill="none" style="width:100%;max-width:800px;">
<text x="400" y="25" text-anchor="middle" fill="#94a3b8" font-size="17" font-weight="bold" font-family="system-ui">Séquence de Retry avec Backoff Exponentiel</text>
<line x1="60" y1="100" x2="740" y2="100" stroke="#334155" stroke-width="2"/>
<circle cx="100" cy="100" r="8" fill="#34d399"/>
<text x="100" y="80" text-anchor="middle" fill="#34d399" font-size="14" font-family="system-ui">1er</text>
<text x="100" y="130" text-anchor="middle" fill="#64748b" font-size="12" font-family="system-ui">0 min</text>
<circle cx="220" cy="100" r="8" fill="#fb923c"/>
<text x="220" y="80" text-anchor="middle" fill="#fb923c" font-size="14" font-family="system-ui">2e</text>
<text x="220" y="130" text-anchor="middle" fill="#64748b" font-size="12" font-family="system-ui">+1 min</text>
<circle cx="340" cy="100" r="8" fill="#fb923c"/>
<text x="340" y="80" text-anchor="middle" fill="#fb923c" font-size="14" font-family="system-ui">3e</text>
<text x="340" y="130" text-anchor="middle" fill="#64748b" font-size="12" font-family="system-ui">+5 min</text>
<circle cx="460" cy="100" r="8" fill="#fb923c"/>
<text x="460" y="80" text-anchor="middle" fill="#fb923c" font-size="14" font-family="system-ui">4e</text>
<text x="460" y="130" text-anchor="middle" fill="#64748b" font-size="12" font-family="system-ui">+15 min</text>
<circle cx="580" cy="100" r="8" fill="#fb923c"/>
<text x="580" y="80" text-anchor="middle" fill="#fb923c" font-size="14" font-family="system-ui">5e</text>
<text x="580" y="130" text-anchor="middle" fill="#64748b" font-size="12" font-family="system-ui">+1 h</text>
<circle cx="700" cy="100" r="8" fill="#fb923c"/>
<text x="700" y="80" text-anchor="middle" fill="#fb923c" font-size="14" font-family="system-ui">6e</text>
<text x="700" y="130" text-anchor="middle" fill="#64748b" font-size="12" font-family="system-ui">+4 h</text>
<text x="400" y="170" text-anchor="middle" fill="#64748b" font-size="14" font-family="system-ui">Retries pendant 72 heures — aucune donnée perdue</text>
</svg>
const processedEvents = new Set(); // Utilisez Redis ou une BDD en productionapp.post('/webhook/attendance', (req, res) => {const { event_id } = req.body;if (processedEvents.has(event_id)) {return res.status(200).json({ received: true, duplicate: true });}processedEvents.add(event_id);// Traiter l'événement...res.status(200).json({ received: true });});
Checklist de Production
Avant de passer en production :
- ✅ Endpoint HTTPS — PunchConnect ne livre pas vers du HTTP en production
- ✅ Vérification de signature — validez chaque requête avec HMAC-SHA256
- ✅ Idempotence — gérez les doublons avec event_id
- ✅ Réponse 200 en moins de 10 secondes — retournez vite, traitez en async si besoin
- ✅ Journalisation — enregistrez chaque webhook pour le débogage
- ✅ Conformité RGPD — documentez le traitement des données biométriques (obligation CNIL)
Guides Connexes
- Présence Biométrique Sans IP Fixe — pourquoi les IP fixes sont la mauvaise approche
- Intégration Webhook ZKTeco — configuration spécifique pour les terminaux ZKTeco
- API REST Dispositif Biométrique — référence API complète avec exemples de code
- Sécuriser les Données Biométriques en Transit — chiffrement, signatures et conformité
Questions Fréquentes
Ai-je besoin d'une IP fixe pour recevoir des webhooks ?
Non. C'est justement le principe. Votre endpoint webhook a juste besoin d'une URL publique — n'importe quelle plateforme cloud (OVH, Scaleway, AWS, Railway) en fournit une automatiquement. Le dispositif se connecte en sortant vers PunchConnect, et PunchConnect se connecte en sortant vers votre URL.
Que se passe-t-il si mon serveur est hors service quand un pointage a lieu ?
PunchConnect met l'événement en file d'attente et retente avec backoff exponentiel pendant 72 heures. Quand votre serveur revient en ligne, tous les événements en attente sont livrés dans l'ordre. Zéro perte de données.
Puis-je recevoir des webhooks sur une fonction serverless ?
Oui. Les webhooks PunchConnect fonctionnent avec AWS Lambda, Vercel Functions, Cloudflare Workers, Google Cloud Functions — toute plateforme pouvant recevoir des requêtes HTTP POST.
Comment déboguer les livraisons webhook échouées ?
Le tableau de bord PunchConnect affiche un journal complet pour chaque webhook : corps de la requête, code de réponse, temps de réponse et historique des retries. Vous pouvez aussi rejouer n'importe quelle livraison échouée en un clic.
Est-ce conforme au RGPD ?
PunchConnect est conçu pour la conformité RGPD. Les données biométriques sont chiffrées en transit (TLS 1.3), chaque webhook est signé pour garantir l'intégrité, et vous gardez le contrôle total de vos données — hébergées en Europe sur demande.
---
*Prêt à recevoir des données de présence en temps réel dans votre app cloud ? Commencez votre essai gratuit — premier dispositif offert, configuration en moins de 15 minutes.*
Comment Fonctionnent les Webhooks Biométriques
Un webhook est simplement une requête HTTP POST que PunchConnect envoie à votre serveur chaque fois qu'un événement se produit — un employé pointe, une nouvelle empreinte est enregistrée, ou un dispositif se déconnecte. Vous donnez une URL à PunchConnect, et il vous pousse les événements en temps réel.
La différence clé avec les installations traditionnelles : votre serveur ne fait pas de polling. Il ne se connecte pas au dispositif. Il écoute simplement les requêtes HTTP entrantes comme n'importe quel endpoint web classique.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 280" fill="none" style="width:100%;max-width:800px;">
<text x="400" y="25" text-anchor="middle" fill="#94a3b8" font-size="17" font-weight="bold" font-family="system-ui">Flux Webhook : Dispositif → Cloud → Votre App</text>
<rect x="20" y="80" width="160" height="80" rx="12" stroke="#22d3ee" stroke-width="2" fill="none"/>
<text x="100" y="115" text-anchor="middle" fill="#22d3ee" font-size="15" font-family="system-ui">🖐 Pointeuse</text>
<text x="100" y="137" text-anchor="middle" fill="#22d3ee" font-size="15" font-family="system-ui">Biométrique</text>
<line x1="180" y1="120" x2="260" y2="120" stroke="#64748b" stroke-width="2" marker-end="url(#whArrowFr)"/>
<text x="220" y="108" text-anchor="middle" fill="#64748b" font-size="13" font-family="system-ui">push</text>
<rect x="260" y="80" width="180" height="80" rx="12" stroke="#a78bfa" stroke-width="2" fill="none"/>
<text x="350" y="115" text-anchor="middle" fill="#a78bfa" font-size="15" font-family="system-ui">☁️ PunchConnect</text>
<text x="350" y="137" text-anchor="middle" fill="#a78bfa" font-size="15" font-family="system-ui">Cloud</text>
<line x1="440" y1="120" x2="520" y2="120" stroke="#64748b" stroke-width="2" marker-end="url(#whArrowFr)"/>
<text x="480" y="108" text-anchor="middle" fill="#64748b" font-size="13" font-family="system-ui">webhook</text>
<rect x="520" y="80" width="160" height="80" rx="12" stroke="#34d399" stroke-width="2" fill="none"/>
<text x="600" y="115" text-anchor="middle" fill="#34d399" font-size="15" font-family="system-ui">💻 Votre App</text>
<text x="600" y="137" text-anchor="middle" fill="#34d399" font-size="15" font-family="system-ui">(Tout Cloud)</text>
<rect x="260" y="200" width="180" height="50" rx="10" stroke="#fb923c" stroke-width="1.5" fill="none"/>
<text x="350" y="230" text-anchor="middle" fill="#fb923c" font-size="14" font-family="system-ui">🔄 File de Retry (72h)</text>
<line x1="350" y1="160" x2="350" y2="200" stroke="#fb923c" stroke-width="1.5" stroke-dasharray="5,4" marker-end="url(#whArrowFr2)"/>
<text x="375" y="185" fill="#64748b" font-size="12" font-family="system-ui">si 5xx</text>
<defs>
<marker id="whArrowFr" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto"><polygon points="0 0, 10 3.5, 0 7" fill="#64748b"/></marker>
<marker id="whArrowFr2" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto"><polygon points="0 0, 10 3.5, 0 7" fill="#fb923c"/></marker>
</defs>
</svg>
Voici ce qui se passe étape par étape :
1. L'employé pointe sur le dispositif biométrique (empreinte, visage, badge)
2. Le dispositif pousse l'événement vers le moteur protocolaire de PunchConnect (sortant depuis le dispositif — pas besoin d'IP fixe)
3. PunchConnect normalise les données brutes en un payload JSON propre
4. Le webhook se déclenche — PunchConnect envoie un HTTP POST à votre URL de callback enregistrée
5. Votre app traite l'événement (mise à jour base de données, déclenchement workflow, envoi notification)
Si votre endpoint retourne une erreur 5xx ou expire, PunchConnect retente automatiquement avec backoff exponentiel pendant 72 heures. Aucune donnée de pointage n'est perdue.