This guide provides step-by-step instructions for integrating with Schlage ENGAGE devices using Gateway IP Mode (410-IP). In this mode, ENGAGE locks communicate with a Gateway device over Bluetooth Low Energy (BLE), and the Gateway communicates with your access control system via IP/Ethernet connectivity.
[Access Control System] <---> [ENGAGE Gateway] <---> [ENGAGE Locks]
(IP/Ethernet) (BLE)
410-IP Mode means:
For detailed architecture information, see ENGAGE_SAM_API_Integration_2.37.pdf.
In the ENGAGE ecosystem, a site is the fundamental container for ENGAGE devices and the essential starting point for any deployment. Each site is an abstract container that includes:
Key characteristics of a site:
ENGAGE supports two types of sites with fundamentally different management models:
IMPORTANT FOR INTEGRATORS: If you’re building a PACS integration, you MUST create unmanaged sites.
The site is the fundamental security unit in ENGAGE:
CRITICAL: Once a site is created, its site key cannot be changed. It is very important not to lose the key — without it, you cannot create new credentials for devices in that site.
Unmanaged sites must be created using the ENGAGE cloud API. They cannot be created via the mobile app or web portal.
Prerequisites:
API Endpoint: POST https://partner.lockwebserv.com/engage/sites/register
Authentication: HTTP Basic Auth with your ENGAGE account credentials
Request Body:
{
"SiteName": "Main Office Building",
"Base64Key": "ABEiM0RVZneImaq7zN3u/wARIjNEVWZ3iJmqu8zd7v8=",
"IsEngageManaged": "False",
"SiteSoftwareKey": "YourCompanyName"
}
Field Descriptions:
| Field | Description | Required | Example |
|---|---|---|---|
SiteName | Human-readable name for the site | Yes | ”Main Office Building” |
Base64Key | The site encryption key encoded as Base64 (32 bytes = 44 chars) | Yes | ”ABEiM0RVZneImaq7zN3u/w…” |
IsEngageManaged | Must be “False” for PACS integrations | Yes | ”False” |
SiteSoftwareKey | Your company/software identifier - must be preregistered with Allegion | Yes | ”YourCompanyName” |
IMPORTANT - SiteSoftwareKey Registration:
The SiteSoftwareKey value must be preregistered with Allegion before you can create sites. This identifier is used for SAM (Software Asset Management) metadata and tracking.
To verify your company is registered:
Query the registered software keys endpoint:
GET https://partner.lockwebserv.com/engage/lookup/sitesoftware
Authorization: Basic <base64(your-email@company.com:your-password)>
Response:
{
"siteSoftwareKeys": [
"CompanyA",
"CompanyB_PACS",
"AcmeAccessControl",
"YourCompanyName"
]
}
If your company is not in the list:
SiteSoftwareKey valueBest practices:
Response:
{
"SiteReference": "886182ed-0395-4236-8dcf-50cc89e813d2",
"SiteName": "Main Office Building"
}
The SiteReference GUID is your site’s unique identifier. Save this value—you’ll need it for subsequent API calls.
The site key must be a random 32-byte (256-bit) value. Generate it using a cryptographically secure random number generator.
Example using OpenSSL:
# Generate 32 random bytes in hex format (64 hex characters)
openssl rand -hex 32
# Output: 123456789abcdef123456789abcdef123456789abcdef123456789abcdef12
# Convert to Base64 for API registration
echo -n "123456789abcdef123456789abcdef123456789abcdef123456789abcdef12" | xxd -r -p | base64
# Output: EjRWeJq83u8SRFZneJmqu8zd7v8SRFZneJmqu8zd7v8=
Example using Python:
import os
import base64
# Generate 32 random bytes
site_key_bytes = os.urandom(32)
# Convert to hex (for your records)
site_key_hex = site_key_bytes.hex()
print(f"Site Key (Hex): {site_key_hex}")
# Convert to Base64 (for API registration)
site_key_base64 = base64.b64encode(site_key_bytes).decode('ascii')
print(f"Site Key (Base64): {site_key_base64}")
CRITICAL SECURITY REQUIREMENTS:
Example secure storage locations:
# Step 1: Generate site key
SITE_KEY_HEX=$(openssl rand -hex 32)
SITE_KEY_BASE64=$(echo -n "$SITE_KEY_HEX" | xxd -r -p | base64)
echo "Generated Site Key (HEX): $SITE_KEY_HEX"
echo "Generated Site Key (Base64): $SITE_KEY_BASE64"
echo "SAVE THESE VALUES SECURELY!"
# Step 2: Create site via API
curl -X POST https://partner.lockwebserv.com/engage/sites/register \
-u "your-email@company.com:your-password" \
-H "Content-Type: application/json" \
-d "{
\"SiteName\": \"Main Office Building\",
\"Base64Key\": \"$SITE_KEY_BASE64\",
\"IsEngageManaged\": \"False\",
\"SiteSoftwareKey\": \"YourCompanyName\"
}"
# Step 3: Save the returned SiteReference GUID
Response example:
{
"SiteReference": "886182ed-0395-4236-8dcf-50cc89e813d2",
"SiteName": "Main Office Building"
}
Once your site is created, use the hex format of the site key for encrypting credentials:
from Crypto.Cipher import AES
# Your site key in hex format (64 hex characters = 32 bytes)
site_key_hex = "123456789abcdef123456789abcdef123456789abcdef123456789abcdef12"
site_key = bytes.fromhex(site_key_hex)
# Credential to encrypt (16 bytes)
credential = bytes.fromhex("8F166040FFFFFFFFFFFFFFFFFFFFFFFF")
# Encrypt with AES-256 CBC, IV = all zeros
iv = bytes(16) # 16 zero bytes
cipher = AES.new(site_key, AES.MODE_CBC, iv)
encrypted_credential = cipher.encrypt(credential)
print(f"Encrypted: {encrypted_credential.hex().upper()}")
When managing sites for multiple customers, you have two options:
Option 1: Central Account
Option 2: One Account Per Premises
Both approaches require: Multiple account types with different permission levels, as some end users need lower-permission accounts specifically for device enrollment.
CRITICAL GOTCHA: Before enrolling devices into a site:
This is easy to forget and very difficult to debug. Always verify devices are factory reset and removed from previous sites before attempting enrollment.
If you need to create an ENGAGE account:
After creating your site:
See also: Creating Engage Accounts and using Mobile App
To help you test and explore the ENGAGE APIs, download these Postman collections:
Allegion APIs.postman_collection_11-6-2019.json
allegionApiEnvironment.postman_environment-10_18_2022.json
How to Use:
Allegion APIs.postman_collection_11-6-2019.jsonallegionApiEnvironment.postman_environment-10_18_2022.jsonWhen a Gateway is new or factory reset, it auto-generates a unique username and random password. You must retrieve these credentials and commit them to the Gateway for ongoing use. This two-step process establishes secure access to the Gateway API.
Both the GET and PUT credential requests use these default credentials for authentication:
EngageGatewayDefaultUserEngageGatewayDefaultPasswordPurpose: These are temporary credentials used only to retrieve and commit the auto-generated Gateway credentials. After the two-step process is complete, you’ll use the auto-generated credentials for all subsequent API access.
API Endpoint: GET {{gatewayIP}}/gateway/newCredentials
When the Gateway is first powered on or factory reset, it automatically generates a unique username and random password. Retrieve these using the default credentials.
Request:
GET http://192.168.1.100/gateway/newCredentials
Authorization: Basic <base64(EngageGatewayDefaultUser:EngageGatewayDefaultPassword)>
Example with curl:
curl -u "EngageGatewayDefaultUser:EngageGatewayDefaultPassword" \
http://192.168.1.100/gateway/newCredentials
Example with Postman:
GEThttp://192.168.1.100/gateway/newCredentialsEngageGatewayDefaultUserEngageGatewayDefaultPasswordResponse:
{
"credentials": {
"username": "gateway_b100000000011d96",
"password": "9EAgj5m859KMwP+W962hPFZHU2QJqAR1"
}
}
Credential Format:
gateway_ + Gateway serial number or MAC address
gateway_b100000000011d969EAgj5m859KMwP+W962hPFZHU2QJqAR1CRITICAL: Save these credentials immediately! You’ll need them for Step 2b and all future Gateway access.
API Endpoint: PUT {{gatewayIP}}/gateway/newCredentials
After retrieving the auto-generated credentials, you must commit them by sending a PUT request. This activates the credentials and disables the default authentication.
Request:
PUT http://192.168.1.100/gateway/newCredentials
Authorization: Basic <base64(EngageGatewayDefaultUser:EngageGatewayDefaultPassword)>
Content-Type: application/json
{}
Important: The request body is empty ({}). You are not setting custom credentials—you’re committing the auto-generated credentials from Step 2a.
Example with curl:
curl -X PUT http://192.168.1.100/gateway/newCredentials \
-u "EngageGatewayDefaultUser:EngageGatewayDefaultPassword" \
-H "Content-Type: application/json" \
-d '{}'
Example with Postman:
PUThttp://192.168.1.100/gateway/newCredentialsEngageGatewayDefaultUserEngageGatewayDefaultPassword{}Response:
{
"status": "success",
"message": "Credentials committed successfully"
}
What Happens After PUT:
EngageGatewayDefaultUser / EngageGatewayDefaultPassword) are disabled1. Power on new/factory-reset Gateway
↓
2. Connect Gateway to network (Ethernet)
↓
3. Discover Gateway IP address (DHCP server, network scan, etc.)
↓
4. GET /gateway/newCredentials
- Authenticate with: EngageGatewayDefaultUser / EngageGatewayDefaultPassword
- Receive: auto-generated username/password
- Example response: gateway_b100000000011d96 / 9EAgj5m859KMwP+W962hPFZHU2QJqAR1
↓
5. SAVE the auto-generated credentials in secure storage immediately!
↓
6. PUT /gateway/newCredentials (blank body)
- Authenticate with: EngageGatewayDefaultUser / EngageGatewayDefaultPassword
- Body: {} (empty JSON object)
- This commits the auto-generated credentials
↓
7. Test access with the auto-generated credentials from step 4
↓
8. Update all API clients, Postman collections, etc. with new credentials
↓
9. Document which Gateway uses which credentials
After completing the PUT request, immediately verify you can access the Gateway with the auto-generated credentials:
# Test with the auto-generated credentials from GET response
curl -u "gateway_b100000000011d96:9EAgj5m859KMwP+W962hPFZHU2QJqAR1" \
http://192.168.1.100/gateway/deviceInfo
Expected: Successful response with Gateway information
If this fails: The credentials may not have been committed properly. You may need to factory reset and try again.
Act quickly:
Secure storage: Store auto-generated Gateway credentials in:
Access control:
Network security:
Documentation:
Backup credentials:
Credential rotation:
Multi-Gateway deployments:
Cannot Change Credentials: Unlike many systems, ENGAGE Gateway credentials are fixed to the auto-generated values. You cannot set custom usernames or passwords.
To “change” credentials, you must:
Why this matters:
Problem: GET /gateway/newCredentials returns 401 Unauthorized
Solutions:
EngageGatewayDefaultUser / EngageGatewayDefaultPassword (case-sensitive)Problem: GET returns empty, null, or error response
Solutions:
/gateway/deviceInfo with possible saved credentialsProblem: PUT /gateway/newCredentials returns 401 Unauthorized
Solutions:
EngageGatewayDefaultUser / EngageGatewayDefaultPasswordProblem: PUT returns success but cannot authenticate with auto-generated credentials
Solutions:
/gateway/deviceInfo to test credentialsProblem: Forgot to save credentials before PUT, or lost saved credentials
Solution:
Problem: PUT request seems to succeed but default credentials still work
Solutions:
After committing credentials via PUT, all subsequent API requests must use the auto-generated credentials from Step 2a:
Using Basic Auth:
GET http://192.168.1.100/gateway/deviceInfo
Authorization: Basic <base64(gateway_b100000000011d96:9EAgj5m859KMwP+W962hPFZHU2QJqAR1)>
Example with curl:
curl -u "gateway_b100000000011d96:9EAgj5m859KMwP+W962hPFZHU2QJqAR1" \
http://192.168.1.100/gateway/deviceInfo
Example with Postman:
gateway_b100000000011d969EAgj5m859KMwP+W962hPFZHU2QJqAR1Using Postman Environment Variables (Recommended):
For managing multiple Gateways efficiently:
Create environment (e.g., “Building A - Gateway 1”)
Add variables:
gateway_ip = 192.168.1.100gateway_username = gateway_b100000000011d96gateway_password = 9EAgj5m859KMwP+W962hPFZHU2QJqAR1gateway_serial = b100000000011d96 (for documentation)gateway_location = Building A - Main Entrance (for documentation)Update request URLs to: http://{{gateway_ip}}/...
In Authorization tab:
{{gateway_username}}{{gateway_password}}Benefits:
The Gateway supports two discovery methods (these are often set in the engage app when commissioning the device to the site):
{
"gatewayConfig": {
"gatewayIpModeConfig": {
"interfaceId": "eth0",
"interfaceEnable": "T",
"discoveryMethod": "dhcp"
}
}
}
{
"gatewayConfig": {
"gatewayIpModeConfig": {
"interfaceId": "eth0",
"interfaceEnable": "T",
"discoveryMethod": "staticIP",
"fixedIpAddr": "192.168.1.100",
"defGatewayIpAddr": "192.168.1.1",
"netmask": "255.255.255.0",
"ipDnsAddr": "8.8.8.8"
}
}
}
Set the connection protocol for communication with your access control system. In typical usage, the Gateway acts as a server and your access control system connects to it:
{
"gatewayConfig": {
"genGatewayConfig": {
"hostProtocol": "ipEngage",
"deviceName": "Main-Building-Gateway"
}
}
}
Host Protocol Options:
"ipEngage": Gateway acts as IP server (typical for 410-IP mode)"rs485Rsi": RSI protocol over RS-485"ipClient": Gateway acts as websocket client (see Advanced Topics section)Important: See ENGAGE_JSON_Data_Structures - 2.18.pdf pages 57-65 for complete Gateway configuration options.
Link using the Engage Mobile app, or use the Gateway’s scan functionality to discover nearby ENGAGE locks:
API Endpoint: GET /gateway/scanList
Response:
{
"gatewayScanList": [
{
"mainSn": "A0B1000000000123",
"deviceName": "Front Door",
"signalQuality": "High",
"modelType": "nde"
}
]
}
API Endpoint: POST /edgeDevices
Request:
{
"linkInfo": {
"linkId": "door_001",
"deviceId": "A0B1000000000123",
"deviceName": "Front Door"
}
}
Response:
{
"linkInfo": {
"linkId": "door_001",
"deviceId": "A0B1000000000123",
"deviceName": "Front Door",
"linkCommStatus": "connected",
"modelType": "nde"
}
}
API Endpoint: GET /edgeDevices
Check that linkCommStatus shows "connected" for your devices.
A PrimeCr (Primary Credential) is a 16-byte encrypted data structure that represents a user’s access credential. It can contain:
PrimeCr = [Primary (8 bytes)] + [Secondary (8 bytes)]
= 16 bytes total
Example:
0x8F166040FFFFFFFFFFFFFFFFFFFFFFFF0x8F166040FFFFFFFF823456FFFFFFFFFFCritical Reference: Schlage ENGAGE Credential Sort and Encryption-1.34.pdf provides the complete specification for credential encoding.
A credential is any object that transmits a series of bits to a reader to gain access or privileges. Common credential types include:
| Type | Interface | Security Level | Notes |
|---|---|---|---|
| Proximity | Touchless | Low | Not encrypted, vulnerable to replay/cloning |
| Smart Card | Touch/Touchless | High | Encrypted, contains storage |
| Mobile Bluetooth | Touchless | High | Requires app, encrypted |
| NFC | Touchless | High | No app required, encrypted |
| Magstripe | Touch | Very Low | Not encrypted, easily counterfeited |
Important ENGAGE Distinction: Unlike traditional access control systems where readers parse facility codes, card IDs, and verify parity bits, ENGAGE locks simply compare the entirety of the expected credential data to the presented credential data. The lock does not parse individual fields or check parity — it performs a complete binary match.
Access cards contain multiple data elements encoded in their bit patterns:
The most common format, used by millions of installations worldwide:
Structure (26 bits total):
Bit 0: Even parity bit
Bits 1-8: Facility code (8 bits = 0-255)
Bits 9-24: Card number (16 bits = 0-65,535)
Bit 25: Odd parity bit
Parity Configuration:
Example:
A more advanced format with larger capacity:
Structure (37 bits total, indexed 0-36):
Bit 0: Even parity bit
Bits 1-16: Facility/Site code (16 bits = 0-65,535)
Bits 17-35: Card ID (19 bits = 0-524,287)
Bit 36: Odd parity bit
Parity Configuration:
Example - Real Card:
Let’s walk through encoding a real card with complete bit-level detail.
Card Number (200802 decimal):
Binary: 0110001000001100010 (19 bits)
Site Code (700 decimal):
Binary: 0000001010111100 (16 bits)
Position the data according to H10304 format specification:
INDEX: 0 1 16 17 35 36
P [Site Code ] [Card ID ] P
POSITIONS: 0 1234567890123456 1234567890123456789 36
SITE CODE: 0000001010111100
CARD ID: 0110001000001100010
PARITY BITS: ? ?
Expanded with padding:
INDEX: 0123456789012345678901234567890123456
EVEN PAR MASK: 1111111111111111111000000000000000000
ODD PAR MASK: 0000000000000000001111111111111111111
DATA (no par): ?00000010101111000110001000001100010?
Count the 1s in positions covered by even parity mask (bits 0-18):
Bits 1-18: 0000001010111100001
Count of 1s: 7 (odd number)
Since we have an odd count and need even parity, set bit 0 to 1 to make the total even (8).
Result: Bit 0 = 1
Count the 1s in positions covered by odd parity mask (bits 18-36):
Bits 18-35: 100110001000001100010
Count of 1s: 6 (even number)
Since we have an even count and need odd parity, set bit 36 to 1 to make the total odd (7).
Result: Bit 36 = 1
Complete binary (37 bits):
1000000101011110001100010000011000101
Breakdown:
Bit 0: 1 (even parity)
Bits 1-16: 0000001010111100 (site code 700)
Bits 17-35: 0110001000001100010 (card 200802)
Bit 36: 1 (odd parity)
Convert to hexadecimal:
Binary: 1000000101011110001100010000011000101
Hex: 0x102BC620C5
PrimeCR (Primary Credential) is a 16-byte field in the ENGAGE JSON that contains credential data:
PrimeCR = [Primary (8 bytes)] + [Secondary (8 bytes)]
= 16 bytes total (128 bits)
Usage:
0xFFCritical Concept: All credential data must be aligned to byte boundaries (multiples of 8 bits).
Starting data (37 bits):
Binary: 1000000101011110001100010000011000101
Hex: 0x102BC620C5
Step 1: Left shift 3 positions (append 3 zeros):
Original: 1000000101011110001100010000011000101 (37 bits)
Shifted: 1000000101011110001100010000011000101000 (40 bits = 5 bytes)
Hex: 0x815E310628
Step 2: Pad to 8 bytes with 0xFF:
0x815E310628FFFFFFFFFF
Step 3: Add secondary credential (8 bytes of 0xFF for single-factor):
0x815E310628FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
Result: 16-byte PrimeCR
Breakdown:
0x815E310628 - Card data (37 bits left-shifted by 3)
FFFFFFFFFF - Padding to fill first 8 bytes
FFFFFFFFFFFFFFFF - Secondary credential (none = 0xFF)
| Format | Bits | Shift By | Result Bytes |
|---|---|---|---|
| 26-bit | 26 | 6 bits (×64) | 4 bytes |
| 34-bit | 34 | 6 bits (×64) | 5 bytes |
| 35-bit | 35 | 5 bits (×32) | 5 bytes |
| 37-bit | 37 | 3 bits (×8) | 5 bytes |
| 48-bit | 48 | 0 bits (×1) | 6 bytes |
Formula: bits_to_shift = (8 - (credential_bits % 8)) % 8
Scenario: User must present both card and 6-digit PIN.
Card data (26-bit):
Raw: 0x23C5981
Shifted left 6: 0x8F166040
Padded to 8 bytes: 0x8F166040FFFFFFFF
PIN data (123456 decimal = 0x1E240):
Raw: 0x1E240
Shifted and padded: 0x001E2400FFFFFFFF
Combined PrimeCR:
0x8F166040FFFFFFFF001E2400FFFFFFFF
Breakdown:
Primary (card): 0x8F166040FFFFFFFF
Secondary (PIN): 0x001E2400FFFFFFFF
JSON Configuration:
{
"usrID": 50,
"primeCr": "ENCRYPTED_VALUE_HERE",
"prCrTyp": "card",
"scndCrTyp": "pin"
}
Before encryption, credentials MUST be sorted in ascending order by their unencrypted PrimeCR values.
Why this matters:
Sorting Process:
Example Sort Order:
0x001E2400FFFFFFFFFFFFFFFFFFFFFFFF (lowest)
0x8F166040FFFFFFFFFFFFFFFFFFFFFFFF
0x9A250750FFFFFFFFFFFFFFFFFFFFFFFF (highest)
Use AES-256 CBC encryption per Schlage ENGAGE specification:
Encryption Parameters:
0x00000000000000000000000000000000)Example:
Site Key (hex): 00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF
Unencrypted: 815E310628FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
Encrypted: 7a749873c4b3f54dfdd31b3f82e67e55
Tools for Encryption:
IMPORTANT: Never send unencrypted PrimeCR values to locks. Always encrypt before transmission.
1. Obtain card data (from enrollment reader or audit trail)
↓
2. Parse facility code and card number
↓
3. Convert to binary representation
↓
4. Calculate and add parity bits
↓
5. Convert to hexadecimal
↓
6. Left-shift to align to byte boundary
↓
7. Pad with 0xFF to complete 16-byte PrimeCR
↓
8. Sort all credentials (unencrypted)
↓
9. Encrypt each PrimeCR with site key
↓
10. Build JSON with encrypted values in sorted order
↓
11. Send to lock via Gateway API
Use audit trails to verify correctness:
Example Audit:
{
"audits": [
{
"event": "13000025", // Card not in database, length=37 bits
"event": "13010628", // Card data bytes (reverse order)
"event": "13025e31",
"event": "13030081"
}
]
}
Reconstruct: 0x00815e310628 → matches our calculated value ✓
Always calculate parity last: Fill all data bits first, then calculate parity to ensure masks sum correctly
Document your format: Record the exact Wiegand format (26-bit, 37-bit, etc.) used in your deployment
Test with real cards: Verify encoding by capturing audit events from actual card presentations
Understand byte alignment: Don’t blindly shift — understand why alignment to byte boundaries is necessary
Never skip sorting: Always sort before encryption, even for small databases
Verify encryption parameters: Confirm IV=0 and correct site key are used
Send complete databases: Prefer sending full sorted databases over incremental updates
Monitor database limits:
0x06000004 (database full)Facility code limitations: Don’t rely on facility codes alone for security with standard 125 kHz proximity cards (easily cloned)
Maintain sort order: After encryption, keep the same ordering based on unencrypted values
{
"db": {
"usrRcrd": {
"deleteAll": 1,
"delete": [],
"update": [],
"add": [
{
"usrID": 1,
"adaEn": 0,
"fnctn": "norm",
"crSch": [1],
"actDtTm": "20000101000000",
"expDtTm": "21350101000000",
"primeCr": "34110EA549AA549AA73FF06DC93B63B4",
"prCrTyp": "card",
"scndCrTyp": "null"
}
]
},
"schedules": [
{
"days": ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
"strtHr": 0,
"strtMn": 0,
"lngth": 1439
}
],
"holidays": [],
"autoUnlock": [],
"nxtDbVerTS": "0x0000000000000001"
}
}
| Field | Description |
|---|---|
deleteAll | 1 = Replace entire database, 0 = Update existing |
usrID | Unique user identifier (1-1,048,575) |
fnctn | Credential function: “norm”, “toggle”, “freeze”, etc. |
crSch | Array of schedule IDs (minimum one schedule required) |
actDtTm | Activation date/time (YYYYMMDDHHMMSS) |
expDtTm | Expiration date/time |
primeCr | Encrypted and sorted 16-byte credential |
prCrTyp | Primary credential type: “card” or “pin” |
scndCrTyp | Secondary credential type: “card”, “pin”, or “null” |
nxtDbVerTS | Version timestamp for synchronization |
{
"db": {
"usrRcrd": {
"deleteAll": 1,
"delete": [],
"update": [],
"add": [
{
"usrID": 1,
"primeCr": "34110EA549AA549AA73FF06DC93B63B4",
...
},
{
"usrID": 2,
"primeCr": "45220FB65ABB65ABB84GG17ED04C74C5",
...
}
]
}
}
}
Remember: Credentials in the add array must be sorted by unencrypted value before encryption.
Every database must include at least one schedule. Schedule ID 1 is typically 24/7 access:
{
"schedules": [
{
"days": ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
"strtHr": 0,
"strtMn": 0,
"lngth": 1439
}
]
}
Schedule ID 2: Monday-Friday, 8 AM to 5 PM (9 hours = 540 minutes)
{
"schedules": [
{
"days": ["Mo", "Tu", "We", "Th", "Fr"],
"strtHr": 8,
"strtMn": 0,
"lngth": 540
}
]
}
{
"usrID": 1,
"crSch": [1, 2],
...
}
This user has access during schedule 1 OR schedule 2 time windows.
Require both card and PIN during certain hours:
{
"schedules": [
{
"days": ["Mo", "Tu", "We", "Th", "Fr"],
"strtHr": 18,
"strtMn": 0,
"lngth": 720,
"auth": "2f"
}
]
}
Authentication modes:
"1f": Single factor (card OR PIN)"2f": Two factor (card AND PIN, same user)"1fx2": Single factor twice (two different users)"2fx2": Two factor twice (two different users, each with card+PIN)Override normal schedules on specific dates:
{
"holidays": [
{
"strtDtTm": "20261225000000",
"endDtTm": "20261226000000",
"action": "pass"
},
{
"strtDtTm": "20260704000000",
"endDtTm": "20260705000000",
"action": "sec"
}
]
}
Actions:
"pass": Unlock door (passage mode)"sec": Lock door (secure mode)"rstrctSec": Restricted secure (only freeze and pass-through credentials work)Automatically lock/unlock doors at specific times:
{
"autoUnlock": [
{
"action": "pass",
"strtHr": 8,
"strtMn": 0,
"days": ["Mo", "Tu", "We", "Th", "Fr"]
},
{
"action": "sec",
"strtHr": 17,
"strtMn": 0,
"days": ["Mo", "Tu", "We", "Th", "Fr"]
}
]
}
Endpoint: PUT /edgeDevices/{linkId}/lockData
Request:
{
"edgeDevice": [
{
"linkId": "door_001",
"db": {
"usrRcrd": { ... },
"schedules": [ ... ],
"holidays": [ ... ],
"autoUnlock": [ ... ]
},
"nxtDbVerTS": "0x0000000000000001"
}
]
}
The nxtDbVerTS (Next Database Version Timestamp) keeps the lock and host synchronized:
nxtDbVerTS value (starts at 0 for new locks)nxtDbVerTS valuenxtDbVerTSnxtDbVerTSImportant: Always increment the nxtDbVerTS value when sending database updates.
For updating individual users without replacing the entire database:
{
"db": {
"usrRcrd": {
"deleteAll": 0,
"delete": [
{
"primeCr": "34110EA549AA549AA73FF06DC93B63B4",
"prCrTyp": "card"
}
],
"update": [
{
"usrID": 2,
"primeCr": "45220FB65ABB65ABB84GG17ED04C74C5",
"expDtTm": "20271231235959",
...
}
],
"add": [
{
"usrID": 100,
"primeCr": "56330GC76BCC76BCC95HH28FE15D85D6",
...
}
]
}
}
}
Processing order: Delete → Update → Add
{
"edgeDevice": [
{
"linkId": "door_001",
"config": {
"name": "Front Door",
"type": "office",
"relock": 5,
"doorProp": 30,
"ada": 10,
"rtcTime": "20260401120000",
"gatewayCommFail": "sec",
"tamperFail": "sec"
}
}
]
}
| Tag | Description | Values |
|---|---|---|
relock | Auto-relock delay (seconds) | 1-60 for Control, 1-30 for others |
doorProp | Door propped alarm delay (seconds) | 1-255 |
ada | ADA compliant unlock time (seconds) | 1-255 |
type | Lock function type | ”strm”, “office”, “apt”, “prvcy” |
gatewayCommFail | Failsafe mode when Gateway offline | ”asIs”, “sec”, “safe” |
tamperFail | Failsafe mode on tamper | ”asIs”, “sec”, “safe” |
Enable/disable credential types:
{
"config": {
"proxConfHID": "T",
"uid14443": "T",
"noc14443": "T",
"iClsUID40b": "F",
"bleCredEn": "T",
"pinEn": "T",
"pinLength": 6
}
}
IMPORTANT: By default, locks do NOT generate audit events for invalid/denied credentials. You must explicitly enable this feature.
Set invCrdAudEn to "T" to receive access denied events:
{
"config": {
"invCrdAudEn": "T"
}
}
Why this matters:
"F" (disabled) - invalid credential presentations generate NO audit events"T" - lock generates audit events when unknown/invalid credentials are presentedCommon use cases for access denied audits:
Example: Enable access denied auditing in complete doorfile:
{
"edgeDevice": [
{
"linkId": "door_001",
"db": {
"usrRcrd": {
"deleteAll": 1,
"add": [ ... ]
},
"schedules": [ ... ],
"holidays": [ ... ],
"autoUnlock": [ ... ]
},
"config": {
"invCrdAudEn": "T",
"name": "Front Door",
"type": "office"
},
"nxtDbVerTS": "0x0000000000000001"
}
]
}
Note: The config section appears after the db, schedules, holidays, and autoUnlock sections in the doorfile structure.
Audit event codes for denied access (when invCrdAudEn is enabled):
13xxxxxx: Invalid credential presented, includes credential data in subsequent audit recordsRECOMMENDATION: Always set invCrdAudEn to "T" in production deployments for security monitoring and troubleshooting.
IMPORTANT: Understanding how configuration updates are applied is critical to avoid unintended changes.
Configuration parameters are sent within the lock data structure (often called the “doorfile”). The config section appears after the database sections:
{
"edgeDevice": [
{
"linkId": "door_001",
"db": {
"usrRcrd": { ... },
"schedules": [ ... ],
"holidays": [ ... ],
"autoUnlock": [ ... ]
},
"config": {
"name": "Front Door",
"invCrdAudEn": "T"
},
"nxtDbVerTS": "0x0000000000000001"
}
]
}
The config section appears alongside the db (database) section in the same request.
Key concept: Only parameters explicitly included in the config object are updated. Omitted parameters remain unchanged at their current values.
Example 1: Update only audit setting
{
"edgeDevice": [
{
"linkId": "door_001",
"config": {
"invCrdAudEn": "T"
}
}
]
}
Result:
invCrdAudEn is updated to "T"Example 2: Update multiple parameters
{
"edgeDevice": [
{
"linkId": "door_001",
"config": {
"invCrdAudEn": "T",
"relock": 10,
"doorProp": 45
}
}
]
}
Result:
invCrdAudEn is updated to "T"relock is updated to 10 secondsdoorProp is updated to 45 secondsCommon pattern: Send database and configuration together in a single request:
{
"edgeDevice": [
{
"linkId": "door_001",
"db": {
"usrRcrd": {
"deleteAll": 1,
"add": [ ... ]
},
"schedules": [ ... ],
"holidays": [ ... ],
"autoUnlock": [ ... ]
},
"config": {
"invCrdAudEn": "T",
"rtcTime": "20260415120000"
},
"nxtDbVerTS": "0x0000000000000001"
}
]
}
Result:
Update only what you need: Don’t include parameters you’re not changing—this avoids accidental overwrites and keeps requests smaller
Track current configuration: Maintain a record of each lock’s current configuration to know what values are already set
Verify after update: After sending configuration, retrieve lock data to verify the update was applied:
GET {{gatewayIP}}/edgeDevices/{{linkId}}/lockData
Test configuration changes: Test on a single lock before applying to all locks in your deployment
Document configuration standards: Maintain documentation of your standard lock configuration (which parameters you always set)
Initial setup vs. updates:
Scenario 1: Enable auditing on existing locks (configuration only)
{
"edgeDevice": [
{
"linkId": "door_001",
"config": {
"invCrdAudEn": "T"
}
}
]
}
All other settings remain unchanged. No database sections needed.
Scenario 2: Update clock and auditing (configuration only)
{
"edgeDevice": [
{
"linkId": "door_001",
"config": {
"rtcTime": "20260415143000",
"invCrdAudEn": "T"
}
}
]
}
Only RTC time and audit setting are updated.
Scenario 3: Full initial configuration with database
{
"edgeDevice": [
{
"linkId": "door_001",
"db": {
"usrRcrd": {
"deleteAll": 1,
"add": [ ... ]
},
"schedules": [ ... ],
"holidays": [ ... ],
"autoUnlock": [ ... ]
},
"config": {
"name": "Front Door",
"type": "office",
"relock": 5,
"doorProp": 30,
"ada": 10,
"invCrdAudEn": "T",
"gatewayCommFail": "sec",
"tamperFail": "sec",
"proxConfHID": "T",
"pinEn": "T",
"rtcTime": "20260415143000"
},
"nxtDbVerTS": "0x0000000000000001"
}
]
}
During initial setup, specify all desired configuration parameters along with the complete database.
Complete configuration reference: JSON Data Structures document, pages 13-21.
{{gateway_url}} = http://192.168.1.100 (your Gateway IP)GET {{gateway_url}}/gateway/deviceInfo
Expected response includes firmware version, network settings, and linked devices.
GET {{gateway_url}}/gateway/scanList
Returns nearby ENGAGE devices available for linking.
POST {{gateway_url}}/edgeDevices
Content-Type: application/json
{
"linkInfo": {
"linkId": "test_door_01",
"deviceId": "A0B1000000000123",
"deviceName": "Test Door"
}
}
GET {{gateway_url}}/edgeDevices/test_door_01/lockData
Returns current lock state, battery level, and configuration.
PUT {{gateway_url}}/edgeDevices/test_door_01/lockData
Content-Type: application/json
{
"edgeDevice": [
{
"linkId": "test_door_01",
"db": {
"usrRcrd": {
"deleteAll": 1,
"delete": [],
"update": [],
"add": [
{
"usrID": 1,
"adaEn": 0,
"fnctn": "norm",
"crSch": [1],
"actDtTm": "20000101000000",
"expDtTm": "21350101000000",
"primeCr": "YOUR_ENCRYPTED_CREDENTIAL_HERE",
"prCrTyp": "card",
"scndCrTyp": "null"
}
]
},
"schedules": [
{
"days": ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
"strtHr": 0,
"strtMn": 0,
"lngth": 1439
}
],
"holidays": [],
"autoUnlock": []
},
"nxtDbVerTS": "0x0000000000000001"
}
]
}
GET {{gateway_url}}/edgeDevices/test_door_01/lockData
Look for audits array in response.
linkId, deviceId, etc. as variablesrtcTimeReference: See [03 - What are json files and further postman usage](ip-gateway-info/Slideshows for engage training/PPT slideshows/) presentation for detailed Postman workflows.
Locks store audit events locally and report them to the Gateway:
{
"audits": [
{
"event": "008A0000",
"time": "20260401143022",
"auditId": "145"
}
]
}
API Endpoint: GET {{gatewayIP}}/edgeDevices/{{linkId}}/audits
Retrieve audit events from a specific lock:
Request:
GET http://192.168.1.100/edgeDevices/door_001/audits
Authorization: Basic <base64(username:password)>
Response:
{
"edgeDevice": [
{
"linkId": "door_001",
"audits": [
{
"event": "008A0000",
"time": "20260415143022",
"auditId": "145"
},
{
"event": "008B0001",
"time": "20260415143045",
"auditId": "146"
}
]
}
]
}
Refer to the ENGAGE Audit & Alert Definitions document for complete event codes. Common examples:
IMPORTANT: The Gateway API uses a polling model for monitoring audits and lock status. There is no push notification or webhook mechanism in 410-IP mode.
Audit and Status Monitoring:
Why These Limits?
Too frequent (< 5 seconds):
Recommended range (5-10 seconds):
Too infrequent (> 60 seconds):
CRITICAL: Stop polling during firmware updates
When performing firmware updates (ONR - Over-the-Network Reflash):
Resume polling after:
See Firmware Updates section for detailed update procedures.
Get Audits Only:
GET http://{{gatewayIP}}/edgeDevices/{{linkId}}/audits
Get Lock Data (includes audits and status):
GET http://{{gatewayIP}}/edgeDevices/{{linkId}}/lockData
Get All Locks Status:
GET http://{{gatewayIP}}/edgeDevices
Example polling implementation:
// Poll interval: 5 seconds (5000 ms)
const POLL_INTERVAL = 5000;
let pollingEnabled = true;
async function pollLockAudits(linkId) {
if (!pollingEnabled) {
console.log('Polling disabled (firmware update in progress)');
return;
}
try {
const response = await fetch(
`http://192.168.1.100/edgeDevices/${linkId}/audits`,
{
headers: {
'Authorization': 'Basic ' + btoa('username:password')
}
}
);
const data = await response.json();
// Process audits
if (data.edgeDevice?.[0]?.audits) {
processAudits(data.edgeDevice[0].audits);
}
} catch (error) {
console.error('Polling error:', error);
}
}
// Control polling state
function stopPolling() {
pollingEnabled = false;
console.log('Polling stopped for firmware update');
}
function resumePolling() {
pollingEnabled = true;
console.log('Polling resumed');
}
// Start polling
setInterval(() => {
pollLockAudits('door_001');
}, POLL_INTERVAL);
Use consistent intervals: Don’t vary polling frequency randomly—maintain steady intervals
Handle errors gracefully:
Track processed audits:
auditId processedauditId values)Disable during maintenance:
Batch operations:
Connection pooling:
ENGAGE locks can be remotely controlled via the Gateway API, allowing your access control system to command doors into different states (unlock, lock, passage mode, etc.) without requiring a physical credential presentation.
The lockControl endpoint provides real-time control over lock states. This is useful for:
The Gateway provides two endpoint variants for controlling locks:
PUT {{gatewayIP}}/edgeDevices/lockControl
Commands all linked locks on the Gateway simultaneously. Useful for facility-wide lockdown or passage mode.
PUT {{gatewayIP}}/edgeDevices/{{linkId}}/lockControl
Commands a single lock identified by its linkId. Useful for individual door control.
JSON Payload:
{
"lockControl": {
"lockState": {
"nextLockState": "momentaryUnlock"
}
}
}
Field Descriptions:
| Field | Description | Required |
|---|---|---|
lockControl | Top-level container for lock control commands | Yes |
lockState | Container for state change commands | Yes |
nextLockState | Target state for the lock | Yes |
| State | Description | Duration | Use Case |
|---|---|---|---|
momentaryUnlock | Temporarily unlock, then relock | Relock delay period | Remote unlock for visitor entry |
sec | Secure (locked) mode | Until next event | Lock down door, end passage mode |
pass | Passage (unlocked) mode | Until next event | Business hours, events |
rstrctSec | Restricted secure | Until next event | Lockdown with passthrough credentials only |
Note: Available states depend on the lock’s configured door mode (office, storeroom, apartment, privacy). See Door Modes document for state compatibility.
IMPORTANT: Lock control commands put the device into the commanded state until a recalculation event occurs.
Recalculation Events (triggers lock to re-evaluate its state):
Frozen States: If the door is in a “frozen” state (via freeze credential), lock control commands may be ignored until the freeze is released.
Scenario: Visitor arrives, security guard remotely unlocks the front door.
PUT http://192.168.1.100/edgeDevices/front_door/lockControl
Content-Type: application/json
{
"lockControl": {
"lockState": {
"nextLockState": "momentaryUnlock"
}
}
}
Result: Front door unlocks for the configured relock delay period (e.g., 5 seconds), then automatically relocks.
Scenario: Enable passage mode on all doors for a company event.
PUT http://192.168.1.100/edgeDevices/lockControl
Content-Type: application/json
{
"lockControl": {
"lockState": {
"nextLockState": "pass"
}
}
}
Result: All linked locks enter passage (unlocked) mode and remain unlocked until another command or recalculation event occurs.
Scenario: Security incident requires immediate lockdown of all doors.
PUT http://192.168.1.100/edgeDevices/lockControl
Content-Type: application/json
{
"lockControl": {
"lockState": {
"nextLockState": "rstrctSec"
}
}
}
Result: All doors enter restricted secure mode. Only passthrough/freeze credentials will work until lockdown is cleared.
Scenario: Event ends, return all doors to secure mode.
PUT http://192.168.1.100/edgeDevices/lockControl
Content-Type: application/json
{
"lockControl": {
"lockState": {
"nextLockState": "sec"
}
}
}
Result: All doors return to locked (secure) mode. Normal credentials will unlock as configured.
Successful lock control commands return confirmation:
{
"lockControl": {
"lockState": {
"nextLockState": "momentaryUnlock"
}
},
"status": "success"
}
After sending a lock control command, verify the lock state changed:
GET http://192.168.1.100/edgeDevices/{{linkId}}/lockData
Check the response for current lock state in the lockState field:
{
"edgeDevice": [
{
"linkId": "front_door",
"lockState": {
"currentLockState": "pass"
}
}
]
}
Verify connectivity: Check linkCommStatus is "connected" before sending lock control commands
Monitor response: Always verify the API response indicates success
Coordinate with schedules: Be aware that auto-unlock/holiday schedules may override manual lock control commands
Audit trail: Lock control commands generate audit events—monitor these to track who/what changed door states
Emergency procedures: Document procedures for emergency lockdown and ensure operators are trained
Test regularly: Periodically test lock control functionality to ensure integration is working correctly
Failsafe configuration: Set appropriate gatewayCommFail mode to determine lock behavior if Gateway goes offline
Understand door modes: Different door types (office, storeroom, apartment) respond differently to lock control commands
// Visitor presses intercom button
async function remoteUnlock(doorLinkId) {
// Send unlock command
await fetch(`http://${gatewayIP}/edgeDevices/${doorLinkId}/lockControl`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
lockControl: {
lockState: {
nextLockState: 'momentaryUnlock'
}
}
})
});
// Log the event
console.log(`Door ${doorLinkId} remotely unlocked at ${new Date()}`);
}
// Business hours automation
async function setBusinessHoursMode() {
const now = new Date();
const hour = now.getHours();
const day = now.getDay();
// Monday-Friday, 8 AM to 5 PM
if (day >= 1 && day <= 5 && hour >= 8 && hour < 17) {
await setAllDoorsState('pass');
} else {
await setAllDoorsState('sec');
}
}
async function setAllDoorsState(state) {
await fetch(`http://${gatewayIP}/edgeDevices/lockControl`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
lockControl: {
lockState: {
nextLockState: state
}
}
})
});
}
No individual override: Broadcast commands affect all linked locks—you cannot exclude specific locks from a broadcast command
State persistence: Locks remain in commanded state until a recalculation event—plan for how to return to normal operation
Network dependency: Lock control requires Gateway connectivity—locks should have appropriate failsafe configuration
BLE latency: Commands must traverse IP → Gateway → BLE → Lock, introducing latency (typically 1-3 seconds)
Conflicting schedules: Auto-unlock and holiday schedules may interfere with manual lock control commands
Door mode limitations: Some lock states are not compatible with all door modes (e.g., passage mode on privacy function locks)
Lock doesn’t respond to control command:
linkCommStatus shows "connected"Lock enters state but immediately reverts:
Broadcast command only affects some locks:
linkCommStatus for all locksFirmware updates can be performed over-the-network (ONR - Over-the-Network Reflash) for both the Gateway itself and for locks linked to the Gateway.
All firmware updates use three key fields for scheduling and control:
| Field | Format | Description | Example |
|---|---|---|---|
fwurl | String (256 max) | URL path to firmware binary file | http://fw.lockwebserv.com/... |
fwDwnldTm | String (14 chars) | When to download the firmware | "20260403020000" or "0" |
fwImplTm | String (14 chars) | When to flash/implement the firmware | "20260404030000" or "0" |
Time Format: YYYYMMDDHHMMS
"20260403020000" = April 3, 2026, 2:00:00 AM"0" for immediate download/implementationWhy Two Timing Fields?
Step 1: Retrieve Available Firmware Versions
GET https://partner.lockwebserv.com/engage/firmware/gwe
Response includes firmware version and download URL.
Step 2: Command Gateway to Update
API Endpoint: PUT {{gateway_url}}/gateway/config
Request:
{
"gatewayConfig": {
"genGatewayConfig": {
"fwurl": "http://fw.lockwebserv.com/firmwarecontainer/gwe_01.51.24_gw_image_v1_1.51.24.bin",
"fwDwnldTm": "0",
"fwImplTm": "0"
}
}
}
Immediate Update:
fwDwnldTm and fwImplTm to "0"Scheduled Update:
{
"gatewayConfig": {
"genGatewayConfig": {
"fwurl": "http://fw.lockwebserv.com/firmwarecontainer/gwe_01.51.24_gw_image_v1_1.51.24.bin",
"fwDwnldTm": "20260403020000",
"fwImplTm": "20260404030000"
}
}
}
This downloads firmware at 2:00 AM on April 3rd, then implements at 3:00 AM on April 4th.
Step 3: Monitor Gateway Update Status
GET {{gateway_url}}/gateway/deviceInfo
Check response for current firmware version and update status.
Important Notes for Gateway Updates:
The Gateway can distribute firmware updates to all linked locks.
Step 1: Retrieve Available Lock Firmware
GET https://partner.lockwebserv.com/engage/firmware/nde
Replace nde with your device model: ndeb, leb, fe410bm, fe410b, fe410, cte, rmru
Step 2: Command Locks to Update
API Endpoint: PUT {{gateway_url}}/gateway/config
Request - Single Device Model:
{
"gatewayConfig": {
"edgeDeviceFwUrls": [
{
"fwurl": "http://fw.lockwebserv.com/firmwarecontainer/nde_02.11.29_Releasepkg_02.11.29.bin",
"mdl": "nde",
"fwDwnldTm": "0",
"fwImplTm": "0",
"fwVer": "02.11.29"
}
]
}
}
Request - Multiple Device Models:
{
"gatewayConfig": {
"edgeDeviceFwUrls": [
{
"fwurl": "http://fw.lockwebserv.com/firmwarecontainer/nde_02.11.29_Releasepkg_02.11.29.bin",
"mdl": "nde",
"fwDwnldTm": "20260403020000",
"fwImplTm": "20260404030000",
"fwVer": "02.11.29"
},
{
"fwurl": "http://fw.lockwebserv.com/firmwarecontainer/le_02.11.29_Releasepkg_02.11.29.bin",
"mdl": "le",
"fwDwnldTm": "20260403020000",
"fwImplTm": "20260404030000",
"fwVer": "02.11.29"
}
]
}
}
Edge Device Firmware Fields:
| Field | Required | Description | Example |
|---|---|---|---|
fwurl | Yes | Firmware binary URL | http://fw.lockwebserv.com/... |
mdl | Yes | Device model type | "nde", "le", "control", etc. |
fwVer | Yes | Firmware version (must match binary) | "02.11.29" |
fwDwnldTm | Yes | Download schedule | "0" or "20260403020000" |
fwImplTm | Yes | Implementation schedule | "0" or "20260404030000" |
CRITICAL: The fwVer field must exactly match the version encoded in the firmware binary filename. Mismatches will cause update failures.
API Endpoint: GET {{gateway_url}}/gateway/deviceInfo
Check the edgeDeviceFwUrls → extendedStatus array for per-device update status:
{
"gatewayConfig": {
"edgeDeviceFwUrls": [
{
"mdl": "nde",
"fwVer": "02.11.29",
"extendedStatus": [
{
"linkId": "door_001",
"fwUpdateStatus": "inProgress",
"fwTransferCmpltPercentage": 45
},
{
"linkId": "door_002",
"fwUpdateStatus": "completed",
"fwTransferCmpltPercentage": 100
}
]
}
]
}
}
Update Status Values:
"pendingUpdate": Waiting to start (scheduled or queued)"inProgress": Currently downloading/transferring firmware"completed": Successfully updated"failed": Update failed (check lock logs for details)Transfer Percentage:
fwTransferCmpltPercentage: 0-100 indicating download progressComplete Update Process:
1. Retrieve firmware information from cloud API
↓
2. Verify firmware URL is accessible
↓
3. Schedule download time (immediate or future)
↓
4. Schedule implementation time (immediate or future)
↓
5. Send update command to Gateway
↓
6. Gateway downloads firmware from URL
↓
7. [For locks] Gateway transfers firmware to locks via BLE
↓
8. Device implements/flashes firmware at scheduled time
↓
9. Device reboots with new firmware
↓
10. Verify new firmware version via deviceInfo
When to Schedule Updates:
Immediate Updates ("0" for both fields):
Scheduled Download, Immediate Implementation:
{
"fwDwnldTm": "20260403020000", // Download at 2 AM
"fwImplTm": "0" // Flash immediately after download
}
Scheduled Download and Implementation:
{
"fwDwnldTm": "20260403020000", // Download at 2 AM
"fwImplTm": "20260404030000" // Flash at 3 AM next day
}
Recommended Timing:
| Aspect | Gateway Update | Lock Update |
|---|---|---|
| Config Section | genGatewayConfig | edgeDeviceFwUrls (array) |
| Model Field | Not required | Required (mdl) |
| Version Field | Embedded in URL | Explicit (fwVer) |
| Multiple Devices | N/A (single Gateway) | Array supports multiple models |
| Downtime | Gateway temporarily offline | Individual locks reboot |
| Transfer Method | Direct from URL | Gateway → BLE → Lock |
| Cloud API Endpoint | /engage/firmware/gwe | /engage/firmware/{model} |
Gateway Update Fails:
Lock Update Fails:
fwVer matches binary filename exactlylinkCommStatus is "connected"mdl field matches actual device modelStatus Remains “pendingUpdate”:
Status Shows “failed”:
Scenario: Update 50 NDE locks and 1 Gateway during weekend maintenance window.
Step 1: Update Gateway (Friday night)
{
"gatewayConfig": {
"genGatewayConfig": {
"fwurl": "http://fw.lockwebserv.com/firmwarecontainer/gwe_01.51.24_gw_image_v1_1.51.24.bin",
"fwDwnldTm": "20260404020000", // Saturday 2:00 AM
"fwImplTm": "20260404030000" // Saturday 3:00 AM
}
}
}
Step 2: Update Locks (Saturday night)
{
"gatewayConfig": {
"edgeDeviceFwUrls": [
{
"fwurl": "http://fw.lockwebserv.com/firmwarecontainer/nde_02.11.29_Releasepkg_02.11.29.bin",
"mdl": "nde",
"fwDwnldTm": "20260405020000", // Sunday 2:00 AM
"fwImplTm": "20260405040000", // Sunday 4:00 AM
"fwVer": "02.11.29"
}
]
}
}
Step 3: Monitor Progress
# Saturday morning - check Gateway updated
curl http://192.168.1.100/gateway/deviceInfo
# Sunday morning - check lock updates
curl http://192.168.1.100/gateway/deviceInfo | jq '.gatewayConfig.edgeDeviceFwUrls[0].extendedStatus'
Step 4: Verify Success
01.51.24"completed"02.11.29Gateway Updates Cause Downtime: During Gateway firmware flash, all communication with locks is interrupted. Plan accordingly.
Version Matching is Critical: For lock updates, fwVer field must match exactly. Use the exact version string from the firmware API.
Test Before Production: Always test firmware updates on a subset of devices before full deployment.
Backup Configurations: Document current Gateway and lock configurations before updating.
Do Not Interrupt: Do not power cycle devices during firmware flash. This can brick the device.
BLE Range Matters: Lock updates require stable BLE connection. Ensure good signal quality before initiating updates.
Sequential Processing: Gateway processes edge device updates sequentially, not in parallel. Large deployments take time.
Firmware Rollback: Firmware rollback is not typically supported. Thoroughly test new firmware before deploying.
GET https://partner.lockwebserv.com/engage/firmware/{deviceType}GET {{gateway_url}}/gateway/deviceInfoSymptoms: Lock appears in scan list but won’t link, or linkCommStatus shows “disconnected”
Solutions:
Symptoms: Database update API returns error or lock doesn’t reflect changes
Solutions:
Symptoms: Valid credential is rejected, lock beeps/flashes red
Solutions:
actDtTm and expDtTmrtcTime config)proxConfHID = “T”)Symptoms: Lock takes several seconds to respond to credential presentation
Root Cause: Database is not properly sorted, causing linear search instead of binary search
Solution:
Symptoms: Cannot connect to Gateway IP address
Solutions:
nmap -sn 192.168.1.0/24Symptoms: Lock generates events but they don’t appear in API responses
Solutions:
linkCommStatus = “connected”)gatewayCommFail mode (recommend “sec”)nxtDbVerTS values to maintain database history// Pseudo-code for enrolling a new user
// 1. Get card number from enrollment reader
const cardNumber = readCard(); // e.g., 0x23C5981
// 2. Encode credential
const encoded = encodeCredential(cardNumber, 26); // Shift & pad
// Result: 0x8F166040FFFFFFFFFFFFFFFFFFFFFFFF
// 3. Get all existing credentials from database
const existingUsers = await getExistingDatabase();
// 4. Add new user to list
const newUser = {
usrID: getNextUserId(),
primeCr: encoded, // Not yet encrypted
prCrTyp: "card",
scndCrTyp: "null",
// ... other fields
};
existingUsers.push(newUser);
// 5. Sort by unencrypted value
existingUsers.sort((a, b) =>
BigInt("0x" + a.primeCr) - BigInt("0x" + b.primeCr)
);
// 6. Encrypt all credentials
existingUsers.forEach(user => {
user.primeCr = encryptCredential(user.primeCr, SITE_KEY);
});
// 7. Send to lock
await updateLockDatabase(existingUsers);
// Grant access for 24 hours
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const tempUser = {
usrID: 9999,
primeCr: encryptedCredential,
prCrTyp: "card",
scndCrTyp: "null",
fnctn: "oneTm", // One-time use
crSch: [1],
actDtTm: formatDateTime(new Date()),
expDtTm: formatDateTime(tomorrow)
};
await addUser(tempUser);
// Put all doors in secure lockdown mode
const lockdownHoliday = {
strtDtTm: formatDateTime(new Date()),
endDtTm: "21351231235959", // Far future
action: "rstrctSec" // Only freeze/passthrough work
};
await Promise.all(
allDoors.map(door =>
updateHolidays(door, [lockdownHoliday])
)
);
// Office hours + after-hours with 2FA
const schedules = [
{
// Schedule 1: 24/7 (for admins)
days: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
strtHr: 0,
strtMn: 0,
lngth: 1439,
auth: "1f"
},
{
// Schedule 2: Business hours (9-5)
days: ["Mo", "Tu", "We", "Th", "Fr"],
strtHr: 9,
strtMn: 0,
lngth: 480,
auth: "1f"
},
{
// Schedule 3: After hours (requires 2FA)
days: ["Mo", "Tu", "We", "Th", "Fr"],
strtHr: 17,
strtMn: 0,
lngth: 960,
auth: "2f"
}
];
// Admin user gets schedule 1
const adminUser = {
usrID: 1,
crSch: [1],
...
};
// Regular employee gets schedules 2 & 3
const employeeUser = {
usrID: 100,
primeCr: encryptedCard,
prCrTyp: "card",
scndCrTyp: "pin", // Has both card and PIN
crSch: [2, 3], // Business hours + after hours
...
};
ENGAGE supports four authentication modes:
{
"schedules": [
{
"auth": "1f",
...
}
]
}
Example 2f credential:
{
"usrID": 50,
"primeCr": "ENCRYPTED_CARD_AND_PIN",
"prCrTyp": "card",
"scndCrTyp": "pin",
"crSch": [5] // Schedule 5 has auth: "2f"
}
Reader-controllers (RC) support up to 32 extended schedules with 32 intervals each:
{
"schedulesExt": [
{
"extScheduleID": 1,
"intvlID": 1,
"moStrt": "0800",
"moEnd": "1700",
"moAuth": "1f",
"tuStrt": "0000",
"tuEnd": "2400",
"tuAuth": "2f"
}
]
}
Enable with: "extSchedEn": "T"
Configure general purpose inputs/outputs on CT controllers:
{
"config": {
"gpi": [
{
"iType": "rex",
"iEn": "T",
"pol": "high"
},
{
"iType": "dps",
"iEn": "T",
"pol": "high"
}
],
"gpo": [
{
"oType": "ttl1",
"oEn": "T",
"pol": "low",
"mode": "puls",
"src": ["rex"]
}
]
}
}
For enhanced security, enable HMAC authentication:
{
"config": {
"asmEn": "T",
"asmKey": "36353136"
}
}
Reference: JSON Data Structures document, page 38.
In most 410-IP deployments, the Gateway acts as a server and your access control system connects to it. However, in some architectures, you may want the Gateway to act as a websocket client that initiates connections to a remote server.
Use hostProtocol: "ipClient" when:
{
"gatewayConfig": {
"genGatewayConfig": {
"hostProtocol": "ipClient",
"deviceName": "Main-Building-Gateway"
},
"gatewayIpModeConfig": {
"ipServerURL": "https://your-server.com/api/engage",
"caServerURL": "https://your-server.com/api/ca",
"wsKeepAlive": "60"
}
}
}
| Parameter | Description |
|---|---|
ipServerURL | Websocket endpoint for ENGAGE API communication |
caServerURL | Websocket endpoint for credential authority operations |
wsKeepAlive | Keepalive interval in seconds (default: 60) |
ENGAGE_SAM_API_Integration_2.37.pdf
ENGAGE_JSON_Data_Structures - 2.18.pdf
Schlage ENGAGE Credential Sort and Encryption-1.34.pdf
ENGAGE - Audits - 0.23.xlsm
Complete API testing collections for both ENGAGE Cloud API and Gateway API:
Allegion APIs.postman_collection_11-6-2019.json
allegionApiEnvironment.postman_environment-10_18_2022.json
Import Instructions:
gateway_ip: Your Gateway’s IP address (e.g., 192.168.1.100)gateway_username: Auto-generated Gateway usernamegateway_password: Auto-generated Gateway passwordcloud_username: Your ENGAGE account emailcloud_password: Your ENGAGE account passwordsite_reference: Your site GUID from site creationsite_key: Your site encryption key (hex format)| Model Code | Description |
|---|---|
| nde | ENGAGE Wireless Cylindrical Lock |
| ndeb | NDE with BLE credential capability |
| le | ENGAGE Wireless Mortise Lock |
| leb | LE with BLE credential capability |
| control | Schlage Deadbolt/Interconnected Lock (BE467/FE410) |
| controlb | Control with BLE credential capability |
| cte | ENGAGE Controller |
| rc | Reader-Controller (RC11/RC15) |
| Function | Description |
|---|---|
| norm | Normal unlock with relock delay |
| toggle | Toggle between locked/unlocked |
| freeze | Disable reader until freeze presented again |
| passThru | Always unlock even in lockdown |
| lockDown | Put lock in secure mode |
| oneTm | One-time use credential |
| Mode | Description |
|---|---|
| strm | Storeroom (always locked, no interior unlock) |
| office | Office (unlocked from inside, locked outside) |
| apt | Apartment (privacy function) |
| prvcy | Privacy (inside lock/unlock button) |
See also: Door Modes - Functions Explanation and State Change Table for Apartment Mode
Set up test environment:
Implement credential encoding:
Build database management:
Test thoroughly:
Security hardening:
Monitoring and alerting:
Documentation:
Scaling:
ip-gateway-info/Slideshows for engage training/Before contacting support, check:
When contacting Allegion support, have ready:
Card Number: Facility Code 123, Card Number 45678
Combined Value: 0x1EC56E (26 bits)
Encoding:
1. Raw: 0x1EC56E
2. Shift left 6: 0x7B15B800
3. Pad primary: 0x7B15B800FFFFFFFF
4. Pad secondary: 0xFFFFFFFFFFFFFFFF
5. Combined PrimeCr: 0x7B15B800FFFFFFFFFFFFFFFFFFFFFFFF
6. Encrypt with site key: [encrypted result]
Card: 26-bit, value 0x23C5981
PIN: 6-digit, value 823456
Encoding:
Primary (card):
1. Raw: 0x23C5981
2. Shift left 6: 0x8F166040
3. Pad: 0x8F166040FFFFFFFF
Secondary (PIN):
1. Raw: 823456 decimal = 0xC9150
2. Shift to fit: 0x00C91500
3. Pad: 0x00C91500FFFFFFFF
Combined: 0x8F166040FFFFFFFF00C91500FFFFFFFF
Encrypted: [result depends on site key]
PIN: 123456
Encoding:
Primary (PIN):
1. 123456 decimal = 0x1E240
2. Shift: 0x001E2400
3. Pad: 0x001E2400FFFFFFFF
Secondary (null):
Pad: 0xFFFFFFFFFFFFFFFF
Combined: 0x001E2400FFFFFFFFFFFFFFFFFFFFFFFF
Encrypted: [result depends on site key]
JSON:
{
"prCrTyp": "pin",
"scndCrTyp": "null",
"primeCr": "[encrypted value]"
}
{
"edgeDevice": [
{
"linkId": "main_entrance",
"db": {
"usrRcrd": {
"deleteAll": 1,
"delete": [],
"update": [],
"add": [
{
"usrID": 1,
"adaEn": 0,
"fnctn": "norm",
"crSch": [1],
"actDtTm": "20260101000000",
"expDtTm": "20261231235959",
"primeCr": "34110EA549AA549AA73FF06DC93B63B4",
"prCrTyp": "card",
"scndCrTyp": "null"
},
{
"usrID": 2,
"adaEn": 1,
"fnctn": "norm",
"crSch": [1, 2],
"actDtTm": "20260101000000",
"expDtTm": "20261231235959",
"primeCr": "45220FB65ABB65ABB84GG17ED04C74C5",
"prCrTyp": "card",
"scndCrTyp": "pin"
}
]
},
"schedules": [
{
"days": ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
"strtHr": 0,
"strtMn": 0,
"lngth": 1439,
"auth": "1f"
},
{
"days": ["Mo", "Tu", "We", "Th", "Fr"],
"strtHr": 8,
"strtMn": 0,
"lngth": 540,
"auth": "1f"
}
],
"holidays": [
{
"strtDtTm": "20260704000000",
"endDtTm": "20260705000000",
"action": "pass"
},
{
"strtDtTm": "20261225000000",
"endDtTm": "20261226000000",
"action": "pass"
}
],
"autoUnlock": [
{
"action": "pass",
"strtHr": 8,
"strtMn": 0,
"days": ["Mo", "Tu", "We", "Th", "Fr"]
},
{
"action": "sec",
"strtHr": 17,
"strtMn": 0,
"days": ["Mo", "Tu", "We", "Th", "Fr"]
}
]
},
"config": {
"name": "Main Entrance",
"type": "office",
"relock": 5,
"doorProp": 30,
"ada": 10,
"gatewayCommFail": "sec",
"tamperFail": "sec",
"rtcTime": "20260401120000"
},
"nxtDbVerTS": "0x0000000000000001"
}
]
}
This guide has covered the essential concepts and procedures for integrating with Schlage ENGAGE devices using Gateway IP mode. Key takeaways:
With proper implementation, the ENGAGE Gateway IP mode provides a robust, scalable access control solution. Refer to the detailed technical documentation for advanced features and edge cases.
Happy integrating!