Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/olive-pianos-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

Migrate chat.syncMessages endpoint to new API structure with schema validation while preserving existing behavior and API compatibility
188 changes: 142 additions & 46 deletions apps/meteor/app/api/server/v1/chat.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Message } from '@rocket.chat/core-services';
import type { IMessage, IThreadMainMessage } from '@rocket.chat/core-typings';
import type { IMessage, IThreadMainMessage, IRoom } from '@rocket.chat/core-typings';
import { MessageTypes } from '@rocket.chat/message-types';
import { Messages, Users, Rooms, Subscriptions } from '@rocket.chat/models';
import {
Expand All @@ -9,7 +9,6 @@ import {
isChatUpdateProps,
isChatGetThreadsListProps,
isChatDeleteProps,
isChatSyncMessagesProps,
isChatGetMessageProps,
isChatPostMessageProps,
isChatSearchProps,
Expand Down Expand Up @@ -54,6 +53,7 @@ import type { ExtractRoutesFromAPI } from '../ApiClass';
import { API } from '../api';
import { getPaginationItems } from '../helpers/getPaginationItems';
import { findDiscussionsFromRoom, findMentionedMessages, findStarredMessages } from '../lib/messages';
import { object } from 'underscore';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused import.

The object function from underscore is imported but never used anywhere in this file.

🧹 Proposed fix
-import { object } from 'underscore';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { object } from 'underscore';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/app/api/server/v1/chat.ts` at line 56, Remove the unused import
"object" from underscore in the file by deleting the import statement that reads
`import { object } from 'underscore';` (no other code changes required); ensure
no other references to the symbol `object` exist in functions within this file
(e.g., chat handlers) and run the linter/TypeScript compiler to confirm the
unused-import warning is resolved.


type ChatStarMessageLocal = {
messageId: IMessage['_id'];
Expand Down Expand Up @@ -168,50 +168,6 @@ API.v1.addRoute(
},
);

API.v1.addRoute(
'chat.syncMessages',
{ authRequired: true, validateParams: isChatSyncMessagesProps },
{
async get() {
const { roomId, lastUpdate, count, next, previous, type } = this.queryParams;

if (!roomId) {
throw new Meteor.Error('error-param-required', 'The required "roomId" query param is missing');
}

if (!lastUpdate && !type) {
throw new Meteor.Error('error-param-required', 'The "type" or "lastUpdate" parameters must be provided');
}

if (lastUpdate && isNaN(Date.parse(lastUpdate))) {
throw new Meteor.Error('error-lastUpdate-param-invalid', 'The "lastUpdate" query parameter must be a valid date');
}

const getMessagesQuery = {
...(lastUpdate && { lastUpdate: new Date(lastUpdate) }),
...(next && { next }),
...(previous && { previous }),
...(count && { count }),
...(type && { type }),
};

const result = await getMessageHistory(roomId, this.userId, getMessagesQuery);

if (!result) {
return API.v1.failure();
}

return API.v1.success({
result: {
updated: 'updated' in result ? await normalizeMessagesForUser(result.updated, this.userId) : [],
deleted: 'deleted' in result ? result.deleted : [],
cursor: 'cursor' in result ? result.cursor : undefined,
},
});
},
},
);

API.v1.addRoute(
'chat.getMessage',
{
Expand Down Expand Up @@ -275,6 +231,97 @@ const isChatPinMessageProps = ajv.compile<ChatPinMessage>(ChatPinMessageSchema);

const isChatUnpinMessageProps = ajv.compile<ChatUnpinMessage>(ChatUnpinMessageSchema);

type ChatSyncMessages = {
roomId: IRoom['_id'];
lastUpdate?: string;
count?: number;
next?: string;
previous?: string;
type?: 'UPDATED' | 'DELETED';
};

const ChatSyncMessagesSchema = {
type: 'object',
properties: {
roomId: {
type: 'string',
},
lastUpdate: {
type: 'string',
nullable: true,
},
count: {
type: 'number',
nullable: true,
},
next: {
type: 'string',
nullable: true,
},
previous: {
type: 'string',
nullable: true,
},
type: {
type: 'string',
enum: ['UPDATED', 'DELETED'],
nullable: true,
},
},
required: ['roomId'],
additionalProperties: false,
};

const isChatSyncMessagesLocalProps = ajv.compile<ChatSyncMessages>(ChatSyncMessagesSchema);

const isSyncMessagesResponse = ajv.compile<{
result: {
updated: IMessage[];
deleted: IMessage[];
cursor?: {
next: string | null;
previous: string | null;
};
};
}>({
type: 'object',
properties: {
result: {
type: 'object',
properties: {
updated: {
type: 'array',
items: { $ref: '#/components/schemas/IMessage' },
},
deleted: {
type: 'array',
items: {
type: 'object',
additionalProperties: true,
},
},
cursor: {
type: 'object',
properties: {
next: { type: ['string', 'null'] },
previous: { type: ['string', 'null'] },
},
required: ['next', 'previous'],
additionalProperties: false,
},
},
required: ['updated', 'deleted'],
additionalProperties: false,
},
success: {
type: 'boolean',
enum: [true],
},
},
required: ['result', 'success'],
additionalProperties: false,
});

const chatEndpoints = API.v1
.post(
'chat.pinMessage',
Expand Down Expand Up @@ -558,6 +605,55 @@ const chatEndpoints = API.v1

return API.v1.success();
},
)
.get(
'chat.syncMessages',
{
authRequired: true,
query: isChatSyncMessagesLocalProps,
response: {
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
200: isSyncMessagesResponse,
},
},
async function action() {
const { roomId, lastUpdate, count, next, previous, type } = this.queryParams;

if (!roomId) {
throw new Meteor.Error('error-param-required', 'The required "roomId" query param is missing');
}

if (!lastUpdate && !type) {
throw new Meteor.Error('error-param-required', 'The "type" or "lastUpdate" parameters must be provided');
}

if (lastUpdate && isNaN(Date.parse(lastUpdate))) {
throw new Meteor.Error('error-lastUpdate-param-invalid', 'The "lastUpdate" query parameter must be a valid date');
}

const getMessagesQuery = {
...(lastUpdate ? { lastUpdate: new Date(lastUpdate) } : {}),
...(next ? { next } : {}),
...(previous ? { previous } : {}),
...(count ? { count } : {}),
...(type ? { type } : {}),
};
Comment on lines +636 to +642
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Mar 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Preserve explicit count=0 when building getMessagesQuery.

Line 633 uses a truthy check, so count: 0 is dropped and downstream logic falls back to defaults instead of honoring the explicit value.

Suggested fix
 			const getMessagesQuery = {
 				...(lastUpdate && { lastUpdate: new Date(lastUpdate) }),
 				...(next && { next }),
 				...(previous && { previous }),
-				...(count && { count }),
+				...(typeof count === 'number' && { count }),
 				...(type && { type }),
 			};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getMessagesQuery = {
...(lastUpdate && { lastUpdate: new Date(lastUpdate) }),
...(next && { next }),
...(previous && { previous }),
...(count && { count }),
...(type && { type }),
};
const getMessagesQuery = {
...(lastUpdate && { lastUpdate: new Date(lastUpdate) }),
...(next && { next }),
...(previous && { previous }),
...(typeof count === 'number' && { count }),
...(type && { type }),
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/app/api/server/v1/chat.ts` around lines 629 - 635, The object
builder for getMessagesQuery in chat.ts drops explicit count=0 due to a truthy
check; update the spread for count to include the property when count is not
undefined (e.g., use a presence check like count !== undefined or
Number.isInteger(count)) so that getMessagesQuery contains count: 0 when caller
passes 0; modify the spread expression where count is referenced (the ...(count
&& { count }) part) to use the non-undefined test instead and keep the rest of
the query construction unchanged.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.

As per maintainer guidance, I’m preserving the current behavior for now. If needed, I can update this to explicitly handle count: 0.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Harxhit, understood — no problem! If the maintainer guidance is to keep the truthy check for now, that's a reasonable call. Just something to keep in mind if count: 0 cases surface in the future. 🐇


🧠 Learnings used
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 39003
File: apps/meteor/client/lib/chats/flows/sendMessage.ts:39-45
Timestamp: 2026-02-24T19:16:35.307Z
Learning: In apps/meteor/client/lib/chats/flows/sendMessage.ts, when sdk.call('sendMessage', ...) throws an error, the message is intentionally left with temp: true (not deleted or cleaned up) to support a future retry UI feature. This allows users to retry sending failed messages rather than losing them.

Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 39545
File: apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts:59-61
Timestamp: 2026-03-11T22:04:20.529Z
Learning: In `apps/meteor/client/views/room/body/hooks/useHasNewMessages.ts`, the `msg.u._id === uid` early-return in the `streamNewMessage` handler is intentional: the "New messages" indicator is designed to notify about messages from other users only. Self-sent messages — including those sent from a different session/device — are always skipped, by design. Do not flag this as a multi-session regression.

Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 39677
File: packages/models/src/helpers/omnichannel/agentStatus.ts:10-29
Timestamp: 2026-03-16T22:56:50.405Z
Learning: In `packages/models/src/helpers/omnichannel/agentStatus.ts` (PR `#39677`), the `queryStatusAgentOnline` function intentionally omits the `$or` offline-status guard for non-bot agents when `isLivechatEnabledWhenAgentIdle === true`. This is by design: the setting `Livechat_enabled_when_agent_idle` (`accept_chats_when_agent_idle`) means agents should receive chats even when idle/offline, so the offline filter must be removed in that path. Bots are always status-agnostic and are always included regardless of their online/offline status. Do not flag this as a bug.

Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 39230
File: apps/meteor/app/api/server/v1/chat.ts:214-222
Timestamp: 2026-03-03T11:11:48.541Z
Learning: In apps/meteor/server/lib/moderation/reportMessage.ts, the reportMessage function validates that description is not empty or whitespace-only with `if (!description.trim())`. When migrating the chat.reportMessage endpoint to OpenAPI, adding minLength validation to the schema preserves this existing behavior.

Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 38974
File: apps/meteor/app/api/server/v1/im.ts:220-221
Timestamp: 2026-02-24T19:09:01.522Z
Learning: In Rocket.Chat OpenAPI migration PRs for endpoints under apps/meteor/app/api/server/v1, avoid introducing logic changes. Only perform scope-tight changes that preserve behavior; style-only cleanups (e.g., removing inline comments) may be deferred to follow-ups to keep the migration PR focused.

Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 39535
File: apps/meteor/app/apps/server/bridges/livechat.ts:249-249
Timestamp: 2026-03-11T16:46:55.955Z
Learning: In `apps/meteor/app/apps/server/bridges/livechat.ts`, `createVisitor()` intentionally does not propagate `externalIds` to `registerData`. This is by design: the method is deprecated (JSDoc: `deprecated Use createAndReturnVisitor instead. Note: This method does not support externalIds.`) to push callers toward `createAndReturnVisitor()`, which does support `externalIds`. Do not flag the missing `externalIds` field in `createVisitor()` as a bug.

Learnt from: amitb0ra
Repo: RocketChat/Rocket.Chat PR: 39647
File: apps/meteor/app/api/server/v1/users.ts:710-757
Timestamp: 2026-03-15T14:31:28.969Z
Learning: In RocketChat/Rocket.Chat, the `UserCreateParamsPOST` type in `apps/meteor/app/api/server/v1/users.ts` (migrated from `packages/rest-typings/src/v1/users/UserCreateParamsPOST.ts`) intentionally has `fields: string` (non-optional) and `settings?: IUserSettings` without a corresponding AJV schema entry. This is a pre-existing divergence carried over verbatim from the original rest-typings source (PR `#39647`). Do not flag this type/schema misalignment during the OpenAPI migration review — it is tracked as a separate follow-up fix.

Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 39414
File: apps/meteor/app/api/server/v1/rooms.ts:1241-1297
Timestamp: 2026-03-10T08:13:52.153Z
Learning: In the RocketChat/Rocket.Chat OpenAPI migration PRs for endpoints under apps/meteor/app/api/server/v1/rooms.ts, the pattern `ajv.compile<void>({...})` is intentionally used for the 200 response schema even when the endpoint returns `{ success: true }`. This is an established convention across all migrated endpoints (rooms.leave, rooms.favorite, rooms.delete, rooms.muteUser, rooms.unmuteUser). Do not flag this as a type mismatch during reviews of these migration PRs.

Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37423
File: packages/i18n/src/locales/en.i18n.json:18-18
Timestamp: 2025-11-07T14:50:33.544Z
Learning: Rocket.Chat settings: in apps/meteor/ee/server/settings/abac.ts, the Abac_Cache_Decision_Time_Seconds setting uses invalidValue: 0 as the fallback when ABAC is unlicensed. With a valid license, admins can still set the value to 0 to intentionally disable the ABAC decision cache.

Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 38227
File: apps/meteor/app/api/server/router.ts:44-49
Timestamp: 2026-01-26T18:26:01.279Z
Learning: In apps/meteor/app/api/server/router.ts, when retrieving bodyParams and queryParams from the Hono context via c.get(), do not add defensive defaults (e.g., ?? {}). The code should fail fast if these parameters are missing, as endpoint handlers expect them to be present and breaking here helps surface parsing problems rather than hiding them.

Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38219
File: packages/core-typings/src/cloud/Announcement.ts:5-6
Timestamp: 2026-01-17T01:51:47.764Z
Learning: In packages/core-typings/src/cloud/Announcement.ts, the AnnouncementSchema.createdBy field intentionally overrides IBannerSchema.createdBy (object with _id and optional username) with a string enum ['cloud', 'system'] to match existing runtime behavior. This is documented as technical debt with a FIXME comment at apps/meteor/app/cloud/server/functions/syncWorkspace/handleCommsSync.ts:53 and should not be flagged as an error until the runtime behavior is corrected.

Learnt from: smirk-dev
Repo: RocketChat/Rocket.Chat PR: 39625
File: apps/meteor/app/api/server/v1/push.ts:85-97
Timestamp: 2026-03-14T14:58:58.834Z
Learning: In RocketChat/Rocket.Chat, the `push.token` POST/DELETE endpoints in `apps/meteor/app/api/server/v1/push.ts` were already migrated to the chained router API pattern on `develop` prior to PR `#39625`. `cleanTokenResult` (which strips `authToken` and returns `PushTokenResult`) and `isPushTokenPOSTProps`/`isPushTokenDELETEProps` validators already exist on `develop`. PR `#39625` only migrates `push.get` and `push.info` to the chained pattern. Do not flag `cleanTokenResult` or `PushTokenResult` as newly introduced behavior-breaking changes when reviewing this PR.

Learnt from: amitb0ra
Repo: RocketChat/Rocket.Chat PR: 39676
File: .changeset/migrate-users-register-openapi.md:3-3
Timestamp: 2026-03-16T21:50:37.589Z
Learning: In RocketChat/Rocket.Chat OpenAPI migration PRs, removing endpoint types and validators from `rocket.chat/rest-typings` (e.g., `UserRegisterParamsPOST`, `/v1/users.register` entry) is the *required* migration pattern per RocketChat/Rocket.Chat-Open-API#150 Rule 7 ("No More rest-typings or Manual Typings"). The endpoint type is re-exposed via a module augmentation `.d.ts` file in the consuming package (e.g., `packages/web-ui-registration/src/users-register.d.ts`). This is NOT a breaking change — the correct changeset bump for `rocket.chat/rest-typings` in this scenario is `minor`, not `major`. Do not flag this as a breaking change during OpenAPI migration reviews.

Learnt from: ggazzo
Repo: RocketChat/Rocket.Chat PR: 35995
File: apps/meteor/app/api/server/v1/rooms.ts:1107-1112
Timestamp: 2026-02-23T17:53:06.802Z
Learning: During PR reviews that touch endpoint files under apps/meteor/app/api/server/v1, enforce strict scope: if a PR targets a specific endpoint (e.g., rooms.favorite), do not propose changes to unrelated endpoints (e.g., rooms.invite) unless maintainers explicitly request them. Focus feedback on the touched endpoint's behavior, API surface, and related tests; avoid broad cross-endpoint changes in the same PR unless requested.

Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 39340
File: apps/meteor/app/api/server/v1/im.ts:1349-1398
Timestamp: 2026-03-12T10:26:26.697Z
Learning: In `apps/meteor/app/api/server/v1/im.ts` (PR `#39340`), the `DmEndpoints` type intentionally includes temporary stub entries for `/v1/im.kick`, `/v1/dm.kick`, `/v1/im.leave`, and `/v1/dm.leave` (using `DmKickProps` and `DmLeaveProps`) even though no route handlers exist for them yet. These stubs were added to preserve type compatibility after removing the original `DmLeaveProps` and related files. They are planned for cleanup in a follow-up PR. Do not flag these as missing implementations when reviewing this file until the follow-up is merged.

Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 0
File: :0-0
Timestamp: 2026-02-24T19:05:56.710Z
Learning: Rocket.Chat repo context: When a workspace manifest on develop already pins a dependency version (e.g., packages/web-ui-registration → "rocket.chat/ui-contexts": "27.0.1"), a lockfile change in a feature PR that upgrades only that dependency’s resolution is considered a manifest-driven sync and can be kept, preferably as a small "chore: sync yarn.lock with manifests" commit.

Learnt from: amitb0ra
Repo: RocketChat/Rocket.Chat PR: 39676
File: apps/meteor/app/api/server/v1/users.ts:862-869
Timestamp: 2026-03-16T23:33:11.443Z
Learning: In RocketChat/Rocket.Chat OpenAPI migration PRs (e.g., PR `#39676` for users.register in apps/meteor/app/api/server/v1/users.ts), calls to `this.parseJsonQuery()` inside migrated handlers are intentionally preserved without adding a corresponding `query` AJV schema to the route options. Adding query-param schemas for the `fields`/`sort`/`query` parameters consumed by `parseJsonQuery()` is a separate cross-cutting concern shared by many endpoints (e.g., users.create, users.update, users.list) and is explicitly out of scope for individual endpoint migration PRs. Do not flag the absence of a `query` schema for `parseJsonQuery()` usage as a violation of OpenAPI/AJV contract during migration reviews.

Learnt from: amitb0ra
Repo: RocketChat/Rocket.Chat PR: 39647
File: apps/meteor/app/api/server/v1/users.ts:891-899
Timestamp: 2026-03-15T14:31:23.493Z
Learning: In RocketChat/Rocket.Chat, `IUser.inactiveReason` in `packages/core-typings/src/IUser.ts` is typed as `'deactivated' | 'pending_approval' | 'idle_too_long'` (optional, no `null`), but the database stores `null` for newly created users. The Typia-generated `$ref: '#/components/schemas/IUser'` schema therefore correctly rejects `null` for `inactiveReason`. This causes the test "should create a new user with default roles" to fail when response validation is active (TEST_MODE). The fix is to add `| null` to `inactiveReason` in core-typings and rebuild Typia schemas in a separate PR. Do not flag this test failure as a bug introduced by the users.create OpenAPI migration (PR `#39647`). Do not suggest inlining a custom schema to work around it, as migration rules require using `$ref` when a Typia schema exists.

Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.


const result = await getMessageHistory(roomId, this.userId, getMessagesQuery);

if (!result) {
return API.v1.failure();
}

return API.v1.success({
result: {
updated: 'updated' in result ? await normalizeMessagesForUser(result.updated, this.userId) : [],
deleted: 'deleted' in result ? result.deleted : [],
cursor: 'cursor' in result ? result.cursor : undefined,
},
});
},
);

API.v1.addRoute(
Expand Down
54 changes: 2 additions & 52 deletions packages/rest-typings/src/v1/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -595,48 +595,10 @@ const GetMentionedMessagesSchema = {

export const isChatGetMentionedMessagesProps = ajv.compile<GetMentionedMessages>(GetMentionedMessagesSchema);

type ChatSyncMessages = {
roomId: IRoom['_id'];
lastUpdate?: string;
count?: number;
next?: string;
previous?: string;
type?: 'UPDATED' | 'DELETED';
};

const ChatSyncMessagesSchema = {
type: 'object',
properties: {
roomId: {
type: 'string',
},
lastUpdate: {
type: 'string',
nullable: true,
},
count: {
type: 'number',
nullable: true,
},
next: {
type: 'string',
nullable: true,
},
previous: {
type: 'string',
nullable: true,
},
type: {
type: 'string',
enum: ['UPDATED', 'DELETED'],
nullable: true,
},
},
required: ['roomId'],
additionalProperties: false,
};

export const isChatSyncMessagesProps = ajv.compile<ChatSyncMessages>(ChatSyncMessagesSchema);



type ChatSyncThreadMessages = PaginatedRequest<{
tmid: string;
Expand Down Expand Up @@ -970,18 +932,6 @@ export type ChatEndpoints = {
total: number;
};
};
'/v1/chat.syncMessages': {
GET: (params: ChatSyncMessages) => {
result: {
updated: IMessage[];
deleted: IMessage[];
cursor: {
next: string | null;
previous: string | null;
};
};
};
};
'/v1/chat.postMessage': {
POST: (params: ChatPostMessage) => {
ts: number;
Expand Down