---
title: "One page of the journal of changes"
method: POST
path: "/disk.sync.changes.list"
tags: ["disk"]
---

# One page of the journal of changes

`POST /disk.sync.changes.list`

Hands over what has happened to the files of the caller since the position they name, in the
order the portal published it.

Ask for the next page with the `nextCursor` of the previous one and stop when `hasMore` says
the journal is over. A page that came back short is not the end of it: records fall out to
the confirmation of visibility, and only `hasMore` answers that question. `hasMore` is
decided against the tail the portal considers settled, so the youngest records of the
journal - the ones that may still be on their way in - are held back rather than handed over
and forgotten. That is what disk.sync.capabilities.get publishes as safeWindowSeconds, and
it is applied by the server: a client neither names that boundary nor has to compensate for
it.

Applying the same page twice is safe. The cursor moves only over records the page actually
walked, so a client that failed before it saved the cursor asks with the old one and is
handed the same records again - and applying a change of an object, a tombstone or a change
of access a second time leaves the memory of the client exactly as the first time did.

A record about an object carries the card of that object as it stood when the record was
published. A tombstone and a change of access carry no card at all, which is what lets them
reach a client that can no longer see the object: a tombstone says what the client holds is
to be dropped, a change of access says a boundary of visibility moved and names the scope to
look at again.

`cursor` belongs to the pair of a user and an application, and not to the user alone. A
cursor handed to another application of the same user is refused with CURSOR_INVALID, the
same code as a cursor of another user or a damaged one. The cursor outlives the session it
came with: opening a session again does not annul it, and a client that has just replaced
its session goes on reading the journal from where it stood.

`cursorExpiresAt` is when the position stops being accepted. It is stamped when the session
was created and is not moved by reading: the position handed out by the newest page carries
the same moment as the one the session began with. A session is therefore read for the
retention of the journal counted from its creation - disk.sync.capabilities.get publishes
that as journalRetentionDays - however fresh the position of the client is, and after that
every position of that session is refused with CURSOR_EXPIRED.

A client that means to go on reading creates a session again before that moment. The cheap
way is to do it while caught up: read until `hasMore` says the journal is over, create the
session straight away and go on from the cursor it hands back. In that order nothing is
lost and nothing has to be reconciled - the cursor of a new session is stamped a safe
window back from the moment it was created, so a client that has just reached the end of
the journal is handed a position at or behind its own, and the records it is handed twice
change nothing. The fetch token of that session may simply be left unused: this is not a
reason to run the initial fetch again.

Rebuilding the memory is the answer to a position that was already refused, and only to
that. A client that let its cursor expire cannot learn what happened while it was away -
the journal no longer holds those records - so it reads the initial fetch of a new session
and asks disk.sync.state.check about everything it still holds that the fetch did not name.

`warnings` names what the page could not describe, and `reason` says which of the two it
was. OBJECT_UNAVAILABLE is an object that is still visible to the caller and has no card to
build - it was deleted between the publication of the record and the assembly of the page.
RECORD_UNREADABLE is a record whose own shape is outside this contract: a kind of resource,
a kind of change or a reason for a tombstone the server does not know, which is what a
portal being deployed node by node produces while the rollout lasts. In both cases the page
is formed round such a record and the position moves past it, so one unreadable object
cannot stop the journal of a client for good. The identifier is part of the warning, because
that is what lets a client read the object back - by disk.sync.state.check, which is the
only way a warned record is ever recovered: the journal does not offer it again.

## Request body

- object
  - `cursor` string, required — Where the reading of the journal stands. Opaque: it comes from disk.sync.session.create and then from every page, and is given back exactly as it was received.
  - `limit` integer — How many records of the page are asked for, 100 by default. A value above the ceiling is refused rather than trimmed. A page that asks for a heavy section is served at most 50 records - a shorter page, not a refusal. Both ceilings are published by disk.sync.capabilities.get.
  - `sections` string[] — The sections of a card to fill on the records that carry one. Everything this portal serves when the parameter is absent. A section the portal declares but does not fill is refused when asked for rather than answered empty, and so is a parameter the schema does not declare at all.

## Response `200`

One page of the journal of changes.

- object
  - `result` object
    - `items` BitrixDiskChangedto[] — The records of the page, in the order they were published.
      - `id` string
      - `type` 'created' | 'updated' | 'contentVersion' | 'renamed' | 'moved' | 'trashed' | 'restored' | 'deleted' | 'accessChanged'
      - `occurredAt` string, date-time
      - `resourceType` string
      - `resourceId` integer
      - `authorId` integer
      - `changedFields` unknown[]
        - unknown
      - `operation` string
      - `tombstoneReason` 'deleted' | 'access_lost' | 'interaction_aged' | 'interaction_removed'
      - `accessScope` BitrixDiskAccessscopedto
        - `kind` string
        - `rootId` integer
        - `affectsDescendants` boolean
      - `card` BitrixDiskObjectcarddto
        - `type` string
        - `objectId` integer
        - `name` string
        - `extension` string
        - `mimeType` string
        - `size` integer
        - `versionId` integer
        - `versionNumber` integer
        - `createdAt` string, date-time
        - `updatedAt` string, date-time
        - `createdBy` BitrixDiskEntityrefdto
          - `id` integer
          - `name` string
        - `updatedBy` BitrixDiskEntityrefdto
          - `id` integer
          - `name` string
        - `storageOwner` BitrixDiskEntityrefdto
          - `id` integer
          - `name` string
        - `contentAvailable` boolean
        - `downloadId` string
        - `places` unknown[]
          - unknown
        - `placeExpected` boolean
    - `hasMore` boolean — Whether the journal goes on above this page.
    - `nextCursor` string — The position the next page is asked for by. Answered on the last page as well.
    - `cursorExpiresAt` string, date-time — When the position stops being accepted. Stamped when the session was created and not moved by reading: every page of that session answers the same moment.
    - `warnings` object[] — What this page could not describe. Empty in the usual case.
      - `resourceType` string
      - `resourceId` integer
      - `reason` string

## Other responses

- `400` — FEATURE_NOT_SUPPORTED, CURSOR_EXPIRED, CURSOR_INVALID, BITRIX_REST_V3_EXCEPTION_VALIDATION_REQUESTVALIDATIONEXCEPTION
- `403` — ACCESS_DENIED
- `429` — BITRIX_REST_V3_EXCEPTION_RATELIMITEXCEPTION

## Changes

- **2026-09-29** `f6be0a558c33` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/bitrix24/apis/bitrix24-rest-v3-api/changes/disk.sync.changes.list/post.md)

---

[API](https://skmtc.dev/bitrix24/apis/bitrix24-rest-v3-api.md) · [All operations](https://skmtc.dev/bitrix24/apis/bitrix24-rest-v3-api/llms.txt) · [OpenAPI document](https://skmtc.dev/bitrix24/apis/bitrix24-rest-v3-api/revisions/f6be0a558c33?raw)
