Events
Events tell you what happened at a hub: a door unlocked, a temperature changed, a device joined, the hub went offline. Each hub keeps its own ordered log of events, and 1key keeps the last 24 hours of it. Needs events:read.
Reading events
Read a hub's events with a cursor. Start without one to get the oldest retained events, then pass next_cursor as after each time.
curl "https://api.1keyplatform.dev/v1/hubs/1key-xfy2-nmf4-5xwp/events?after=1042&wait=30" \ -H "Authorization: Bearer $ONEKEY_API_KEY"
{
"data": [
{
"cursor": "1043",
"type": "channel.observed",
"hub_id": "1key-xfy2-nmf4-5xwp",
"device_id": 7,
"time": "2026-10-03T14:03:11.143000Z",
"clock_trusted": true,
"data": { "key": "lock", "endpoint": 0, "value": false }
}
],
"next_cursor": "1043",
"has_more": false
}
import os, requests
API = "https://api.1keyplatform.dev/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['ONEKEY_API_KEY']}"}
def follow(hub_id, cursor=None):
while True:
params = {"wait": 30, **({"after": cursor} if cursor else {})}
res = requests.get(f"{API}/hubs/{hub_id}/events", headers=HEADERS, params=params, timeout=70)
if res.status_code == 410:
cursor = resync(hub_id) # read current state, then continue from the newest event
continue
res.raise_for_status()
page = res.json()
for event in page["data"]:
handle(event) # must tolerate seeing the same event twice
cursor = page["next_cursor"]
save_cursor(hub_id, cursor) # persist after handling
async function follow(hubId, cursor) {
for (;;) {
const url = new URL(`https://api.1keyplatform.dev/v1/hubs/${hubId}/events`);
url.searchParams.set("wait", "30");
if (cursor) url.searchParams.set("after", cursor);
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.ONEKEY_API_KEY}` } });
if (res.status === 410) { cursor = await resync(hubId); continue; }
if (!res.ok) throw new Error(`events: ${res.status}`);
const page = await res.json();
for (const event of page.data) await handle(event);
cursor = page.next_cursor;
await saveCursor(hubId, cursor);
}
}
Streaming
For a held-open connection, use Server-Sent Events:
curl -N https://api.1keyplatform.dev/v1/hubs/1key-xfy2-nmf4-5xwp/events/stream \ -H "Authorization: Bearer $ONEKEY_API_KEY" \ -H "Last-Event-ID: 1042"
id: 1043
event: channel.observed
data: {"cursor":"1043","type":"channel.observed","hub_id":"1key-xfy2-nmf4-5xwp","device_id":7,"time":"2026-10-03T14:03:11.143000Z","clock_trusted":true,"data":{"key":"lock","endpoint":0,"value":false}}
: keepalive
Each message's id is the event cursor. Without Last-Event-ID, the stream starts with the next new event. A : keepalive comment arrives every 30 seconds, and the server ends the stream after 15 minutes. Reconnect with Last-Event-ID set to the last id you processed; SSE clients do this automatically.
Delivery rules
| Rule | What to do |
|---|---|
| Order is per hub | Events from one hub arrive in order. There is no order across hubs. |
| At least once | You may see an event again after a reconnect or retry. Deduplicate on (hub_id, cursor). |
| 24-hour retention | A cursor older than 24 hours gets 410 cursor_expired. |
| Gaps | If 1key could not get some of a hub's events, a hub.events_skipped event says so. Re-read devices for current state. |
Resyncing after 410
410 cursor_expired means events you had not read are gone. Read current state instead of replaying history: call GET /hubs/{hub_id}/devices, then read events without after and continue from the newest one.
Cursors
A cursor looks like 1043. Treat it as an opaque string: store it, compare it for equality, pass it back. Do not parse it or do arithmetic on it.
Event types and their data are listed in the event reference.