Contract Specification
Detailed specification for each of the 7 adapter protocol endpoints.
1. GET /ping
Health check. Called periodically by ZenEdge.
Request: No body.
Response (200):
{
"status": "ok",
"timestamp": "2025-01-15T10:00:00Z",
"version": "2.0"
}
Error (503):
{
"status": "error",
"message": "Device controller unreachable"
}
2. GET /capabilities
Declare what the adapter supports. Called once during onboarding and periodically for sync.
Request: No body.
Response (200):
{
"adapter": {
"name": "UniFi Access Adapter",
"version": "2.0",
"vendor": "Ubiquiti",
"protocol": "2.0"
},
"devices": [
{
"device_id": "ada-58b6b21b",
"name": "Door 9728",
"type": "lock",
"model": "UA-Door",
"brand": "UniFi Access",
"criticality": "CRITICAL",
"actions": ["unlock", "lock", "status"],
"webhook_capable": true,
"html_embed": true
}
]
}
3. GET /status?device_id={id}
Get the current state of a device.
Response (200):
{
"device_id": "ada-58b6b21b",
"online": true,
"state": {
"locked": true,
"battery": 85,
"last_activity": "2025-01-15T09:30:00Z"
}
}
4. POST /action?device_id={id}
Execute an action on a device.
Request:
{
"action": "unlock",
"params": {
"duration_seconds": 5
}
}
Response (200):
{
"success": true,
"action": "unlock",
"device_id": "ada-58b6b21b",
"result": {
"message": "Door unlocked for 5 seconds"
}
}
5. GET /ui?device_id={id}
Return an HTML snippet for the guest-facing tile.
Response (200):
Content-Type: text/html
<div class="adapter-tile">
<h3>Door Lock</h3>
<button onclick="unlock()">Unlock Door</button>
<script>
async function unlock() {
// Action logic via parent frame postMessage
}
</script>
</div>
The HTML is rendered in a sandboxed iframe on the magic link page.
6. POST /webhook/register?device_id={id}
Register a callback URL for push events.
Request:
{
"callback_url": "https://api-zenedge.zenspace.io/api/v1/webhooks/adapter/ada-58b6b21b",
"events": ["status_change", "tamper", "low_battery"],
"secret": "hmac-shared-secret"
}
Response (200):
{
"registered": true,
"events": ["status_change", "tamper", "low_battery"]
}
7. POST /webhook/{event}?device_id={id}
Receive push events from the device (adapter → ZenEdge).
This is the callback URL that the adapter POSTs to when events occur.
Payload (from adapter):
{
"event": "status_change",
"device_id": "ada-58b6b21b",
"timestamp": "2025-01-15T10:05:00Z",
"data": {
"locked": false,
"changed_by": "booking-access"
}
}
Next Steps
- device_id Routing — Why v2.0 uses query parameters
- Criticality Tiers — Device failure behavior