1. Project Overview
Anviz Local Web API is a Python wrapper around libtc-b_new_sdk.so that exposes HTTPS REST APIs. Clients send command and payload through the unified /api/commands/execute endpoint. The server calls the SDK to communicate with attendance devices and returns JSON.
Core Files
app.py: HTTP/HTTPS service, JWT authentication, and command dispatching.sdk_wrapper.py: SDK shared library wrapper, structure parsing, and device command implementation.run_service.sh: Startup script for local or container execution.crosschex-webapi.env: Runtime configuration file.test_api.sh: API test script with automatic login and JWT retrieval.
Default Behavior
- Listens on
0.0.0.0:5011by default. - Enables HTTPS by default. Self-signed certificates are stored in
certs/. - Enables JWT by default. command APIs require a Bearer Token.
- Requests for the same
device_idare queued. Differentdevice_idvalues can be processed in parallel.
2. Docker Environment Setup
If Docker is already installed on the system, skip this chapter. The following steps use Ubuntu 22.04/24.04 as examples. In production, install Docker Engine and the Compose plugin from the official Docker APT repository. Official documentation:
2.1 Install Docker Engine
# 1) Install base dependencies
sudo apt update
sudo apt install -y ca-certificates curl
# 2) Remove old packages that may conflict. Ignore errors if Docker was never installed
sudo apt remove -y docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc
# 3) Add the official Docker GPG key
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# 4) Add the Docker APT repository
sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
# 5) Install Docker Engine and the Compose plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 6) Start Docker and enable it at boot
sudo systemctl enable --now docker
sudo systemctl status docker
2.2 Verify Docker
sudo docker run hello-world
2.3 Allow the Current User to Run Docker
sudo usermod -aG docker $USER
newgrp docker
# Verify that docker commands work without sudo
docker ps
docker ps still reports insufficient permissions, log out of the current shell or restart the system and try again.3. Configuration File
crosschex-webapi.env is the service runtime configuration file. run_service.sh loads it automatically.
JWT_SECRET and AUTH_PASSWORD, and you should replace the HTTPS certificate with a production certificate.| Setting | Default / Example | Description |
|---|---|---|
HOST | 0.0.0.0 | Web API listen address. |
PORT | 5011 | Web API listen port. |
SDK_LIBRARY | /app/libtc-b_new_sdk.so | CrossChex SDK shared library path. In containers, this path must match the image or volume mount. |
HTTPS_ENABLED | 1 | Whether HTTPS is enabled. Keep it enabled in production. |
SSL_CERT_FILE | /app/certs/server.crt | HTTPS certificate path. |
SSL_KEY_FILE | /app/certs/server.key | HTTPS private key path. |
AUTH_ENABLED | 1 | Whether JWT authentication is enabled. Keep it enabled in production. |
JWT_SECRET | openssl rand -hex 32 | JWT signing secret. Use a strong random string. |
JWT_EXPIRES_SECONDS | 3600 | Token lifetime in seconds. |
AUTH_PASSWORD | Custom strong password | Authentication password. Change it in production. |
WEBHOOK_ENABLED | 0 | Set to 1 to enable realtime attendance record forwarding. |
WEBHOOK_URL | Empty | HTTP/HTTPS endpoint that receives command 100 JSON. Leave empty to disable forwarding. |
WEBHOOK_TOKEN | Empty | Optional Bearer Token. Leave empty to omit the Authorization header. |
Generate JWT Secret
openssl rand -hex 32
Generate a Self-Signed Certificate for Testing Only
mkdir -p certs
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout certs/server.key \
-out certs/server.crt \
-days 3650 \
-subj "/CN=127.0.0.1"
curl -k or importing the certificate into the client trust store.4. Docker Deployment
4.1 Recommended Directory Layout
/webapi/
├── sdk/
│ ├── certs/
│ │ ├── server.crt
│ │ └── server.key
│ ├── demo/
│ │ └── index.html
│ ├── app.py
│ ├── crosschex-webapi.env
│ ├── libtc-b_new_sdk.so
│ ├── run_http.sh
│ ├── run_service.sh
│ ├── sdk_wrapper.py
│ └── test_api.sh
├── crosschex-webapi-usage.html
└── crosschex-webapi.tar
4.2 Load Image
docker load -i /webapi/crosschex-webapi.tar
4.3 Start with docker run
# Ubuntu
docker run -it --name crosschex-webapi --network host -v /webapi/sdk:/app crosschex-webapi:latest
# Mac/Windows
docker run -it --name crosschex-webapi -p 5010:5010 -p 5011:5011 -p 8080:8080 -p 5060:5060/udp -v d:/webapi/sdk:/app crosschex-webapi:latest
4.4 Check Container Runtime
docker ps --filter name=crosschex-webapi
docker logs -f crosschex-webapi
curl -k https://127.0.0.1:5011/healthz
5. JWT Authentication
5.1 Get Token
curl -k -X POST https://127.0.0.1:5011/api/auth/token \
-H 'Content-Type: application/json' \
-d '{"password":"change-this-password"}'
Successful response:
{
"code": 200,
"token_type": "Bearer",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600
}
5.2 Call the command API
TOKEN=$(curl -k -sS -X POST https://127.0.0.1:5011/api/auth/token \
-H 'Content-Type: application/json' \
-d '{"password":"change-this-password"}' \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')
curl -k -X POST https://127.0.0.1:5011/api/commands/execute \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"command":23,"payload":{}}'
5.3 Authentication Errors
| HTTP Status | Scenario | Response Example |
|---|---|---|
401 | Missing token, invalid token format, invalid signature, or expired token. | {"code":401,"message":"Token expired."} |
6. API Usage Specification
6.1 Health Check
The health check does not require JWT.
GET /healthz
curl -k https://127.0.0.1:5011/healthz
6.2 Unified Command Endpoint
POST /api/commands/execute
Content-Type: application/json
Authorization: Bearer <token>
{
"command": 23,
"payload": {}
}
All business commands use command to identify the operation and payload to carry command parameters.
6.3 Common Response Structure
{
"code": 200,
"command": 23,
"data": { ... },
"records": [ ... ],
"count": 1
}
Not every API returns records and count. When there are no records, some APIs only return data.totalcnt=0.
6.4 Common Business Error Codes
| code | HTTP Status | Description |
|---|---|---|
400 | Bad Request | Invalid request JSON, missing parameters, or invalid parameter range. |
401 | Unauthorized | Authentication failed. |
404 | Not Found | Device not connected, API not found, or no event was received while waiting. |
502 | Bad Gateway | SDK call failed immediately. |
504 | Gateway Timeout | Timed out waiting for the device response. |
7. Command List
| command | Name | Main payload parameters | Description |
|---|---|---|---|
| 1 | Connect Device | device_id, device_ip, device_port | Call the SDK to connect to a device and return device ID, type, version, IP, and dev_idx. |
| 2 | Read New Records | device_id | Read record status first. If NewRecNum=0, return totalcnt=0 immediately. |
| 3 | Disconnect Device | device_id | Disconnect a connected device. |
| 4 | Read All Records | device_id | Read record status first. If TotalRecNum=0, return totalcnt=0 immediately. |
| 5 | Read Record Status | device_id | Return employee, fingerprint, password, card, total record, and new record counts. |
| 6 | Clear Records | device_id | Delete or clear device records. |
| 7 | Read Person Info | device_id | Read record status first. If EmployeeNum=0, return totalcnt=0 immediately. |
| 8 | Add/Modify Person | device_id, userid, password, card_id, username, etc. | Write basic person information. |
| 9 | Delete Person | device_id, userid, operation | Delete a person or person-related data. |
| 10 | Read Device Time | device_id | Return the current device time. |
| 11 | Set Device Time | device_id, year, month, day, hour, minute, second | Set the device time. |
| 12 | Initialize User Area | device_id | Clear user-related areas. |
| 13 | Initialize System | device_id | Initialize the device system. This is a high-risk operation. |
| 14 | Force Unlock | device_id | Trigger device force unlock. |
| 15 | Read SN | device_id | Read the device serial number. |
| 16 | Download Fingerprint Template | device_id, userid, templateid | Read a user fingerprint template. |
| 17 | Upload Fingerprint Template | device_id, userid, templateid, template, template_len | Write a user fingerprint template. |
| 18 | Download Face Image Template | device_id, userid | Read a face image template. |
| 19 | Upload Face Image Template | device_id, userid, template | Write a face image template. |
| 20 | Enroll Fingerprint Online | device_id, userid, templateid | Trigger online fingerprint enrollment on the device. |
| 21 | Enroll Face Online | device_id, userid | Trigger online face image enrollment on the device. |
| 22 | Read Records by User and Time | device_id, userid, start_date, end_date | Read the matching record count first and return immediately if it is 0. |
| 23 | List Connected Devices | None | Return the list of connected devices in the current service. records is always an array. |
| 24 | Read Period Settings | device_id, timeid | timeid range is 1..32. Returns start1/end1 through start7/end7. |
| 25 | Set Period Settings | device_id, timeid, start1..end7 | Time format is HH:mm. |
| 26 | Read Group Settings | device_id, teamid | teamid must be in the range 2..16. Returns timeid1..timeid4. |
| 27 | Set Group Settings | device_id, teamid, timeid1..timeid4 | teamid must be in the range 2..16. |
| 28 | Read Attendance Status | device_id | Return fp_len and status1..status8. |
| 29 | Set Attendance Status | device_id, status1..status8 | The status field length depends on the device type. |
| 30 | Search Devices | None | Search LAN devices over UDP and return IP, MAC, port, version, and related information. |
Request and Response JSON Examples
The following examples show successful responses. Actual values may vary by device model, firmware version, and data content. Field structure follows the current API implementation.
command=1 Connect Device
Request JSON
{
"command": 1,
"payload": {
"device_id": 1,
"device_ip": "192.168.0.144",
"device_port": 5010
}
}Response JSON
{
"code": 200,
"command": 1,
"data": {
"device_id": 1,
"device_type": "FDEEP3M",
"device_typeflag": 45154820,
"device_ip": "192.168.0.144:5010",
"device_version": "03.74.D6",
"device_idx": 1
}
}command=2 Read New Records
Request JSON
{
"command": 2,
"payload": {
"device_id": 1
}
}Response JSON
{
"code": 200,
"command": 2,
"data": {
"device_id": 1,
"totalcnt": 1
},
"records": [
{
"userid": 1,
"checktime": "2026-07-27 09:30:00",
"backid": 16,
"rectype": 128, //highest bit 1 means door opened, for example 128: opened+IN, 129: opened+OUT, 0: IN, 1: OUT
"curidx": 1
}
],
"count": 1
}command=3 Disconnect Device
Request JSON
{
"command": 3,
"payload": {
"device_id": 1
}
}Response JSON
{
"code": 200,
"command": 3,
"data": {
"device_id": 1,
"device_type": "FDEEP3M",
"device_typeflag": 45154820,
"device_ip": "192.168.0.144:5010",
"device_version": "03.74.D6",
"device_idx": 1,
"live": 0
}
}command=4 Read All Records
Request JSON
{
"command": 4,
"payload": {
"device_id": 1
}
}Response JSON
{
"code": 200,
"command": 4,
"data": {
"device_id": 1,
"totalcnt": 1
},
"records": [
{
"userid": 1,
"checktime": "2026-07-27 09:30:00",
"backid": 16,
"rectype": 128,
"curidx": 1
}
],
"count": 1
}command=5 Read Record Status
Request JSON
{
"command": 5,
"payload": {
"device_id": 1
}
}Response JSON
{
"code": 200,
"command": 5,
"data": {
"device_id": 1,
"employee_count": 10,
"template_count": 8,
"password_count": 6,
"card_count": 9,
"total_record_count": 100,
"new_record_count": 3
}
}command=6 Clear Records
Request JSON
{
"command": 6,
"payload": {
"device_id": 1
}
}Response JSON
{
"code": 200,
"command": 6,
"data": {
"device_id": 1,
"result": 0, //0: success, non-zero: failure
"deleted_count": 100
}
}command=7 Read Person Info
Request JSON
{
"command": 7,
"payload": {
"device_id": 1
}
}Response JSON
{
"code": 200,
"command": 7,
"data": {
"device_id": 1,
"totalcnt": 1
},
"records": [
{
"curidx": 1,
"userid": 1,
"password": "1",
"card_id": 12732605,
"username": "Jack",
"deptid": 0,
"groupid": 1,
"mode": 0,
"fp_status": 1024, //template flags, bit0: fingerprint 1 ... bit9: fingerprint 10, bit10: face, for example 1024: face, 3: fingerprint 1 + fingerprint 2
"special": 64, //192/208: administrator
"start_date": "2026-07-01",
"end_date": "2031-07-06",
"scheduling_id": 0,
"start_scheduling_time": "00:00",
"end_scheduling_time": "00:00"
}
],
"count": 1
}command=8 Add/Modify Person
Request JSON
{
"command": 8,
"payload": {
"device_id": 1,
"userid": 1,
"password": "1",
"card_id": 12732605,
"username": "Jack",
"deptid": 0,
"groupid": 1,
"mode": 0,
"special": 64,
"start_date": "2026-07-01",
"end_date": "2031-07-06",
"scheduling_id": 0,
"start_scheduling_time": "00:00",
"end_scheduling_time": "00:00"
}
}Response JSON
{
"code": 200,
"command": 8,
"data": {
"device_id": 1,
"result": 0, //0: success, non-zero: failure
"userid": 1
}
}command=9 Delete Person
Request JSON
{
"command": 9,
"payload": {
"device_id": 1,
"userid": 1,
"operation": 255 //255: delete person data and templates, 3: delete template, 4: delete password, 8: delete card
}
}Response JSON
{
"code": 200,
"command": 9,
"data": {
"device_id": 1,
"result": 0, //0: success, non-zero: failure
"userid": 1
}
}command=10 Read Device Time
Request JSON
{
"command": 10,
"payload": {
"device_id": 1
}
}Response JSON
{
"code": 200,
"command": 10,
"data": {
"device_id": 1,
"result": 0, //0: success, non-zero: failure
"year": 2026,
"month": 7,
"day": 27,
"hour": 10,
"minute": 30,
"second": 20,
"datetime": "2026-07-27 10:30:20"
}
}command=11 Set Device Time
Request JSON
{
"command": 11,
"payload": {
"device_id": 1,
"year": 2026,
"month": 7,
"day": 27,
"hour": 10,
"minute": 30,
"second": 20
}
}Response JSON
{
"code": 200,
"command": 11,
"data": {
"device_id": 1,
"result": 0 //0: success, non-zero: failure
}
}command=12 Initialize User Area
Request JSON
{
"command": 12,
"payload": {
"device_id": 1
}
}Response JSON
{
"code": 200,
"command": 12,
"data": {
"device_id": 1,
"result": 0 //0: success, non-zero: failure
}
}command=13 Initialize System
Request JSON
{
"command": 13,
"payload": {
"device_id": 1
}
}Response JSON
{
"code": 200,
"command": 13,
"data": {
"device_id": 1,
"result": 0 //0: success, non-zero: failure
}
}command=14 Force Unlock
Request JSON
{
"command": 14,
"payload": {
"device_id": 1
}
}Response JSON
{
"code": 200,
"command": 14,
"data": {
"device_id": 1,
"result": 0 //0: success, non-zero: failure
}
}command=15 Read SN
Request JSON
{
"command": 15,
"payload": {
"device_id": 1
}
}Response JSON
{
"code": 200,
"command": 15,
"data": {
"device_id": 1,
"result": 0, //0: success, non-zero: failure
"sn": "1750100022370065"
}
}command=16 Download Fingerprint Template
Request JSON
{
"command": 16,
"payload": {
"device_id": 1,
"userid": 1,
"templateid": 1
}
}Response JSON
{
"code": 200,
"command": 16,
"data": {
"device_id": 1,
"result": 0, //0: success, non-zero: failure
"userid": 1,
"templateid": 1,
"template": "AQIDBA==" //base64
}
}command=17 Upload Fingerprint Template
Request JSON
{
"command": 17,
"payload": {
"device_id": 1,
"userid": 1,
"templateid": 1,
"template": "AQIDBA==" //base64
}
}Response JSON
{
"code": 200,
"command": 17,
"data": {
"device_id": 1,
"result": 0, //0: success, non-zero: failure
"userid": 1,
"templateid": 1
}
}command=18 Download Face Image Template
Request JSON
{
"command": 18,
"payload": {
"device_id": 1,
"userid": 1
}
}Response JSON
{
"code": 200,
"command": 18,
"data": {
"device_id": 1,
"result": 0, //0: success, non-zero: failure
"userid": 1,
"template": "AQIDBA==" //base64
}
}command=19 Upload Face Image Template
Request JSON
{
"command": 19,
"payload": {
"device_id": 1,
"userid": 1,
"template": "AQIDBA==" //base64
}
}Response JSON
{
"code": 200,
"command": 19,
"data": {
"device_id": 1,
"result": 0, //0: success, non-zero: failure
"userid": 1
}
}command=20 Enroll Fingerprint Online
Request JSON
{
"command": 20,
"payload": {
"device_id": 1,
"userid": 123,
"templateid": 1
}
}Response JSON
{
"code": 200,
"command": 20,
"data": {
"device_id": 1,
"result": 0, //0: success, non-zero: failure
"userid": 123,
"templateid": 1,
"template": "AQIDBA==" //base64
}
}command=21 Enroll Face Online
Request JSON
{
"command": 21,
"payload": {
"device_id": 1,
"userid": 123
}
}Response JSON
{
"code": 200,
"command": 21,
"data": {
"device_id": 1,
"result": 0, //0: success, non-zero: failure
"userid": 123,
"template": "AQIDBA==" //base64
}
}command=22 Read Records by User and Time
Request JSON
{
"command": 22,
"payload": {
"device_id": 1,
"userid": 1,
"start_date": "2026-07-01",
"end_date": "2026-07-27"
}
}Response JSON
{
"code": 200,
"command": 22,
"data": {
"device_id": 1,
"totalcnt": 1
},
"records": [
{
"userid": 1,
"checktime": "2026-07-27 09:30:00",
"backid": 16,
"rectype": 128,
"curidx": 1
}
],
"count": 1
}command=23 List Connected Devices
Request JSON
{
"command": 23,
"payload": {}
}Response JSON
{
"code": 200,
"command": 23,
"data": {
"totalcnt": 1
},
"records": [
{
"device_id": 1,
"device_type": "FDEEP3M",
"device_typeflag": 45154820,
"device_ip": "192.168.0.144:5010",
"device_version": "03.74.D6",
"device_idx": 1
}
],
"count": 1
}command=24 Read Period Settings
Request JSON
{
"command": 24,
"payload": {
"device_id": 1,
"timeid": 1
}
}Response JSON
{
"code": 200,
"command": 24,
"data": {
"device_id": 1,
"result": 0,
"timeid": 1, //1-24: normal periods, 25-28: normally closed periods, 29-32: normally open periods
"start1": "07:00", //Monday start time
"end1": "19:00", //Monday end time
"start2": "08:00",
"end2": "20:00",
"start3": "00:00",
"end3": "00:00",
"start4": "00:00",
"end4": "00:00",
"start5": "00:00",
"end5": "00:00",
"start6": "00:00",
"end6": "00:00",
"start7": "00:00", //Sunday start time
"end7": "00:00" //Sunday end time
}
}command=25 Set Period Settings
Request JSON
{
"command": 25,
"payload": {
"device_id": 1,
"timeid": 1, //1-24: normal periods, 25-28: normally closed periods, 29-32: normally open periods
"start1": "07:00", //Monday start time
"end1": "19:00", //Monday end time
"start2": "08:00",
"end2": "20:00",
"start3": "00:00",
"end3": "00:00",
"start4": "00:00",
"end4": "00:00",
"start5": "00:00",
"end5": "00:00",
"start6": "00:00",
"end6": "00:00",
"start7": "00:00", //Sunday start time
"end7": "00:00" //Sunday end time
}
}Response JSON
{
"code": 200,
"command": 25,
"data": {
"device_id": 1,
"result": 0,
"timeid": 1
}
}command=26 Read Group Settings
Request JSON
{
"command": 26,
"payload": {
"device_id": 1,
"teamid": 2 //2-16
}
}Response JSON
{
"code": 200,
"command": 26,
"data": {
"device_id": 1,
"result": 0,
"teamid": 2,
"timeid1": 1,
"timeid2": 2,
"timeid3": 0,
"timeid4": 0
}
}command=27 Set Group Settings
Request JSON
{
"command": 27,
"payload": {
"device_id": 1,
"teamid": 2, //2-16
"timeid1": 1, //0-24
"timeid2": 2, //0-24
"timeid3": 0, //0-24
"timeid4": 0 //0-24
}
}Response JSON
{
"code": 200,
"command": 27,
"data": {
"device_id": 1,
"result": 0,
"teamid": 2
}
}command=28 Read Attendance Status
Request JSON
{
"command": 28,
"payload": {
"device_id": 1
}
}Response JSON
{
"code": 200,
"command": 28,
"data": {
"device_id": 1,
"result": 0,
"fp_len": 160,
"status1": "IN",
"status2": "OUT",
"status3": "BREAK",
"status4": "RETURN",
"status5": "",
"status6": "",
"status7": "",
"status8": ""
}
}command=29 Set Attendance Status
Request JSON
{
"command": 29,
"payload": {
"device_id": 1,
"status1": "IN",
"status2": "OUT",
"status3": "BREAK",
"status4": "RETURN",
"status5": "",
"status6": "",
"status7": "",
"status8": ""
}
}Response JSON
{
"code": 200,
"command": 29,
"data": {
"device_id": 1,
"result": 0
}
}command=30 Search Devices
Request JSON
{
"command": 30,
"payload": {}
}Response JSON
{
"code": 200,
"command": 30,
"data": {
"totalcnt": 1
},
"records": [
{
"devtype": "FDEEP3M",
"devsn": "1750100022370065",
"devid": 1,
"ipaddr": "192.168.0.144",
"ipmask": "255.255.255.0",
"gwaddr": "192.168.0.1",
"macaddr": "00:22:ca:8a:fd:1f",
"servaddr": "192.168.0.159",
"port": 5010,
"netmode": 1,
"version": "03.74.D6"
}
],
"count": 1
}8. Using test_api.sh
test_api.sh accesses https://127.0.0.1:5011 by default and uses -k to skip self-signed certificate verification. Except for health, business commands automatically log in and include JWT.
# Health check, no token required
./test_api.sh health
# Test the login endpoint only
./test_api.sh login
# List connected devices; the script logs in automatically
./test_api.sh connected
# Connect device
./test_api.sh connect 192.168.0.144 5010 1
# Search devices
./test_api.sh searchdev
Common Environment Variables
BASE_URL=https://192.168.0.159:5011 \
AUTH_PASSWORD='your-password' \
DEVICE_ID=1 \
DEVICE_IP=192.168.0.144 \
./test_api.sh connect
Reuse an Existing Token
API_TOKEN='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' ./test_api.sh connected
9. Using the Web API Demo
9.1 Start the demo HTTP server
docker exec -it crosschex-webapi /app/run_http.sh
9.2 Open the Web API Demo page
Open healthz first to remove browser network blocking. Then enter the correct API Base URL and password. The default password is CrossChex, but use the actual configured AUTH_PASSWORD.10. Troubleshooting
| Symptom | Cause | Resolution |
|---|---|---|
AUTH_ENABLED=1 but JWT_SECRET is empty | crosschex-webapi.env was not loaded or JWT_SECRET is not configured. | Confirm that run_service.sh loads the env file, or set ENV_FILE. Configure a strong random JWT_SECRET. |
Missing Authorization Bearer token | The command API was called without JWT. | Call /api/auth/token first, then include Authorization: Bearer <token>. |
Token expired | JWT exceeded JWT_EXPIRES_SECONDS. | Log in again to get a new token. |
SSL cert file not found | Certificate path is wrong or certs is not mounted in the container. | Check SSL_CERT_FILE, SSL_KEY_FILE, and volume mounts. |
Timed out while waiting for device response | Device does not respond, network is unreachable, port is unreachable, or the event does not match. | Check device IP, port 5010, container host network, device connection state, and SDK logs. |
| UDP search cannot find devices | Container network isolation restricts UDP broadcast. | Use --network host or network_mode: host. |
| HTTPS curl certificate error | A self-signed certificate is used. | Use -k during testing. Use a trusted certificate in production. |
View Logs
# Docker run
docker logs -f crosschex-webapi
# With systemd deployment
journalctl -u crosschex-webapi -f