---
title: "Получить изменения, случившиеся после момента, названного курсором"
method: GET
path: "/api/v1/reference/changes"
tags: ["Core reference"]
---

# Получить изменения, случившиеся после момента, названного курсором

`GET /api/v1/reference/changes`

## Request

- Base URL: `https://erp.akeda.ru`
- URL: `https://erp.akeda.ru/api/v1/reference/changes`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-Api-Key`
  - OAuth 2 (authorization code, authorization URL `https://erp.akeda.ru/oauth2/authorize`, token URL `https://erp.akeda.ru/oauth2/token`, scopes: `akeda:delegate`)

Дельта-чтение: «что изменилось с момента X». Одна операция на все сущности — сущность называется параметром `entity`, а не отдельным адресом, поэтому интегратор учит одну форму и хранит один курсор.

Ответ несёт ИДЕНТИЧНОСТЬ изменения, а не объект: сущность, идентификатор, вид изменения и момент. Сам объект читатель забирает штатной операцией под своими правами — у ленты нет своего сериализатора, значит нет и второго места, где можно отдать поле, которого читателю видеть нельзя.

Первый вызов делается БЕЗ курсора и данных не отдаёт: он возвращает пустой список и позицию. Порядок такой — снять курсор, вычитать данные штатными операциями, продолжить с курсора; лента не является выгрузкой и строк, не менявшихся с момента заведения журнала, не содержит.

Гарантия — «НЕ МЕНЬШЕ одного раза». Один объект может приехать дважды: строка, изменённая повторно, приезжает с новым изменением, а курсор, предъявленный второй раз, отдаёт ту же страницу. Читатель обязан сводить по паре `entity` + `id`. «Ровно один раз» не обещано и обещано не будет.

`has_more` означает «в этом прогоне есть ещё», а не «в кабинете больше ничего не менялось»: ложь закрывает прогон, следующий запрос откроет новый и увидит случившееся после.

Курсор непрозрачен: его возвращают как есть, не разбирая и не собирая. Он привязан к кабинету и к набору сущностей; предъявленный в другом кабинете или с другим `entity` отклоняется. Курсор старше срока хранения журнала — 30 суток — отвечает 409 `changes.cursor_expired`: за ним стоят вычищенные строки, и молчание вместо отказа означало бы тихую потерю.

## Query parameters

- `entity` string
- `cursor` string
- `limit` integer

## Headers

- `X-Tenant` string, required

## Response `200`

Страница изменений и курсор продолжения

- CoreChangeFeedPage
  - `entities` string[], required: Сущности, которые эта лента обслуживает предъявителю
  - `count` integer, required: Число строк, а не прогонов
  - `limit` integer, required
  - `has_more` boolean, required: «В этом прогоне есть ещё». Ложь закрывает прогон, а не кабинет: следующий запрос увидит случившееся после
  - `cursor` string, required: Непрозрачная строка. Возвращается как есть; разбирать и собирать её нельзя
  - `changes` CoreChange[], required
    - `entity` string, required: Имя сущности из реестра ленты (core.contact)
    - `id` string, required: Идентификатор объекта в его собственном API
    - `op` 'upsert' | 'delete', required: Что случилось со СТРОКОЙ. `upsert` — строка есть: заведена или изменена. `delete` — строки больше нет в кабинете. Архив, скрытие и пометка на удаление — это `upsert`: строка на месте, поменялось её состояние, и увидит его читатель штатным чтением объекта. Если это чтение отвечает 404, объект читателю больше не доступен, и копию он снимает так же, как по `delete`.
    - `changed_at` string, date-time, required: Момент изменения. Для человека и для журнала; порядок ленты задаёт не он, а фиксация транзакции, поэтому фильтровать по нему на своей стороне нельзя

## Other responses

- `400`: Некорректный запрос или нарушение бизнес-правила
- `401`: Сессия, ключ или кабинет не подтверждены
- `403`: Текущая роль не имеет требуемого права
- `409`: Конфликт версии, идемпотентности или бизнес-состояния
- `500`: Внутренняя ошибка без раскрытия server-only деталей

---

[API](https://skmtc.dev/akeda/apis/akeda-erp-client-api.md) · [All operations](https://skmtc.dev/akeda/apis/akeda-erp-client-api/llms.txt) · [OpenAPI document](https://skmtc.dev/akeda/apis/akeda-erp-client-api/revisions/3c60ded179f2?raw)
