Documentation contents

VCP 1 Documentation›API Reference

Delta Sync

The optional changes feed that lets clients re-sync a large imported library without re-crawling it.

Advertise the changes keyword in your manifest'scapabilities to mount this route. Without it, clients re-sync by re-listing everything and comparing updated_at — which works, but costs far more requests on both sides.

GET/changes?since={cursor}

# At first import: no "since" param -> empty list + the current cursor
curl "https://api.example.com/vcp/v1/changes" \
  -H "VCP-Service-Token: vcps_a81f42..."

# Every re-sync after that: pass the stored cursor
curl "https://api.example.com/vcp/v1/changes?since=chg_8842&limit=200" \
  -H "VCP-Service-Token: vcps_a81f42..."
200 OK
{
  "changes": [
    { "type": "video.updated", "id": "vid_88213", "updated_at": "2026-07-15T02:00:00Z" },
    { "type": "video.deleted", "id": "vid_11209" },
    { "type": "collection.updated", "id": "col_summer24" },
    { "type": "collection.deleted", "id": "col_old" }
  ],
  "next_since": "chg_9016"
}

How it works

  • Bootstrap — at first import the client calls GET /changeswith no since. You return an empty list plus the currentnext_since cursor, which the client stores.
  • Re-sync — the client passes its stored cursor and applies each entry:*.updated means refetch that object, *.deleted means remove it locally. It then stores the new next_since.
  • Entry types follow the pattern<object>.<created|updated|deleted> —video.updated, collection.deleted, and so on.

Cursor lifetime

since cursors must stay valid for at least 30 days. If you can't honor a cursor (it's too old, your change log was truncated), return 410 gone — that signals the client to do a full re-crawl and bootstrap a new cursor.

Coming soon!

We're putting the finishing touches on VClarity. It'll be on the App Store and Google Play very soon.