Skip to main content

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