Configurando Webhooks Biométricos Sem IP Fixo
Seus dispositivos biométricos não precisam de IP fixo nem servidor local. Aprenda a configurar webhooks na nuvem que enviam dados de ponto em tempo real para qualquer endpoint — com código pronto para produção em Node.js, Python e cURL.
Introduction
Seu relógio de ponto biométrico fica na recepção do escritório. Sua aplicação roda na AWS. O dispositivo não consegue acessar seu servidor, e seu servidor não consegue acessar o dispositivo. Esse é o problema fundamental que toda equipe enfrenta quando tenta obter dados de ponto em tempo real na sua aplicação na nuvem.
A solução tradicional — um servidor local com IP fixo rodando 24/7 — foi projetada para um mundo onde as aplicações ficavam on-premise. Em 2026, sua app roda no Vercel, Railway, Render ou num cluster Kubernetes. Você precisa de webhooks, não de loops de polling.
Este guia te leva pela configuração de webhooks biométricos com PunchConnect. Do zero a receber eventos de ponto ao vivo em menos de 15 minutos. Sem IP fixo. Sem VPN. Sem servidor local.
Como Funcionam os Webhooks Biométricos
Um webhook é simplesmente uma requisição HTTP POST que o PunchConnect envia para o seu servidor toda vez que algo acontece — um funcionário bate ponto, uma nova digital é cadastrada, ou um dispositivo fica offline. Você dá uma URL ao PunchConnect e ele envia os eventos em tempo real.
A diferença principal das instalações tradicionais: seu servidor não faz polling. Não se conecta ao dispositivo. Simplesmente escuta requisições HTTP como qualquer endpoint web normal.
<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">Fluxo Webhook: Dispositivo → Nuvem → Sua 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">🖐 Relógio</text>
<text x="100" y="137" text-anchor="middle" fill="#22d3ee" font-size="15" font-family="system-ui">de Ponto</text>
<line x1="180" y1="120" x2="260" y2="120" stroke="#64748b" stroke-width="2" marker-end="url(#whArrowPt)"/>
<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">Nuvem</text>
<line x1="440" y1="120" x2="520" y2="120" stroke="#64748b" stroke-width="2" marker-end="url(#whArrowPt)"/>
<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">💻 Sua App</text>
<text x="600" y="137" text-anchor="middle" fill="#34d399" font-size="15" font-family="system-ui">(Qualquer Nuvem)</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">🔄 Fila 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(#whArrowPt2)"/>
<text x="375" y="185" fill="#64748b" font-size="12" font-family="system-ui">se 5xx</text>
<defs>
<marker id="whArrowPt" 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="whArrowPt2" 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>
Passo a passo:
1. O funcionário bate ponto no dispositivo biométrico (digital, rosto, cartão)
2. O dispositivo envia o evento para o motor de protocolo do PunchConnect (saída do dispositivo — não precisa de IP fixo)
3. PunchConnect normaliza os dados brutos num payload JSON limpo
4. O webhook dispara — PunchConnect envia um HTTP POST para a sua URL de callback registrada
5. Sua app processa o evento (atualizar banco de dados, disparar workflow, enviar notificação)
Se seu endpoint retornar erro 5xx ou timeout, o PunchConnect retenta automaticamente com backoff exponencial por até 72 horas. Nenhum dado de ponto é perdido.
Passo 1: Criar Seu Endpoint Webhook
Seu endpoint webhook é simplesmente uma rota HTTP normal que aceita requisições POST:
Node.js (Express):
Python (Flask):
```python
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhook/ponto', methods=['POST'])
def handle_punch():
data = request.json
employee_id = data['employee_id']
timestamp = data['timestamp']
punch_type = data['punch_type']
print(f"Ponto recebido: Funcionário {employee_id} às {timestamp}")
# Sua lógica: salvar no BD, notificar gestor, atualizar ERP...
return jsonify({"received": True}), 200
if __name__ == '__main__':
app.run(port=3000)
```
Faça deploy em qualquer plataforma cloud — Railway, Render, DigitalOcean, AWS Lambda, um VPS. Essa URL vira seu callback.
const express = require('express');const app = express();app.use(express.json());app.post('/webhook/ponto', (req, res) => {const { event_id, employee_id, timestamp, device_serial, punch_type } = req.body;console.log(`Ponto recebido: Funcionário ${employee_id} às ${timestamp}`);// Sua lógica: salvar no BD, notificar gestor, atualizar ERP...res.status(200).json({ received: true });});app.listen(3000, () => console.log('Webhook listener na porta 3000'));
Passo 2: Registrar Seu Webhook no PunchConnect
Use a API do PunchConnect para indicar ao sistema onde enviar os eventos:
O campo secret é fundamental — o PunchConnect usa ele para assinar cada payload webhook para que você possa verificar a autenticidade.
curl -X POST https://api.punchconnect.com/v1/webhooks \-H "Authorization: Bearer SUA_CHAVE_API" \-H "Content-Type: application/json" \-d '{"url": "https://sua-app.railway.app/webhook/ponto","events": ["attendance.punch"],"secret": "seu-segredo-webhook"}'
Passo 3: Configurar Seu Dispositivo
Adicione seu dispositivo biométrico ao PunchConnect pelo painel. O dispositivo se conecta de saída para a nuvem do PunchConnect — sem redirecionamento de porta, sem IP fixo, sem 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">Arquitetura Tradicional vs. Webhook</text>
<text x="200" y="55" text-anchor="middle" fill="#ef4444" font-size="15" font-weight="bold" font-family="system-ui">❌ Tradicional (IP Fixo)</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">Dispositivo</text>
<line x1="170" y1="95" x2="210" y2="95" stroke="#ef4444" stroke-width="1.5" marker-end="url(#redArrPt)"/>
<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">Servidor Local</text>
<text x="280" y="110" text-anchor="middle" fill="#ef4444" font-size="11" font-family="system-ui">(IP fixo, 24/7)</text>
<text x="200" y="145" text-anchor="middle" fill="#64748b" font-size="12" font-family="system-ui">⚠️ Mudança de IP = perda</text>
<text x="600" y="55" text-anchor="middle" fill="#34d399" font-size="15" font-weight="bold" font-family="system-ui">✅ Webhook (Nuvem)</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">Dispositivo</text>
<line x1="580" y1="95" x2="620" y2="95" stroke="#34d399" stroke-width="1.5" marker-end="url(#greenArrPt)"/>
<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">(nuvem, gerenciado)</text>
<line x1="690" y1="120" x2="690" y2="160" stroke="#34d399" stroke-width="1.5" marker-end="url(#greenArrPt)"/>
<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">Sua App Cloud</text>
<text x="690" y="200" text-anchor="middle" fill="#a78bfa" font-size="11" font-family="system-ui">(qualquer lugar)</text>
<text x="600" y="240" text-anchor="middle" fill="#64748b" font-size="12" font-family="system-ui">✅ Sem IP fixo. Sem servidor local.</text>
<defs>
<marker id="redArrPt" 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="greenArrPt" 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>
A configuração leva cerca de 5 minutos por dispositivo:
1. Adicionar o dispositivo (número de série + modelo)
2. Apontar o endereço do servidor do dispositivo para o endpoint do PunchConnect
3. O dispositivo se conecta automaticamente e começa a enviar dados
4. PunchConnect encaminha os eventos para o seu webhook registrado
Passo 4: Verificar Assinaturas de Webhook
Nunca confie cegamente num webhook recebido. O PunchConnect assina cada payload usando HMAC-SHA256 com seu segredo webhook. Sempre verifique antes de processar.
Isso é especialmente importante para conformidade com a LGPD (Lei Geral de Proteção de Dados). Dados biométricos são classificados como dados pessoais sensíveis pela LGPD, e você precisa garantir integridade e autenticidade em cada ponto da cadeia de processamento. O PunchConnect assina cada evento, facilitando a documentação exigida pela ANPD.
Para empresas que seguem a Portaria 671, os dados de ponto via webhook mantêm a rastreabilidade exigida — cada evento contém timestamp, identificação do dispositivo e método de verificação.
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));}
Passo 5: Lidar com Retentativas e Idempotência
PunchConnect retenta entregas com falha usando backoff exponencial: 1 min → 5 min → 15 min → 1 hora → 4 horas, por até 72 horas.
Sempre implemente idempotência usando o campo `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">Sequência de Retentativa com Backoff Exponencial</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">1ª</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">2ª</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">3ª</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">4ª</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">5ª</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">6ª</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">Retentativas por 72 horas — sem perda de dados</text>
</svg>
const processedEvents = new Set(); // Use Redis ou BD em produçãoapp.post('/webhook/ponto', (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);// Processar o evento...res.status(200).json({ received: true });});
Checklist de Produção
Antes de ir para produção:
- ✅ Endpoint HTTPS — PunchConnect não entrega para HTTP puro em produção
- ✅ Verificação de assinatura — valide cada requisição com HMAC-SHA256
- ✅ Idempotência — trate duplicatas com event_id
- ✅ Resposta 200 em menos de 10 segundos — retorne rápido, processe async se precisar
- ✅ Logging — registre cada webhook para depuração
- ✅ Conformidade LGPD — documente o tratamento de dados biométricos conforme exigido pela ANPD
Guias Relacionados
- Ponto Biométrico Sem IP Fixo — por que IP fixo é a abordagem errada
- Integração Webhook ZKTeco — configuração específica para terminais ZKTeco
- API REST de Dispositivo Biométrico — referência completa da API com exemplos de código
- Segurança de Dados Biométricos em Trânsito — criptografia, assinaturas e conformidade
Perguntas Frequentes
Preciso de IP fixo para receber webhooks?
Não. Esse é o ponto. Seu endpoint webhook só precisa de uma URL pública — qualquer plataforma cloud (AWS, DigitalOcean, Railway, Render) fornece isso automaticamente.
O que acontece se meu servidor estiver fora do ar quando um ponto é registrado?
PunchConnect coloca o evento na fila e retenta com backoff exponencial por 72 horas. Quando seu servidor voltar, todos os eventos pendentes são entregues em ordem. Zero perda de dados.
Posso receber webhooks numa função serverless?
Sim. Os webhooks do PunchConnect funcionam com AWS Lambda, Vercel Functions, Cloudflare Workers, Google Cloud Functions — qualquer plataforma que receba requisições HTTP POST.
Como depuro entregas de webhook com falha?
O painel do PunchConnect mostra um log completo de cada webhook: corpo da requisição, código de resposta, tempo de resposta e histórico de retentativas. Você também pode reenviar qualquer entrega com falha com um clique.
Está em conformidade com a LGPD e Portaria 671?
Sim. PunchConnect foi projetado para conformidade com a LGPD. Dados biométricos são criptografados em trânsito (TLS 1.3), cada webhook é assinado para garantir integridade, e os registros de ponto mantêm a rastreabilidade exigida pela Portaria 671 — com timestamp, identificação do dispositivo e método de verificação em cada evento.
---
*Pronto para receber dados de ponto em tempo real na sua app na nuvem? Comece seu teste gratuito — primeiro dispositivo grátis, configuração em menos de 15 minutos.*
Artigos relacionados
Como Conectar ZKTeco ao Odoo: Integração via API Cloud (Sem VPN, Sem Software Local)
10 min read
TutorialIntegração Webhook ZKTeco: Pipelines de Ponto em Tempo Real com API REST
12 min read
TutorialIntegração API de Leitor de Impressão Digital: Guia do Desenvolvedor para Biometria Cloud
8 min read