---
title: "Delta Sync API over websockets"
method: GET
path: "/api/device/deltasync"
tags: ["Delta Sync"]
---

# Delta Sync API over websockets

`GET /api/device/deltasync`

## Request

- Base URL: `https://passivelogic.com` (the document declares no server; this is the origin it was published from)
- URL: `https://passivelogic.com/api/device/deltasync`
- Auth: one of:
  - API key in header `X-PL-AUTH`
  - API key in header `Authorization: PL-API-KEY`
  - API key in header `PL-API-KEY`
  - HTTP basic
- TypeScript: <https://skmtc.dev/passivelogic/apis/passivelogic-http-api/docs/api/device/deltasync/get.ts> (a function with typed inputs; its response is checked against the schema)

# Delta Sync:

A protocol that keeps a chosen set of fields in sync between a device and the cloud. Only type names, IDs, and plain typed values cross the wire, never Quantum schema encoded objects, so it keeps working across any Quantum version combination, both directions, reactively.

Auth is the device's client certificate (mTLS), and the auth group on the cert bounds what the cloud pushes down and what writes it accepts. No Quantum schema version check here.

On connect the device sends `reconcile` with the values that changed since its watermark and holds its live updates until the cloud confirms with the reconcile's `ack`. After that, both sides stream changes as they happen. A bad node or message never takes down the connection. A node the cloud can't apply comes back with its id and what went wrong. A field it can't read or doesn't recognize is logged and skipped, and the rest of the update still applies. A message it can't read at all comes back as an error saying why. The connection stays open through all of it.

## Supported types and fields:

Right now Delta Sync only carries:

### `property`
- `currentValue` (number)
- `description` (text)

## Delta Sync messageTypes:

### `deltaUpdate`: One or more changed fields on an object, batched so everything from one debounce window travels together. Device to cloud it carries a `requestID` and the cloud answers it with an `ack`, so a batch that fails can be retried. Cloud to device it is a fire and forget push with no ack. A field with a value is set, a field sent as null is cleared, and a field left out is unchanged.
ex)
```json
{
    "messageType": "deltaUpdate",
    "deviceID": "<serialNumber>",
    "requestID": "<uuid>",
    "payload": { "updates": [
        { "property": { "id": "<uuid>", "currentValue": 72.5 } },
        { "property": { "id": "<uuid>", "description": "Supply fan" } },
        { "property": { "id": "<uuid>", "currentValue": 68.0, "description": "Zone 1 setpoint" } },
        { "property": { "id": "<uuid>", "currentValue": null } },
        { "property": { "id": "<uuid>", "description": null } }
    ] }
}
```

### `reconcile`: Device to cloud, sent on connect. The values that changed since the device's watermark, or every synced field on the first connect. The device is the source of truth, so this is a push not a request. Carries a `requestID` and is answered by an `ack`. Same payload shape as `deltaUpdate`.
ex)
```json
{
    "messageType": "reconcile",
    "deviceID": "<serialNumber>",
    "requestID": "<uuid>",
    "payload": {
        "since": "2026-09-04T12:00:00Z",
        "updates": [
            { "property": { "id": "<uuid>", "currentValue": 72.5, "description": "Supply fan" } }
        ]
    }
}
```

### `ack`: Cloud to device. Answers a `reconcile` or a device-sent `deltaUpdate`, echoing its `requestID`. `errors` lists the objects the cloud could not apply, each with a typed `code`. An empty list means everything applied. For a `reconcile` it also lists any property the cloud itself changed since the device's watermark, which the device then sends up, so a reconcile is two sends when the cloud has drifted and one send plus this ack when it has not. Streaming starts after the reconcile's ack.
ex)
```json
{
    "messageType": "ack",
    "deviceID": "<serialNumber>",
    "requestID": "<uuid>",
    "payload": {
        "errors": [
            { "id": "<uuid>", "code": "notFound", "message": "Property not present in the cloud." }
        ],
        "changed": ["<uuid>"]
    }
}
```

### `error`: Either direction. Something was rejected, e.g. bad auth or an unknown message.
ex)
```json
{
    "messageType": "error",
    "deviceID": "<serialNumber>",
    "payload": { "code": "unauthorized", "message": "..." }
}
```

---

[API](https://skmtc.dev/passivelogic/apis/passivelogic-http-api.md) - [All operations](https://skmtc.dev/passivelogic/apis/passivelogic-http-api/llms.txt) - [OpenAPI document](https://skmtc.dev/passivelogic/apis/passivelogic-http-api/revisions/28fce63bcdbb?raw)
