Axsync API v2

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

FieldTypeDescription
cursorstringOpaque position in the change stream.
changesobject[]Changed records since the cursor.
has_morebooleanWhether another page of changes exists.
conflictsobject[]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.

Required scope: sync

Body parameters

FieldTypeDescription
cursorstringOpaque position in the change stream.
changesobject[]Changed records since the cursor.
has_morebooleanWhether another page of changes exists.
conflictsobject[]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.

Required scope: sync

Body parameters

FieldTypeDescription
cursorstringOpaque position in the change stream.
changesobject[]Changed records since the cursor.
has_morebooleanWhether another page of changes exists.
conflictsobject[]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.

Required scope: sync

Query parameters

FieldTypeDescription
limitintegerPage size, 1–100. Default 25.
cursorstringCursor from the previous page.
sortstringField to sort by, prefix - for descending.
filterstringFilter 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.

Required scope: sync

Query parameters

FieldTypeDescription
limitintegerPage size, 1–100. Default 25.
cursorstringCursor from the previous page.
sortstringField to sort by, prefix - for descending.
filterstringFilter 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.

Required scope: sync

Query parameters

FieldTypeDescription
limitintegerPage size, 1–100. Default 25.
cursorstringCursor from the previous page.
sortstringField to sort by, prefix - for descending.
filterstringFilter 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.

Required scope: sync

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"
}