[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"docs-chats\u002Fdiscovery\u002Fsync":3,"docs-tabs-chats\u002Fdiscovery\u002Fsync":6},{"content":4,"lastmod":5},"## Chat changes since a moment\n\n`GET \u002Fv1\u002Fchats\u002Fsync`\n\nReturns what changed in the current user's chats since the given moment: new and removed chats, new, edited and deleted messages, pins. Use it to bring a local copy of the chats up to date after a break without re-reading the whole list.\n\n## Parameters\n\n| Parameter | Type | Required | Default | Description |\n|----------|-----|:-----:|-----------|---------|\n| `lastDate` (query) | string | yes | — | The moment to read changes from. Format `YYYY-MM-DDTHH:MM:SS` with an offset or `Z`, without fractional seconds. For the next page, `navigationData.lastServerDate` of the previous one |\n| `lastId` (query) | integer | no | — | `navigationData.lastId` of the previous page, `0` or more. Not passed on the first page |\n| `limit` (query) | integer | no | 50 | Change log entries per page, 1 to 200. A smaller value is raised to 1 and a larger one is clamped to 200, both echoed in `meta` |\n\nOther parameters and repeated parameters are rejected with `400 INVALID_PARAMS`.\n\n**Pagination.** Request the first page with `lastDate` set to the moment up to which your local copy is current. While `navigationData.hasMore` is `true`, request the next one with `lastDate` equal to `navigationData.lastServerDate` and `lastId` equal to `navigationData.lastId`. Save the `lastServerDate` and `lastId` pair of the last page: the next sync starts from it.\n\n## Examples\n\n### curl — personal key\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fchats\u002Fsync?lastDate=2026-09-24T10:00:00Z&limit=200\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth application\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fchats\u002Fsync?lastDate=2026-09-24T10:00:00Z&limit=200\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — personal key\n\n```javascript\nlet lastDate = '2026-09-24T10:00:00Z'\nlet lastId\nconst added = new Set()\nfor (;;) {\n  const url = new URL('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fchats\u002Fsync')\n  url.searchParams.set('lastDate', lastDate)\n  url.searchParams.set('limit', '200')\n  if (lastId !== undefined) url.searchParams.set('lastId', String(lastId))\n  const res = await fetch(url, { headers: { 'X-Api-Key': 'YOUR_API_KEY' } })\n  const { data } = await res.json()\n  if (Array.isArray(data)) throw new Error('Incremental sync is unavailable; refresh the full recent-chat list')\n  for (const id of Object.values(data.messageSync.addedMessages)) added.add(id)\n  lastDate = data.navigationData.lastServerDate\n  lastId = data.navigationData.lastId\n  if (!data.navigationData.hasMore) break\n}\n```\n\n### JavaScript — OAuth application\n\n```javascript\nlet lastDate = '2026-09-24T10:00:00Z'\nlet lastId\nconst added = new Set()\nfor (;;) {\n  const url = new URL('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fchats\u002Fsync')\n  url.searchParams.set('lastDate', lastDate)\n  url.searchParams.set('limit', '200')\n  if (lastId !== undefined) url.searchParams.set('lastId', String(lastId))\n  const res = await fetch(url, {\n    headers: {\n      'X-Api-Key': 'YOUR_APP_KEY',\n      'Authorization': 'Bearer USER_SESSION_TOKEN',\n    },\n  })\n  const { data } = await res.json()\n  if (Array.isArray(data)) throw new Error('Incremental sync is unavailable; refresh the full recent-chat list')\n  for (const id of Object.values(data.messageSync.addedMessages)) added.add(id)\n  lastDate = data.navigationData.lastServerDate\n  lastId = data.navigationData.lastId\n  if (!data.navigationData.hasMore) break\n}\n```\n\n## Response fields\n\n| Field | Type | Description |\n|------|-----|---------|\n| `success` | boolean | Always `true` on success |\n| `data.chatSync.addedRecent` | object | Chats that appeared in the dialog list: key and value are the chat ID |\n| `data.chatSync.addedChats` | object | Chats that appeared or changed for the user: key and value are the chat ID |\n| `data.chatSync.deletedChats` | object | Chats removed from the user's dialog list: key and value are the chat ID |\n| `data.chatSync.completeDeletedChats` | object | Chats deleted from the Bitrix24 account: key and value are the chat ID |\n| `data.chatSync.readAllChats` | boolean | Arrives as `true` if the user [read all chats](\u002Fdocs\u002Fchats\u002Fmanagement\u002Fread-all) during this period: reset the unread counters in the local copy |\n| `data.messageSync.addedMessages` | object | New messages: key and value are the message ID |\n| `data.messageSync.updatedMessages` | object | Edited messages: key and value are the message ID |\n| `data.messageSync.completeDeletedMessages` | object | Deleted messages: key and value are the message ID |\n| `data.pinSync.addedPins` | object | New pinned messages: key and value are the pin ID |\n| `data.pinSync.deletedPins` | object | Removed message pins: key and value are the pin ID |\n| `data.userSync` | object | User changes: `updatedUsers` and `deletedUsers` |\n| `data.chats` | array | Chat cards for the chats from `chatSync` |\n| `data.messages` | array | Messages from `messageSync` |\n| `data.pins` | array | Message pins from `pinSync`: `id`, `messageId`, `chatId`, `authorId`, `dateCreate` |\n| `data.recentItems` | array | Dialog list rows for the chats from `chatSync` |\n| `data.users` | array | Users mentioned in the changes |\n| `data.dialogIds` | object | Mapping of chat ID to dialog ID: key is the chat ID, value is the `dialogId` |\n| `data.navigationData.lastServerDate` | string | `lastDate` for the next page: the moment of the last change log entry on this page |\n| `data.navigationData.lastId` | number | `lastId` for the next page |\n| `data.navigationData.hasMore` | boolean | `true` if there is a next page |\n| `meta.requestedLimit` | number | The `limit` passed. It arrives together with `appliedLimit`, only when the value was outside the range of 1 to 200 |\n| `meta.appliedLimit` | number | The applied `limit`, only when it was clamped to a bound |\n\n## Response example\n\nThe main fields are shown:\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"chatSync\": {\n      \"addedRecent\": { \"264431\": 264431 },\n      \"addedChats\": { \"264431\": 264431 },\n      \"deletedChats\": { \"264418\": 264418 },\n      \"completeDeletedChats\": []\n    },\n    \"messageSync\": {\n      \"addedMessages\": { \"609001\": 609001 },\n      \"updatedMessages\": [],\n      \"completeDeletedMessages\": []\n    },\n    \"pinSync\": {\n      \"addedPins\": [],\n      \"deletedPins\": []\n    },\n    \"userSync\": {\n      \"updatedUsers\": [],\n      \"deletedUsers\": []\n    },\n    \"messages\": [\n      {\n        \"id\": 609001,\n        \"chatId\": 264431,\n        \"authorId\": 1,\n        \"date\": \"2026-09-24T21:10:22+00:00\",\n        \"text\": \"The draft plan is ready\"\n      }\n    ],\n    \"chats\": [\n      {\n        \"id\": 264431,\n        \"dialogId\": \"chat264431\",\n        \"name\": \"Release preparation\",\n        \"type\": \"chat\"\n      }\n    ],\n    \"dialogIds\": { \"264431\": \"chat264431\" },\n    \"navigationData\": {\n      \"lastServerDate\": \"2026-09-24T21:10:22+00:00\",\n      \"hasMore\": false,\n      \"lastId\": 182091\n    }\n  }\n}\n```\n\n## Error response example\n\n400 — a date without an offset:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_PARAMS\",\n    \"message\": \"`lastDate` must be a datetime YYYY-MM-DDTHH:MM:SS with a UTC offset (+03:00) or Z and no fractional seconds — `navigationData.lastServerDate` of the previous page.\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|---------|\n| 400 | `INVALID_PARAMS` | No `lastDate`, `lastDate` not in the `YYYY-MM-DDTHH:MM:SS` format with an offset or `Z`, or with a nonexistent date or time (`2026-02-30`, `24:00`), `lastId` less than `0`, a non-numeric `limit`, a parameter other than `lastDate`, `lastId` and `limit`, or a repeated parameter. Checked before the Bitrix24 call |\n| 422 | `BITRIX_ERROR` | Bitrix24 returned an error; the Bitrix24 code is in `error.b24Code` |\n| 403 | `BITRIX_ACCESS_DENIED` | Bitrix24 denied access |\n| 403 | `SCOPE_DENIED` | The API key does not have the `im` scope |\n| 401 | `TOKEN_MISSING` | The API key has no Bitrix24 tokens configured |\n| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 is unavailable or returned a server error |\n\nFull list of common API errors — [Errors](\u002Fdocs\u002Ferrors).\n\n## Known specifics\n\n**An empty collection arrives as an array.** When a change group has no entries, an empty array arrives instead of an object: `\"updatedMessages\": []`. Read such fields with `Object.values`, which handles both forms the same way.\n\n**The incremental log does not guarantee four weeks of history.** Some Bitrix24 change records remain for only about a day. After a long interruption, especially one approaching a day, re-read [recent dialogs](\u002Fdocs\u002Fchats\u002Fdiscovery\u002Frecent) and the needed message history in full before trusting a new delta.\n\n**Sync can be turned off on a Bitrix24 account.** Then `data` is an empty array, not an object with the fields above. In that case, re-read the list in full.\n\n**These are not real-time events.** The endpoint answers \"what has changed since a given time\"; to receive new messages as they appear, use [event polling](\u002Fdocs\u002Fchats\u002Fevents\u002Fpoll).\n\n## See also\n\n- [Recent dialogs](\u002Fdocs\u002Fchats\u002Fdiscovery\u002Frecent)\n- [Get events](\u002Fdocs\u002Fchats\u002Fevents\u002Fpoll)\n- [Chat discovery](\u002Fdocs\u002Fchats\u002Fdiscovery)\n","2026-10-07",{},1794739536150]