APIPricingDocsBlogPartnersContact
Back to blog
Tutorial

Biometric Attendance for ERPNext: Complete Integration Guide

Connect ZKTeco fingerprint and face scanners to ERPNext with real-time webhooks. Full code walkthrough β€” from device setup to Auto Attendance, no pyzk required.

PunchConnect TeamΒ·Mar 30, 2026Β·10 min read

Why Biometric Attendance in ERPNext Breaks at Scale

You have ZKTeco fingerprint scanners at your offices and ERPNext managing HR. The official Frappe Biometric Attendance Sync Tool uses pyzk to poll devices over your LAN. One office, one device, one network β€” it works.

Add a second location and everything falls apart.

pyzk requires a Python script running on the same local network as every device. If ERPNext runs on Frappe Cloud, you cannot install pyzk on their servers. If you have branch offices, each needs its own sync machine. If a device gets a new IP via DHCP, the connection breaks silently β€” and you discover it when payroll runs with missing records.

PunchConnect eliminates this entire architecture. Devices push attendance data to a cloud API. ERPNext receives it via webhook in real time. No local scripts. No LAN dependency. No cron jobs polling stale data.

This guide walks you through the complete integration: device configuration, webhook receiver, employee mapping, Auto Attendance, and production hardening. Every code block is copy-paste ready.

Architecture: How the Integration Works

The traditional pyzk approach and the PunchConnect approach solve the same problem differently:

Traditional (pyzk):

- Requires shared network
- 10–30 minute sync delay
- Breaks across sites

PunchConnect (cloud webhook):

- Works from any network
- Real-time (1–3 seconds)
- Single endpoint for all sites

The device pushes punches to PunchConnect's cloud infrastructure. PunchConnect normalizes the data across firmware versions and forwards it as a clean JSON webhook to your ERPNext instance. ERPNext creates an Employee Checkin record, and the built-in Auto Attendance module converts checkins into Attendance entries automatically.

No middleware server. No VPN tunnels. No per-site infrastructure.

text
Device β†’ [LAN] β†’ pyzk script β†’ [cron] β†’ ERPNext API β†’ Employee Checkin

Prerequisites

Before starting, you need:

- ERPNext v14+ or v15+ with the HRMS app installed
- A ZKTeco device with cloud connectivity (SpeedFace V5L, ProFace X, uFace 800, MB460, iClock 680, K-series, or similar)
- A PunchConnect account β€” start a free 7-day trial, no credit card required
- HTTPS on your ERPNext instance β€” webhooks require a valid SSL certificate

Step 1: Create Your PunchConnect Account and Register a Device

Sign up at punchconnect.com/signup. After email verification, you land on the dashboard.

Click Add Device and enter your device's serial number. PunchConnect generates a cloud configuration for your device β€” apply it through the PunchConnect dashboard. The entire process takes about 5 minutes per device.

Once configured, the device appears as "Online" in your dashboard within 60 seconds.

Managing multiple sites? Register all devices under one account. PunchConnect handles 50+ sites and hundreds of devices from a single dashboard. Each device is identified by its serial number, and you can organize them by location.

Step 2: Build the Webhook Receiver in ERPNext

PunchConnect sends a POST request to your ERPNext instance every time someone punches. You need a whitelisted API method to receive it.

Option A: Quick Setup with the PunchConnect ERPNext App

Install the official PunchConnect ERPNext app for zero-code setup:

After installation:

1. Navigate to PunchConnect Settings β†’ enter your API key and webhook secret
2. Click Test Connection to verify
3. In the PunchConnect dashboard, add your webhook URL:

The app handles everything: HMAC signature verification, duplicate detection, employee mapping, sync logs, and a monitoring dashboard.

bash
# From your bench directory
bench get-app https://github.com/punchconnect/erpnext-punchconnect.git
bench --site your-site.local install-app punchconnect
bench --site your-site.local migrate

Option B: Custom Webhook Receiver (Full Control)

If you prefer building your own receiver, create a whitelisted API method in your custom Frappe app:

Add the URL to your site's site_config.json:

Register the endpoint in PunchConnect's dashboard:

python
# your_app/api.py
import frappe
import hmac
import hashlib
from frappe import _
WEBHOOK_SECRET = frappe.conf.get("punchconnect_webhook_secret", "")
@frappe.whitelist(allow_guest=True)
def punchconnect_webhook():
"""Receive attendance events from PunchConnect."""
# 1. Verify HMAC signature
payload = frappe.request.data
signature = frappe.request.headers.get("X-PunchConnect-Signature", "")
expected = hmac.new(
WEBHOOK_SECRET.encode(),
payload,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
frappe.throw(_("Invalid webhook signature"), frappe.AuthenticationError)
# 2. Parse the event
data = frappe.parse_json(payload)
event_type = data.get("event")
if event_type != "attendance":
# Acknowledge non-attendance events (device_status, heartbeat)
return {"status": "ok", "message": f"Event {event_type} acknowledged"}
# 3. Find the employee by device user ID
device_user_id = str(data.get("user_id"))
employee = frappe.db.get_value(
"Employee",
{"attendance_device_id": device_user_id, "status": "Active"},
["name", "employee_name", "company"],
as_dict=True
)
if not employee:
frappe.log_error(
f"No employee found for device user ID: {device_user_id}",
"PunchConnect Webhook"
)
return {"status": "error", "message": "Employee not found"}
# 4. Check for duplicates (same employee, same timestamp)
timestamp = data.get("timestamp")
existing = frappe.db.exists("Employee Checkin", {
"employee": employee.name,
"time": timestamp
})
if existing:
return {"status": "ok", "message": "Duplicate, skipped"}
# 5. Create Employee Checkin
checkin = frappe.get_doc({
"doctype": "Employee Checkin",
"employee": employee.name,
"employee_name": employee.employee_name,
"time": timestamp,
"device_id": data.get("device_serial"),
"log_type": map_punch_direction(data.get("punch_direction"))
})
checkin.insert(ignore_permissions=True)
frappe.db.commit()
return {
"status": "ok",
"checkin": checkin.name,
"employee": employee.employee_name
}
def map_punch_direction(direction):
"""Map PunchConnect direction to ERPNext log type."""
mapping = {
"IN": "IN",
"OUT": "OUT",
"0": "IN", # Some devices use numeric codes
"1": "OUT",
}
return mapping.get(str(direction).upper(), "")

Step 3: Map Employees to Device User IDs

Every employee who uses a biometric device needs their attendance_device_id set. This field links the biometric user ID stored on the device to the ERPNext Employee record.

Manual Mapping

1. Open the Employee doctype
2. Scroll to Attendance and Leave Details
3. Set Attendance Device ID to match the user ID on the biometric device

Bulk Mapping via Data Import

For organizations with hundreds of employees, use ERPNext's Data Import:

Upload via Settings β†’ Data Import β†’ Employee (update existing records).

csv
name,attendance_device_id
HR-EMP-00001,101
HR-EMP-00002,102
HR-EMP-00003,103

Mapping via API

Automate mapping with a script that pulls users from PunchConnect and matches by name:

python
import requests
import frappe
API_KEY = "your_punchconnect_api_key"
def sync_employee_ids():
"""Pull device users from PunchConnect and map to ERPNext employees."""
response = requests.get(
"https://api.punchconnect.com/v1/users",
headers={"Authorization": f"Bearer {API_KEY}"}
)
device_users = response.json()["data"]
for user in device_users:
# Match by employee name (adjust matching logic as needed)
employee = frappe.db.get_value(
"Employee",
{"employee_name": user["name"], "status": "Active"},
"name"
)
if employee:
frappe.db.set_value(
"Employee", employee,
"attendance_device_id", str(user["user_id"])
)
frappe.db.commit()
print(f"Mapped {len(device_users)} device users to employees")

Step 4: Configure Auto Attendance

ERPNext v14+ includes Auto Attendance β€” it converts Employee Checkin records into Attendance entries automatically. This is the final piece of the pipeline.

Enable Auto Attendance

1. Go to HR Settings β†’ enable Auto Attendance
2. Navigate to Shift Type β†’ create or edit a shift
3. Configure the shift timing:

4. Assign shifts to employees: Go to Shift Assignment β†’ create assignments linking employees to shift types

text
Shift Name: Morning Shift
Start Time: 08:00
End Time: 17:00
Enable Auto Attendance: βœ“
Determine Check-in and Check-out: Alternating
Working Hours Threshold for Half Day: 4
Working Hours Threshold for Absent: 2

How Auto Attendance Processes Checkins

The Auto Attendance job runs periodically (configurable in Scheduled Job Type). It:

1. Picks up Employee Checkin records that haven't been processed
2. Matches them against the employee's assigned Shift Type
3. Calculates working hours (first IN to last OUT)
4. Creates an Attendance record with status: Present, Half Day, or Absent
5. Marks the checkins as processed

No manual intervention at any step. Device β†’ Cloud β†’ ERPNext β†’ Attendance β€” fully automated.

python
# The full pipeline in action:
#
# 08:02 β†’ Device captures fingerprint (User ID: 101)
# 08:02 β†’ PunchConnect receives punch
# 08:03 β†’ Webhook fires to ERPNext (1-3 second delay)
# 08:03 β†’ Employee Checkin created (HR-EMP-00001, IN, 08:02)
# ...
# 17:05 β†’ Same employee punches OUT
# 17:05 β†’ Employee Checkin created (HR-EMP-00001, OUT, 17:05)
# ...
# Auto Attendance runs β†’ Attendance record: Present, 9h 3m worked

Step 5: Monitor and Troubleshoot

Using the PunchConnect ERPNext Dashboard

If you installed the official app, navigate to /punchconnect-dashboard for a real-time overview:

- Device count: total, online, offline
- Today's checkins: running count
- Sync success/failure rate: last 24 hours
- Recent activity log: every webhook processed

Using PunchConnect Sync Logs

Every webhook event is recorded in the PunchConnect Sync Log doctype. Filter by status to find failures:

python
# Find failed syncs in the last 24 hours
failed = frappe.get_all(
"PunchConnect Sync Log",
filters={
"status": "Failed",
"creation": [">", frappe.utils.add_days(frappe.utils.now(), -1)]
},
fields=["name", "error_message", "device_serial", "raw_payload"]
)
for log in failed:
print(f"Device {log.device_serial}: {log.error_message}")

Common Issues and Fixes

Employee Checkin not created?
- Verify the employee's attendance_device_id matches the device user ID
- Check PunchConnect Sync Logs for the error message
- Enable "Log Raw Payloads" in PunchConnect Settings for debugging

Webhook returning 401 or 403?
- Confirm the webhook secret matches in both PunchConnect and ERPNext
- Check that the API method is whitelisted with allow_guest=True
- Verify your SSL certificate is valid (webhooks require HTTPS)

Auto Attendance not creating records?
- Confirm Auto Attendance is enabled in HR Settings
- Verify employees have Shift Assignments for the correct date range
- Check the Scheduled Job Type for process_auto_attendance β€” it must be active

Duplicate checkins?
- The PunchConnect app includes built-in duplicate detection
- If using a custom receiver, check for existing records before inserting (see Step 2 code)

Deployment Models: Which Setup Fits You?

Frappe Cloud

You cannot install pyzk on Frappe Cloud servers. PunchConnect webhooks are the only viable path for biometric integration. The webhook receiver runs as a standard Frappe API endpoint β€” no special server access required.

Self-Hosted / Docker

You could use pyzk, but PunchConnect is faster to deploy and more reliable. No cron jobs to maintain, no device IP tracking, no firmware compatibility issues. One webhook endpoint handles all devices.

Multi-Site Organizations

This is where PunchConnect delivers the most value. 100 devices across 20 offices? One PunchConnect account, one webhook endpoint, one ERPNext instance. No per-site infrastructure. No VPN tunnels. No per-location sync scripts.

Currently powering attendance for 24,000+ active employees across 50+ sites in production. Read the AgriWise case study for a real-world deployment at scale.

Security Considerations

Biometric attendance data flows through three points: device, PunchConnect cloud, and your ERPNext instance. Here's how each leg is secured:

Device β†’ PunchConnect: TLS-encrypted connection. Devices authenticate with their registered serial number.

PunchConnect β†’ ERPNext: HTTPS with HMAC-SHA256 signature verification. Every webhook includes an X-PunchConnect-Signature header. Your receiver validates this before processing (see the code in Step 2).

Data at rest: PunchConnect processes attendance events in real time and forwards them. It does not store biometric templates (fingerprints, face data). Only metadata β€” user ID, timestamp, device serial β€” passes through the API.

For a deeper dive into securing biometric data pipelines, read our guide on securing biometric data in transit.

Pricing

$200 per device β€” one-time license. No monthly fees. No per-employee charges.

Compare that to the hidden cost of pyzk infrastructure: a sync server per site, VPN configuration, developer time for firmware debugging, and ongoing maintenance. For most organizations, PunchConnect pays for itself in the first month of reduced DevOps overhead.

Start your free 7-day trial β€” no credit card required.

Frequently Asked Questions

Does biometric attendance for ERPNext work with Frappe Cloud?
Yes. PunchConnect sends data via webhook to a standard Frappe API endpoint. No software installation on Frappe Cloud servers is required.

Which ZKTeco devices are supported?
Any model with cloud connectivity: SpeedFace V5L, ProFace X, uFace 800, MB460, iClock 680, K-series, and more. Contact us to verify your specific model.

How fast does attendance data reach ERPNext?
1–3 seconds via webhook. Compare that to 10–30 minutes with pyzk cron-based polling.

Can I run PunchConnect alongside the existing Frappe sync tool?
Yes. Both create Employee Checkin documents. Run them in parallel during migration, then disable the cron-based script once you verify webhook delivery.

What happens if ERPNext is temporarily down?
PunchConnect retries failed webhook deliveries with exponential backoff. No attendance data is lost during brief outages.

Do I need a static IP for my ERPNext server?
No. You need a domain name with a valid SSL certificate. PunchConnect sends webhooks to your URL β€” DNS handles the rest.

How does this work with ERPNext's permission model?
The webhook receiver uses allow_guest=True because PunchConnect authenticates via HMAC signature, not ERPNext user sessions. The Employee Checkin is created with ignore_permissions=True by the server-side handler.

Can I track which verification method was used (fingerprint vs. face vs. card)?
Yes. The webhook payload includes a verify_mode field. You can store this in a custom field on Employee Checkin for audit purposes.

---

Ready to connect your biometric devices to ERPNext? Start your free 7-day trial β€” register your first device, configure the webhook, and see real-time Employee Checkins in ERPNext within 30 minutes. No credit card. No pyzk. No local infrastructure.

Questions? Contact us β€” we've integrated with every ERPNext deployment model and every ZKTeco firmware variant.

Related articles

Biometric Attendance for ERPNext: Complete Integration Guide | PunchConnect