Protocol & opcode catalog¶
openzk speaks the ZKTeco standalone SDK protocol over TCP port 4370. This
page documents what was learned by reverse-engineering the official SDK for
interoperability: the wire framing, the connection handshake, the bulk
table-read mechanism, and the per-command opcodes.
Provenance & disclaimer
Opcodes below are factual numeric values recovered by disassembling the official SDK's exported functions and reading the opcode immediate each one sends. No proprietary code or binary is included or redistributed. This project is not affiliated with ZKTeco — see the Disclaimer. Behaviour is firmware-dependent; treat "supported?" notes as observations, not guarantees.
Wire framing¶
Each packet is:
MAGIC(4 bytes) | length(4, LE) | payload
payload = command(2, LE) | checksum(2) | session_id(2, LE) | reply_id(2, LE) | data
MAGICis the fixed 4-byte prefix50 50 82 7d.- The checksum and the
reply_idincrement/wrap behaviour are pinned by known-answer test vectors in the test suite, so the bytes are provably correct. - Multi-packet responses are read frame by frame (no fixed-size read that could truncate a reply).
See framing for the pure implementation.
Connection handshake¶
| Step | Command | Code | Notes |
|---|---|---|---|
| Connect | CMD_CONNECT |
1000 |
Opens a session; device returns the session id. |
| Auth | CMD_AUTH |
1102 |
Sent if the device replies ACK_UNAUTH (2005); payload is the scrambled comm key. |
| Handshake | option write | 12 |
Sets the modern SDK session flag (saved & restored on close). |
| Exit | CMD_EXIT |
1001 |
Closes the session. |
Options are read with CMD_OPTIONS_RRQ (11) and written with
CMD_OPTIONS_WRQ (12). Capability flags read at connect include FaceFunOn,
FingerFunOn, PhotoFunOn, ~ZKFPVersion, FaceVersion, and the user
extended-format flag.
Bulk table reads (buffered)¶
Large tables are read via a prepare/read-chunks flow:
| Command | Code | Role |
|---|---|---|
CMD_PREPARE_BUFFER |
1503 |
Announce a buffered read; device replies with the total size. |
CMD_READ_BUFFER |
1504 |
Read a chunk at (offset, size). |
CMD_FREE_DATA |
1502 |
Release the buffer when done. |
The table is selected with CMD_DB_RRQ (7) and an FCT (function/table) id:
| FCT | Table | openzk method |
|---|---|---|
1 |
Attendance log | attendance() |
2 |
Fingerprint templates | fingerprint_templates() |
4 |
Operation log | op_logs() |
5 |
Users | users() |
Per-command opcodes¶
Recovered from the official SDK. "openzk" shows whether the v1 (read-only) client exposes it.
| Function | Opcode | Category | What it does | openzk |
|---|---|---|---|---|
DownloadUserPhoto |
0x271A |
user photo | Fetch a user's stored portrait (JPEG). | ✅ user_photo() |
GetUserFace |
0x0096 |
biometric | Fetch a user's face template (feature vector). | ✅ face_templates() |
GetUserTmpEx |
0x0058 |
biometric | Fetch a user's (extended) fingerprint template. | ◐ via table read |
GetUserInfoEx |
0x0050 |
user | Extended user info record. | ◐ (table read used) |
GetUserGroup |
0x0015 |
user | User's access group. | ✗ |
GetUserTZs |
0x0017 |
user | User's time zones. | ✗ |
GetUserIDCard |
0x2744 |
user | User's RFID card data. | ✗ |
GetUserVD / GetUserVerifyStyle |
0x2724 / 0x0050 |
user | Per-user verify settings. | ✗ |
GetDeviceInfo |
0x000B |
device | Device info / parameters. | ◐ (options + sizes) |
GetPhotoCount |
0x07DD |
capture photo | Count of attendance-capture photos. | ✗ |
GetPhotoByName |
0x07DE |
capture photo | Fetch a capture photo by filename. | ✗ |
GetPhotoNamesByTime |
0x07E0 |
capture photo | List capture-photo names in a time range. | ✗ |
GetPicList |
0x271D |
picture | List names in the "picture" store. | ✗ |
GetCapturePicByList |
0x271C |
picture | Fetch capture pictures by list. | ✗ |
DownloadPicture |
0x272D |
picture | Download a named picture. | ✗ |
UploadUserPhoto |
0x2719 |
write | Upload a user portrait. | ✗ (out of scope) |
UploadPicture |
0x272B |
write | Upload a picture. | ✗ (out of scope) |
UploadTheme |
0x272A |
write | Upload a UI theme. | ✗ (out of scope) |
◐ = the data is reachable through a different mechanism openzk already uses (e.g. the buffered table read), so a dedicated wrapper isn't exposed. ✗ on the read commands = catalogued but not wrapped in v1; ✗ on writes = out of scope by design (openzk is read-only).
Response / status codes¶
| Code | Meaning |
|---|---|
0x07D0 |
ACK_OK — success (or "no data" for some file reads). |
0x05DC |
buffer/content header (carries the payload size). |
0x05DD |
buffer/content data (the actual bytes, e.g. a JPEG). |
0x1379 |
"no photo for this user". |
0x1375 |
command not supported on this firmware. |
0x07D1 |
error / NAK. |
0xFFFF |
unsupported / invalid. |
A note on enrollment "biophotos"¶
On some firmware, the visible-light face enrollment photo shown at check-in is
delivered only through the device's PUSH/ADMS channel (device-to-server
HTTP), not the pull SDK on port 4370 — even the official SDK's pull commands do
not expose it there. openzk is a pull-SDK client, so it surfaces the stored
user portrait (DownloadUserPhoto) and face templates (GetUserFace),
but not those push-only enrollment frames. This is a firmware design fact, noted
here for interoperability.