Integração Webhook ZKTeco: Pipelines de Ponto em Tempo Real com API REST
A integração webhook ZKTeco substitui o polling por eventos em tempo real. Aprenda a configurar webhooks, verificar assinaturas HMAC e construir pipelines de ponto confiáveis com a API REST do PunchConnect. Compatível com Portaria 671 e LGPD.
Introduction
Todo desenvolvedor que construiu uma integração de ponto com ZKTeco começou da mesma forma: escrever um cron job que conecta ao dispositivo a cada 10 minutos, puxa os novos registros de ponto e sincroniza com o banco de dados. Funciona no desenvolvimento. Quebra em produção.
PunchConnect é um middleware cloud API REST que conecta dispositivos biométricos ZKTeco a qualquer sistema de software. Em vez do seu servidor fazer polling nos dispositivos, o PunchConnect entrega os eventos de ponto à sua aplicação em tempo real via webhooks — o mesmo padrão event-driven usado pelo Stripe, GitHub e Twilio. Este artigo guia você por uma integração webhook ZKTeco completa: desde registrar seu primeiro dispositivo até lidar com eventos em produção com lógica de retry, verificação de assinaturas e roteamento multi-unidade.
Por que o polling falha em produção
O modelo de polling tem três problemas fundamentais que nenhuma engenharia resolve.
Latência. Um intervalo de polling de 10 minutos significa que os dados de ponto chegam com 0 a 10 minutos de atraso. Para fluxos sensíveis ao tempo — trocas de turno, alertas de hora extra, controle de acesso — esse atraso é inaceitável. Reduzir o intervalo para 1 minuto gera 1.440 conexões por dispositivo por dia, a maioria sem dados novos.
Dependência de rede. O polling exige que seu servidor alcance o dispositivo em um IP conhecido. Isso implica IPs fixos, port forwarding ou túneis VPN em cada unidade. Para organizações com ERPs na nuvem como Odoo Online ou ERPNext, isso é inviável. Se você já enfrentou esse problema, o guia sobre controle de ponto biométrico sem IP fixo explica a mudança arquitetural em detalhes.
Fragilidade em escala. Gerenciar conexões de polling para 50 dispositivos em 12 filiais significa 50 pontos potenciais de falha. Uma queda do provedor de internet, um reboot do roteador, uma mudança nas regras do firewall — e você perde dados de ponto sem nenhuma notificação.
Como funcionam os webhooks
Os webhooks invertem a direção da conexão. Em vez da sua aplicação perguntar "tem novos registros?" a cada poucos minutos, o PunchConnect avisa sua aplicação no instante em que um registro acontece.
O fluxo é direto:
1. Um funcionário escaneia sua digital ou rosto em um dispositivo ZKTeco.
2. O dispositivo envia o evento para a nuvem do PunchConnect. Você configura isso uma vez no dashboard — sem necessidade de conhecer o protocolo, sem SDK para instalar.
3. O PunchConnect normaliza os dados brutos do dispositivo em um payload JSON limpo e envia um HTTP POST para sua URL de webhook.
4. Sua aplicação processa o evento e responde com 200 OK.
Esta arquitetura significa que sua aplicação só precisa expor um único endpoint HTTPS. Sem regras de firewall de entrada nas redes dos dispositivos. Sem IPs fixos. Sem infraestrutura de polling.
Tipos de eventos suportados
- attendance.created — um novo evento de entrada ou saída
- device.online — um dispositivo se conectou à nuvem
- device.offline — um dispositivo parou de enviar heartbeats
- employee.enrolled — uma nova digital ou template facial foi registrada no dispositivo
Você se inscreve apenas nos eventos que precisa. A maioria das integrações começa com attendance.created e adiciona monitoramento de dispositivos depois.
Pré-requisitos
Você precisa de uma conta PunchConnect (o teste grátis de 7 dias serve para este tutorial), pelo menos um dispositivo ZKTeco registrado no seu dashboard, e um endpoint HTTPS que possa receber requisições POST. Para desenvolvimento local, ferramentas como ngrok fornecem uma URL pública que conecta ao seu servidor local.
Passo 1: Registrar seu webhook
Use a API do PunchConnect para criar uma assinatura de webhook:
curl -X POST https://api.punchconnect.com/api/v2/zk/webhooks \-H "X-CLIENT-ID: seu_client_id" \-H "X-CLIENT-SECRET: seu_client_secret" \-H "Content-Type: application/json" \-d '{"url": "https://seu-servidor.com/webhooks/ponto","events": ["attendance.created", "device.online", "device.offline"],"secret": "seu_segredo_webhook"}'
Passo 2: Construir seu receptor de webhooks
Python (Flask)
import hmacimport hashlibfrom flask import Flask, request, jsonifyapp = Flask(__name__)WEBHOOK_SECRET = "seu_segredo_webhook"def verificar_assinatura(payload, assinatura):esperada = hmac.new(WEBHOOK_SECRET.encode(),payload,hashlib.sha256).hexdigest()return hmac.compare_digest(esperada, assinatura)@app.route("/webhooks/ponto", methods=["POST"])def receber_webhook():assinatura = request.headers.get("X-Signature", "")if not verificar_assinatura(request.data, assinatura):return jsonify({"error": "Assinatura inválida"}), 401evento = request.jsontipo = evento.get("event")event_id = evento.get("event_id")# Idempotência: verificar se já processamos este eventoif evento_ja_processado(event_id):return jsonify({"status": "ja_processado"}), 200if tipo == "attendance.created":funcionario = evento["data"]["employee_name"]hora = evento["data"]["timestamp"]tipo_registro = evento["data"]["punch_type"]print(f"{funcionario} - {tipo_registro} às {hora}")# Inserir no banco, disparar workflow, etc.elif tipo == "device.offline":dispositivo = evento["data"]["device_serial"]print(f"⚠️ Dispositivo {dispositivo} desconectado")# Enviar alerta para equipe de TImarcar_como_processado(event_id)return jsonify({"status": "recebido"}), 200
JavaScript (Express.js)
const express = require('express');const crypto = require('crypto');const app = express();app.use(express.json());const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;function verificarAssinatura(payload, assinatura) {const esperada = crypto.createHmac('sha256', WEBHOOK_SECRET).update(JSON.stringify(payload)).digest('hex');return crypto.timingSafeEqual(Buffer.from(esperada), Buffer.from(assinatura));}app.post('/webhooks/ponto', async (req, res) => {const assinatura = req.headers['x-signature'];if (!verificarAssinatura(req.body, assinatura)) {return res.status(401).json({ error: 'Assinatura inválida' });}const evento = req.body;const { event: tipo, event_id } = evento;// Idempotênciaconst existe = await db.query('SELECT 1 FROM eventos_processados WHERE event_id = ?',[event_id]);if (existe.length > 0) {return res.status(200).json({ status: 'ja_processado' });}if (tipo === 'attendance.created') {const { employee_name, timestamp, punch_type } = evento.data;await db.query('INSERT INTO ponto (funcionario, tipo, hora, dispositivo_id) VALUES (?, ?, ?, ?)',[employee_name, punch_type, timestamp, evento.data.device_serial]);}await db.query('INSERT INTO eventos_processados (event_id) VALUES (?)', [event_id]);res.status(200).json({ status: 'recebido' });});app.listen(3000, () => console.log('Servidor webhook ativo na porta 3000'));
Passo 3: Testar seu webhook
Isso envia um evento sintético attendance.created com dados de teste. Seu receptor deve registrar o evento e retornar 200 OK. Verifique os logs de entrega de webhooks no seu dashboard do PunchConnect para confirmar a entrega.
curl -X POST https://api.punchconnect.com/api/v2/zk/webhooks/test \-H "X-CLIENT-ID: seu_client_id" \-H "X-CLIENT-SECRET: seu_client_secret"
Padrões de produção
Idempotência
Problemas de rede podem fazer o PunchConnect retentar uma entrega de webhook. Cada evento inclui um campo único event_id. Armazene os IDs processados e ignore duplicatas:
eventos_processados = set() # Em produção, use Redis ou seu banco de dadosdef evento_ja_processado(event_id):return event_id in eventos_processadosdef marcar_como_processado(event_id):eventos_processados.add(event_id)
Retentativas automáticas
O PunchConnect retenta entregas com falha (respostas não-2xx) com backoff exponencial: 1 minuto, 5 minutos, 30 minutos, 2 horas, 24 horas. Após 5 tentativas com falha, o webhook é marcado como falhando e você recebe um alerta por email. Seu endpoint deve responder em menos de 10 segundos — descarregue o processamento pesado para uma fila em segundo plano.
Roteamento multi-unidade
Para organizações com dispositivos em múltiplas unidades, use os campos device_serial e location opcionais para rotear eventos para o pipeline correto:
ROTAS_POR_UNIDADE = {"UNIDADE_SP": processar_sao_paulo,"UNIDADE_RJ": processar_rio_janeiro,"UNIDADE_BH": processar_belo_horizonte,}def rotear_evento(evento):unidade = evento["data"].get("location", "default")handler = ROTAS_POR_UNIDADE.get(unidade, processar_default)handler(evento)
Monitoramento de dispositivos
Inscreva-se nos eventos device.online e device.offline para monitorar sua frota. Um dispositivo que fica offline durante o horário comercial provavelmente tem um problema de conectividade — e você quer saber antes que os funcionários reclamem de registros perdidos.
Polling vs Webhooks: comparação prática
| Aspecto | Polling | Webhooks |
|---------|---------|----------|
| Latência | 5-30 min (intervalo fixo) | <2 segundos |
| Infraestrutura | IP fixo + VPN + servidor 24/7 | Um endpoint HTTPS |
| Detecção de falhas | No próximo ciclo de sync | Imediata (evento device.offline) |
| Esforço de desenvolvimento | Lidar com protocolos + conexões + parsing | Um endpoint HTTP + parser JSON |
| Custo em escala (50 dispositivos) | 14.400 conexões/dia | Só eventos reais (~2.500/dia) |
Conformidade regulatória no Brasil
As organizações brasileiras devem considerar marcos regulatórios específicos ao implementar sistemas de ponto biométrico:
Portaria 671/2021 — Ministério do Trabalho
- A Portaria 671 regulamenta os sistemas de registro eletrônico de ponto (REP)
- Sistemas na nuvem (REP-A e REP-P) são permitidos desde que garantam integridade e inviolabilidade dos dados
- Os webhooks do PunchConnect fornecem registros auditáveis com marca de tempo UTC, ID do dispositivo e método de verificação — atendendo aos requisitos de rastreabilidade
LGPD — Lei Geral de Proteção de Dados
- A LGPD (Lei 13.709/2018) classifica dados biométricos como dados pessoais sensíveis (Artigo 5º, II)
- O tratamento exige consentimento específico do titular ou base legal prevista no Artigo 11
- O direito de eliminação (Artigo 18, VI) exige que a empresa possa apagar todos os dados biométricos de um funcionário sob demanda — a API REST do PunchConnect suporta DELETE /v1/users/{id} para isso
Fusos horários brasileiros
- O Brasil tem 4 fusos horários (BRT UTC-3, AMT UTC-4, ACT UTC-5, FNT UTC-2)
- Os webhooks do PunchConnect sempre enviam timestamps em UTC — converta para o fuso local no seu backend
- Para a maioria das empresas (SP, RJ, MG, RS), use America/Sao_Paulo (BRT/BRST)
Integração com ERPs
O verdadeiro poder da integração webhook ZKTeco é o que acontece depois que o evento chega. O PunchConnect funciona com qualquer stack que lide com requisições HTTP POST: Django, Laravel, Spring Boot, Go, Ruby on Rails, ou funções serverless na AWS Lambda ou Cloudflare Workers.
- Para Odoo, consulte o guia completo de conexão ZKTeco ao Odoo
- Para ERPNext, o artigo sobre controle de ponto biométrico ERPNext cobre a configuração completa incluindo o receptor de webhooks do Frappe HR
FAQ
Quanto tempo leva para um evento webhook chegar?
O PunchConnect entrega eventos webhook em 1 a 3 segundos a partir do momento em que o funcionário escaneia no dispositivo. Compare isso com intervalos de polling de 5 a 30 minutos em abordagens tradicionais.
Preciso de IP fixo?
Não. Seu dispositivo ZKTeco conecta de forma saída à nuvem do PunchConnect, e o PunchConnect envia webhooks para a URL da sua aplicação. Nenhum lado requer IP fixo, port forwarding ou VPN. Consulte o guia completo sobre controle de ponto sem IP fixo.
Posso usar um único endpoint para múltiplos dispositivos?
Sim. Quando você cria uma assinatura webhook sem especificar device_serial, o PunchConnect envia eventos de todos os dispositivos da sua conta para esse endpoint. Cada payload inclui o campo device_serial para que sua aplicação roteie os eventos por dispositivo.
O que acontece se meu servidor estiver fora do ar?
O PunchConnect retenta entregas com falha com backoff exponencial por 24 horas (5 retentativas). Os eventos são enfileirados durante a queda e entregues quando seu endpoint volta a funcionar. Nenhum dado de ponto é perdido. Você também pode usar a API REST para buscar eventos perdidos manualmente.
O sistema é compatível com a Portaria 671?
Sim. O PunchConnect atende aos requisitos da Portaria 671 para sistemas REP-P: registros com integridade garantida, marcas de tempo auditáveis, e API para extração de dados. Cada evento webhook inclui timestamp UTC, ID do dispositivo e método de verificação biométrica.
Conclusão
Configurar uma integração webhook ZKTeco com o PunchConnect leva cerca de 15 minutos: registre seu dispositivo, crie um endpoint webhook e comece a receber eventos de ponto em tempo real. Sem SDK para instalar, sem documentação de protocolos para decifrar, sem IPs fixos para configurar.
Comece com o teste grátis de 7 dias — conecte um dispositivo e teste o fluxo de webhooks de ponta a ponta. Quando estiver pronto para escalar, a licença de $200/dispositivo inclui entregas ilimitadas de webhooks, todos os tipos de eventos e a API REST completa para gestão de dispositivos, sincronização de funcionários e consultas de ponto.
Se precisar de ajuda para projetar sua arquitetura de webhooks ou conectar o PunchConnect ao seu ERP específico, fale com a equipe. Construímos isso para implantações que atendem 24.000+ funcionários — sabemos como é a produção.