Sync
Incremental, cursor-based synchronisation for offline-first clients. Pull changes since a cursor, push local changes, resolve conflicts with revision numbers.
The sync object
| Field | Type | Description |
|---|---|---|
cursor | string | Opaque position in the change stream. |
changes | object[] | Changed records since the cursor. |
has_more | boolean | Whether another page of changes exists. |
conflicts | object[] | Pushed records rejected because of a newer server revision. |
Example
{
"cursor": "c_0000019c8e12",
"changes": [],
"has_more": false,
"conflicts": []
}POST /sync/pull
Pull changes. Returns changes after the supplied cursor, up to `limit` records.
Body parameters
| Field | Type | Description |
|---|---|---|
cursor | string | Opaque position in the change stream. |
changes | object[] | Changed records since the cursor. |
has_more | boolean | Whether another page of changes exists. |
conflicts | object[] | Pushed records rejected because of a newer server revision. |
Request
curl -X POST "https://api.axnode.net/v2/sync/pull" \
-H "Authorization: Bearer $AXSYNC_TOKEN" \
-H "Content-Type: application/json" \
-d '{"cursor": "c_0000019c8e12", "changes": []}'const res = await fetch('https://api.axnode.net/v2/sync/pull', {
method: 'POST',
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({"cursor": "c_0000019c8e12", "changes": []})
});
const data = await res.json();import requests
res = requests.post(
'https://api.axnode.net/v2/sync/pull',
headers={'Authorization': f'Bearer {token}'}, json={"cursor": "c_0000019c8e12", "changes": []},
)
data = res.json()Response 201
{
"cursor": "c_0000019c8e12",
"changes": [],
"has_more": false,
"conflicts": []
}POST /sync/push
Push changes. Applies local changes atomically per record; conflicts are returned, not overwritten.
Body parameters
| Field | Type | Description |
|---|---|---|
cursor | string | Opaque position in the change stream. |
changes | object[] | Changed records since the cursor. |
has_more | boolean | Whether another page of changes exists. |
conflicts | object[] | Pushed records rejected because of a newer server revision. |
Request
curl -X POST "https://api.axnode.net/v2/sync/push" \
-H "Authorization: Bearer $AXSYNC_TOKEN" \
-H "Content-Type: application/json" \
-d '{"cursor": "c_0000019c8e12", "changes": []}'const res = await fetch('https://api.axnode.net/v2/sync/push', {
method: 'POST',
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({"cursor": "c_0000019c8e12", "changes": []})
});
const data = await res.json();import requests
res = requests.post(
'https://api.axnode.net/v2/sync/push',
headers={'Authorization': f'Bearer {token}'}, json={"cursor": "c_0000019c8e12", "changes": []},
)
data = res.json()Response 201
{
"cursor": "c_0000019c8e12",
"changes": [],
"has_more": false,
"conflicts": []
}GET /sync/stream
Stream changes. Opens a long-lived server-sent events stream of changes. Reconnect with the last cursor to resume without gaps.
Query parameters
| Field | Type | Description |
|---|---|---|
limit | integer | Page size, 1–100. Default 25. |
cursor | string | Cursor from the previous page. |
sort | string | Field to sort by, prefix - for descending. |
filter | string | Filter expression, see Filtering and sorting. |
Request
curl -X GET "https://api.axnode.net/v2/sync/stream" \
-H "Authorization: Bearer $AXSYNC_TOKEN"const res = await fetch('https://api.axnode.net/v2/sync/stream', {
method: 'GET',
headers: { Authorization: `Bearer ${token}` }
});
const data = await res.json();import requests
res = requests.get(
'https://api.axnode.net/v2/sync/stream',
headers={'Authorization': f'Bearer {token}'},
)
data = res.json()Response 200
{
"data": [
{
"cursor": "c_0000019c8e12",
"changes": [],
"has_more": false,
"conflicts": []
}
],
"has_more": false,
"next_cursor": null
}GET /sync/cursor
Get the latest cursor. Returns the newest cursor, useful for bootstrapping a new device.
Query parameters
| Field | Type | Description |
|---|---|---|
limit | integer | Page size, 1–100. Default 25. |
cursor | string | Cursor from the previous page. |
sort | string | Field to sort by, prefix - for descending. |
filter | string | Filter expression, see Filtering and sorting. |
Request
curl -X GET "https://api.axnode.net/v2/sync/cursor" \
-H "Authorization: Bearer $AXSYNC_TOKEN"const res = await fetch('https://api.axnode.net/v2/sync/cursor', {
method: 'GET',
headers: { Authorization: `Bearer ${token}` }
});
const data = await res.json();import requests
res = requests.get(
'https://api.axnode.net/v2/sync/cursor',
headers={'Authorization': f'Bearer {token}'},
)
data = res.json()Response 200
{
"data": [
{
"cursor": "c_0000019c8e12",
"changes": [],
"has_more": false,
"conflicts": []
}
],
"has_more": false,
"next_cursor": null
}GET /sync/devices
List devices. Lists devices that synced in the last 90 days.
Query parameters
| Field | Type | Description |
|---|---|---|
limit | integer | Page size, 1–100. Default 25. |
cursor | string | Cursor from the previous page. |
sort | string | Field to sort by, prefix - for descending. |
filter | string | Filter expression, see Filtering and sorting. |
Request
curl -X GET "https://api.axnode.net/v2/sync/devices" \
-H "Authorization: Bearer $AXSYNC_TOKEN"const res = await fetch('https://api.axnode.net/v2/sync/devices', {
method: 'GET',
headers: { Authorization: `Bearer ${token}` }
});
const data = await res.json();import requests
res = requests.get(
'https://api.axnode.net/v2/sync/devices',
headers={'Authorization': f'Bearer {token}'},
)
data = res.json()Response 200
{
"data": [
{
"cursor": "c_0000019c8e12",
"changes": [],
"has_more": false,
"conflicts": []
}
],
"has_more": false,
"next_cursor": null
}DELETE /sync/devices/{id}
Revoke a device. Removes a device and invalidates its refresh token.
Request
curl -X DELETE "https://api.axnode.net/v2/sync/devices/sy_8f3k2Q9xLm" \
-H "Authorization: Bearer $AXSYNC_TOKEN"const res = await fetch('https://api.axnode.net/v2/sync/devices/sy_8f3k2Q9xLm', {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` }
});
const data = await res.json();import requests
res = requests.delete(
'https://api.axnode.net/v2/sync/devices/sy_8f3k2Q9xLm',
headers={'Authorization': f'Bearer {token}'},
)
data = res.json()Response 200
{
"deleted": true,
"id": "sy_8f3k2Q9xLm"
}