[NOMERGE] feat: openapi migration progress
OpenAPI Migration Progress
This PR tracks the ongoing progress of migrating Rocket.Chat API endpoints to the new OpenAPI pattern with AJV validation and improved documentation. It will be continuously updated to reflect completed and remaining work.
✅ Summary
- Total endpoints: +600
👥 Team
🚨 Core API Endpoints
These are 500 endpoints critical for core features and should be migrated first:
Authentication (2 / 10 completed)
- login
- login - with Facebook
- login - with Twitter
- login - with Google
- logout
- me RocketChat/Rocket.Chat#39265
- shield.svg RocketChat/Rocket.Chat#39553
- users.2fa.enableEmail
- users.2fa.sendEmailCode
- users.2fa.disableEmail
Content Management (11 / 11 completed)
- emoji-custom.all RocketChat/Rocket.Chat#39553
- emoji-custom.create RocketChat/Rocket.Chat#36523
The Swagger UI doesn't handle multipart/form-data yet
- emoji-custom.delete RocketChat/Rocket.Chat#39553
- emoji-custom.list RocketChat/Rocket.Chat#39553
- emoji-custom.update RocketChat/Rocket.Chat#39553
- custom-user-status.list RocketChat/Rocket.Chat#36916
- [x]custom-user-status.create RocketChat/Rocket.Chat#39553
- custom-user-status.update RocketChat/Rocket.Chat#39553
- custom-user-status.delete RocketChat/Rocket.Chat#39553
- assets.setAsset RocketChat/Rocket.Chat#39265
- assets.unsetAsset RocketChat/Rocket.Chat#39265
Integrations (15 / 15 completed)
- integrations.create RocketChat/Rocket.Chat#39265
- integrations.get RocketChat/Rocket.Chat#39265
- integrations.history RocketChat/Rocket.Chat#39265
- integrations.list RocketChat/Rocket.Chat#39265
- integrations.update RocketChat/Rocket.Chat#39265
- integrations.remove RocketChat/Rocket.Chat#39265
- webdav.getMyAccounts RocketChat/Rocket.Chat#35884
- webdav.removeWebdavAccount RocketChat/Rocket.Chat#35884
- oauth-apps.create RocketChat/Rocket.Chat#36507
The HTTP route validation method incorrectly returns 'error-invalid-params' when it should be 'invalid-params'. This bug is fixed
- oauth-apps.update RocketChat/Rocket.Chat#36585
- oauth-apps.list RocketChat/Rocket.Chat#36586
- oauth-apps.get RocketChat/Rocket.Chat#36598
- oauth-apps.delete RocketChat/Rocket.Chat#36606
Marketplace Apps (0 / 4 completed)
- apps/public/{app-id}/incoming
- apps/public/{appId}/templateMessage
- apps
- apps/logsdocs/json
Messaging (37 / 40 completed)
- chat.delete RocketChat/Rocket.Chat#39820
- chat.react RocketChat/Rocket.Chat#39553
- chat.update
- chat.reportMessage RocketChat/Rocket.Chat#39553
- chat.followMessage RocketChat/Rocket.Chat#39225
- chat.unfollowMessage RocketChat/Rocket.Chat#39225
- chat.getMessage RocketChat/Rocket.Chat#39820
- chat.getThreadsList RocketChat/Rocket.Chat#39820
- chat.getDeletedMessages RocketChat/Rocket.Chat#39820
- chat.getDiscussions RocketChat/Rocket.Chat#39820
- chat.getMentionedMessages RocketChat/Rocket.Chat#39820
- chat.getMessageReadReceipts
- chat.getURLPreview RocketChat/Rocket.Chat#39820
- chat.getPinnedMessages RocketChat/Rocket.Chat#39820
- chat.getStarredMessages RocketChat/Rocket.Chat#39820
- chat.getThreadMessages RocketChat/Rocket.Chat#39820
- chat.ignoreUser RocketChat/Rocket.Chat#39820
- chat.pinMessage RocketChat/Rocket.Chat#36020
- chat.unPinMessage RocketChat/Rocket.Chat#36668
- chat.postMessage RocketChat/Rocket.Chat#39820
- chat.search RocketChat/Rocket.Chat#39820
- chat.sendMessage RocketChat/Rocket.Chat#39820
- chat.starMessage RocketChat/Rocket.Chat#39227
- chat.unStarMessage RocketChat/Rocket.Chat#39227
- chat.syncThreadsList
- chat.syncThreadMessages RocketChat/Rocket.Chat#39820
- chat.syncMessages RocketChat/Rocket.Chat#39820
- dm.close RocketChat/Rocket.Chat#38974
- dm.counters RocketChat/Rocket.Chat#39553
- dm.create RocketChat/Rocket.Chat#39553
- dm.delete RocketChat/Rocket.Chat#36677
The new APIClass currently supports only a single path and does not handle array paths yet. This bug is fixed in the PR – RocketChat/Rocket.Chat#36678
- dm.files RocketChat/Rocket.Chat#39553
- dm.history RocketChat/Rocket.Chat#39553
- dm.list.everyone RocketChat/Rocket.Chat#39820
- dm.list RocketChat/Rocket.Chat#39820
- dm.members RocketChat/Rocket.Chat#39553
- dm.messages RocketChat/Rocket.Chat#39553
- dm.messages.others RocketChat/Rocket.Chat#39553
- dm.open RocketChat/Rocket.Chat#39553
- dm.setTopic RocketChat/Rocket.Chat#39553
Omnichannel (0 / 154 completed)
- livechat/users/{type} RocketChat/Rocket.Chat#39584
- livechat/users/{type}/{_id} RocketChat/Rocket.Chat#39584
- livechat/agent.info/{rid}/{token}
- livechat/agent.next/{token}
- livechat/agents/{agentId}/departments
- livechat/agents/available
- livechat/agents/available-for-service-history
- omnichannel/agents/available
- omnichannel/contact
- omnichannel/contact.search
- omnichannel/contacts
- omnichannel/contacts.update
- omnichannel/contacts.conflicts
- omnichannel/contacts.get
- omnichannel/contacts.search
- omnichannel/contacts.channels
- omnichannel/contacts.history
- omnichannel/contacts.checkExistence
- livechat/analytics/agent-overview
- livechat/analytics/overview
- livechat/config RocketChat/Rocket.Chat#39506
- livechat/rooms
- livechat/visitor/{token}
- livechat/visitors.search
- livechat/tags.list
- livechat/tags/{tagId}
- livechat/tags.getOne
- livechat/priorities
- livechat/priorities.list
- livechat/priorities/{priorityId}
- livechat/priority.getOne
- livechat/business-hours
- livechat/business-hours.list
- livechat/business-hour
- livechat/units
- livechat/units.list
- livechat/monitors
- livechat/inquiries.queuedForUser
- livechat/webhook.test RocketChat/Rocket.Chat#39506
- livechat/integrations.settings RocketChat/Rocket.Chat#39506
- livechat/custom.fields
- livechat/message
- livechat/messages.history/{rid}
- omnichannel/integrations
- omnichannel/integrations.list
- canned-responses
- canned-responses.get
- canned-responses/{_id}
- connector.extension.getRegistrationInfoByUserId
- omnichannel/{rid}/request-transcript
- omnichannel/agents/statistics
- omnichannel/agents/statistics/{agentId}
- omnichannel/resume-chat
- omnichannel/transfer-chat
- omnichannel/room.forward
- omnichannel/room.close
- omnichannel/room.hold
- omnichannel/room.unhold
- omnichannel/room.tags
- omnichannel/room.tags.remove
- omnichannel/room.info/{rid}
- omnichannel/room.visitor.message
- omnichannel/room.agent.message
- omnichannel/room.messages/{rid}
- omnichannel/room.checkExistence
- omnichannel/room.history/{rid}
- omnichannel/room.dual-history/{rid}
- omnichannel/room.peek
- omnichannel/room.queue
- omnichannel/room.queueInfo/{rid}
- omnichannel/room.queueStatistics
- omnichannel/room.metrics/{rid}
- omnichannel/room.metrics
- omnichannel/room.tagStats
- omnichannel/room.transferHistory/{rid}
- omnichannel/visitor/{token}/departments
- omnichannel/visitor/{token}/status
- omnichannel/visitor/{token}/info
- omnichannel/visitor/{token}/history
- omnichannel/visitor/{token}/tags
- voip/events
- voip/managementServer/checkConnection
- voip/callServer/checkConnection
- voip/queues.getSummary
- voip/queues.getQueuedCallsForThisExtension
- voip/queues.getMembershipSubscription
- voip/room
- voip/room.close
- voip/rooms
- voip/room.join
- voip/rooms.list
- voip/rooms.history
- voip/room.recording.start
- voip/room.recording.stop
- voip/room.participants
- voip/room.participant.add
- voip/room.participant.remove
- voip/room.participant.mute
- voip/room.participant.unmute
- voip/room.participant.deafen
- voip/room.participant.undeafen
- voip/room.lock
- voip/room.unlock
- voip/room.info/{roomId}
- voip/participant/info/{participantId}
- voip/participant.muteAll
- voip/participant.unmuteAll
- voip/settings.get
- voip/settings.update
- voip/statistics
- voip/transcripts/{roomId}
- voip/call/{callId}/stats
- voip/call/{callId}/start
- voip/call/{callId}/end
- voip/call/{callId}/transfer
- voip/call/{callId}/mute
- voip/call/{callId}/unmute
- voip/call/{callId}/hold
- voip/call/{callId}/unhold
- voip/call/{callId}/join
- voip/call/{callId}/participants
- voip/call/{callId}/participant.add
- voip/call/{callId}/participant.remove
- voip/call/{callId}/participant.mute
- voip/call/{callId}/participant.unmute
- voip/call/{callId}/record.start
- voip/call/{callId}/record.stop
- voip/call/{callId}/stats.summary
- voip/call/summary
- voip/call/active
- voip/call/history
- voip/call/settings
- voip/channel.create
- voip/channel.list
- voip/channel.info/{channelId}
- voip/channel.update/{channelId}
- voip/channel.delete/{channelId}
- voip/channel.participants/{channelId}
- voip/amqp/connect
- voip/amqp/disconnect
- voip/amqp/publish
- voip/amqp/subscribe
- voip/amqp/unsubscribe
- voip/amqp/queues
- voip/amqp/exchanges
- voip/amqp/bind
- voip/amqp/unbind
- voip/amqp/consume
- voip/amqp/ack
- voip/amqp/nack
- voip/amqp/prefetch
- voip/amqp/metrics
- voip/amqp/channels
- omnichannel/{rid}/request-transcript
Miscellaneous (32 / 35 completed)
- autotranslate.getSupportedLanguages
- autotranslate.saveSettings RocketChat/Rocket.Chat#39265
- autotranslate.translateMessage RocketChat/Rocket.Chat#38978
- email-inbox.list RocketChat/Rocket.Chat#39265
- email-inbox RocketChat/Rocket.Chat#39265
- email-inbox/{_id} RocketChat/Rocket.Chat#39265
- email-inbox.search RocketChat/Rocket.Chat#39265
- email-inbox.send-test/{_id} RocketChat/Rocket.Chat#39265
- shield.svg
- spotlight RocketChat/Rocket.Chat#39265
- stdout.queue
- licenses.info
- licenses.add
- video-conference/jitsi.update-timeout
- video-conference.start RocketChat/Rocket.Chat#39265
- video-conference.join RocketChat/Rocket.Chat#39265
- video-conference.cancel RocketChat/Rocket.Chat#39265
- video-conference.info RocketChat/Rocket.Chat#39265
- video-conference.list RocketChat/Rocket.Chat#39265
- video-conference.providers RocketChat/Rocket.Chat#39265
- video-conference.capabilities RocketChat/Rocket.Chat#39265
- commands.get RocketChat/Rocket.Chat#36953
- commands.list
- commands.preview
- commands.run
- mailer RocketChat/Rocket.Chat#39553
- mailer.unsubscribe RocketChat/Rocket.Chat#39553
- removeInvite/{_id} RocketChat/Rocket.Chat#39200
- useInviteToken RocketChat/Rocket.Chat#39553
- validateInviteToken RocketChat/Rocket.Chat#39553
- sendInvitationEmail RocketChat/Rocket.Chat#39553
- method.call/{method} RocketChat/Rocket.Chat#39265
- method.callAnon/:method RocketChat/Rocket.Chat#39265
- smtp.check RocketChat/Rocket.Chat#39265
- fingerprint RocketChat/Rocket.Chat#39265
Notifications (5 / 7 completed)
- banners/{id} RocketChat/Rocket.Chat#39265
- banners RocketChat/Rocket.Chat#39265
- banners.dismiss RocketChat/Rocket.Chat#39265
- banners.getNew
- push.get RocketChat/Rocket.Chat#39553
- push.info RocketChat/Rocket.Chat#39553
- push.token
Rooms (28 / 128 completed)
- channels.create ⚠️ Blocked – RocketChat/Rocket.Chat#36674
Endpoints type is augmented in apps/meteor, but packages/ddp-client can’t see it because they have separate TypeScript compilation contexts.
need a shared .d.ts type declaration (or project references) so both projects see the same augmentation without importing Meteor code - channels.addAll
- channels.addLeader
- channels.addModerator
- channels.addOwner
- channels.anonymousread
- channels.archive
- channels.close
- channels.counters
- channels.delete
- channels.files
- channels.history
- channels.info ⚠️ Blocked – RocketChat/Rocket.Chat#36674
Endpoints type is augmented in apps/meteor, but packages/ddp-client can’t see it because they have separate TypeScript compilation contexts.
need a shared .d.ts type declaration (or project references) so both projects see the same augmentation without importing Meteor code - channels.invite
- channels.join
- channels.kick
- channels.leave
- channels.list.joined ⚠️ Blocked – RocketChat/Rocket.Chat#36674
Endpoints type is augmented in apps/meteor, but packages/ddp-client can’t see it because they have separate TypeScript compilation contexts.
need a shared .d.ts type declaration (or project references) so both projects see the same augmentation without importing Meteor code - channels.list ⚠️ Blocked – RocketChat/Rocket.Chat#36674
Endpoints type is augmented in apps/meteor, but packages/ddp-client can’t see it because they have separate TypeScript compilation contexts.
need a shared .d.ts type declaration (or project references) so both projects see the same augmentation without importing Meteor code - channels.members
- channels.messages
- channels.moderators
- channels.online
- channels.open
- channels.removeLeader
- channels.removeModerator
- channels.removeOwner
- channels.rename
- channels.roles
- channels.setAnnouncement
- channels.setCustomFields
- channels.setDefault
- channels.setDescription
- channels.setJoinCode
- channels.setPurpose
- channels.setReadOnly
- channels.setTopic
- channels.setType
- channels.unarchive
- channels.getAllUserMentionsByChannel
- channels.getIntegrations
- channels.convertToTeam
- rooms.saveNotification RocketChat/Rocket.Chat#38893
- rooms.adminRooms RocketChat/Rocket.Chat#39827
- rooms.cleanHistory RocketChat/Rocket.Chat#39553
- rooms.info RocketChat/Rocket.Chat#39553
- rooms.getDiscussions RocketChat/Rocket.Chat#39553
- rooms.get RocketChat/Rocket.Chat#39553
- rooms.leave RocketChat/Rocket.Chat#38957
- rooms.delete RocketChat/Rocket.Chat#38549
- rooms.favorite RocketChat/Rocket.Chat#35995
- rooms.upload/{rid}
- rooms.media/{rid}
- rooms.autocomplete.availableForTeams RocketChat/Rocket.Chat#39827
- rooms.autocomplete.channelAndPrivate RocketChat/Rocket.Chat#39827
- rooms.autocomplete.channelAndPrivate.withPagination RocketChat/Rocket.Chat#39827
- rooms.autocomplete.adminRooms RocketChat/Rocket.Chat#39827
- rooms.adminRooms.getRoom RocketChat/Rocket.Chat#39827
- rooms.saveRoomSettings RocketChat/Rocket.Chat#39827
- rooms.nameExists RocketChat/Rocket.Chat#39553
- rooms.changeArchivationState RocketChat/Rocket.Chat#39827
- rooms.export RocketChat/Rocket.Chat#39827
- rooms.muteUser RocketChat/Rocket.Chat#39553
- rooms.unmuteUser RocketChat/Rocket.Chat#39553
- rooms.createDiscussion RocketChat/Rocket.Chat#39827
- rooms.images RocketChat/Rocket.Chat#39553
- rooms.isMember RocketChat/Rocket.Chat#39827
- audit/rooms.members
- rooms.membersOrderedByRole RocketChat/Rocket.Chat#39827
- teams.create
- teams.listAll
- teams.list
- teams.info
- teams.update
- teams.addMembers
- teams.members
- teams.updateMember
- teams.leave
- teams.removeMember
- teams.delete
- teams.autocomplete
- teams.convertToChannel
- teams.addRooms
- teams.removeRoom
- teams.updateRoom
- teams.listRooms
- teams.listRoomsOfUser
- teams.listChildren
- groups.online
- groups.getIntegrations
- groups.addAll
- groups.addLeader
- groups.addModerator
- groups.addOwner
- groups.archive
- groups.close
- groups.counters
- groups.create
- groups.delete RocketChat/Rocket.Chat#39413
- groups.history
- groups.info
- groups.invite
- groups.kick
- groups.leave
- groups.listAll
- groups.list
- groups.members
- groups.messages
- groups.moderators
- groups.open
- groups.removeLeader
- groups.removeModerator RocketChat/Rocket.Chat#39364
- groups.removeOwner
- groups.rename
- groups.setAnnouncement
- groups.setCustomFields
- groups.setDescription
- groups.setPurpose
- groups.setReadOnly
- groups.setTopic
- groups.setType
- groups.unarchive
- groups.files
- groups.setEncrypted
- groups.convertToTeam
- groups.roles
- directory RocketChat/Rocket.Chat#39265
- rooms.hide RocketChat/Rocket.Chat#39827
- rooms.open RocketChat/Rocket.Chat#39553
Settings (39 / 55 completed)
- settings.public RocketChat/Rocket.Chat#39265
- settings.oauth RocketChat/Rocket.Chat#39265
- settings RocketChat/Rocket.Chat#39265
- settings.addCustomOAuth RocketChat/Rocket.Chat#39265
- settings/{_id} RocketChat/Rocket.Chat#39265
- service.configurations RocketChat/Rocket.Chat#39265
- cloud.manualRegister RocketChat/Rocket.Chat#39265
- cloud.createRegistrationIntent RocketChat/Rocket.Chat#39265
- cloud.registerPreIntent RocketChat/Rocket.Chat#39265
- cloud.confirmationPoll RocketChat/Rocket.Chat#39265
- cloud.registrationStatus RocketChat/Rocket.Chat#39265
- cloud.syncWorkspace RocketChat/Rocket.Chat#39265
- cloud.removeLicense RocketChat/Rocket.Chat#39265
- cloud.checkoutUrl RocketChat/Rocket.Chat#39265
- dns.resolve.txt
- dns.resolve.srv
- e2e.fetchMyKeys RocketChat/Rocket.Chat#36779
- e2e.getUsersOfRoomWithoutKey RocketChat/Rocket.Chat#36786
- e2e.setRoomKeyID RocketChat/Rocket.Chat#36716
- e2e.setUserPublicAndPrivateKeys RocketChat/Rocket.Chat#39265
- e2e.rejectSuggestedGroupKey RocketChat/Rocket.Chat#39219
- e2e.acceptSuggestedGroupKey RocketChat/Rocket.Chat#39219
- e2e.fetchUsersWaitingForGroupKey RocketChat/Rocket.Chat#39219
- e2e.provideUsersSuggestedGroupKeys RocketChat/Rocket.Chat#39219
- e2e.resetRoomKey RocketChat/Rocket.Chat#39219
- e2e.updateGroupKey RocketChat/Rocket.Chat#39094
- uploadImportFile RocketChat/Rocket.Chat#39265
- downloadPublicImportFile RocketChat/Rocket.Chat#39265
- startImport RocketChat/Rocket.Chat#39265
- getImportFileData RocketChat/Rocket.Chat#39265
- getImportProgress RocketChat/Rocket.Chat#39265
- getLatestImportOperations RocketChat/Rocket.Chat#39265
- downloadPendingFiles RocketChat/Rocket.Chat#39265
- downloadPendingAvatars RocketChat/Rocket.Chat#39265
- getCurrentImportOperation RocketChat/Rocket.Chat#39265
- importers.list RocketChat/Rocket.Chat#39265
- import.new RocketChat/Rocket.Chat#39265
- import.addUsers RocketChat/Rocket.Chat#39265
- import.run RocketChat/Rocket.Chat#39265
- import.status RocketChat/Rocket.Chat#39265
- import.clear RocketChat/Rocket.Chat#39265
- instances.get
- federation/addServerByUser
- federation/listServersByUser
- federation/removeServerByUser
- federation/searchPublicRooms
- federation/joinExternalPublicRoom
- moderation.reportsByUsers
- moderation.user.reportedMessages
- moderation.reports
- moderation.reportInfo
- moderation.dismissReports
- moderation.user.deleteReportedMessages
- sessions/list
- sessions/list.all
- sessions/info
- sessions/info.admin
- sessions/logout.me
- sessions/logout
- pw.getPolicy RocketChat/Rocket.Chat#39265
Statistics (0 / 15 completed)
- statistics.list ⚠️ Blocked – RocketChat/Rocket.Chat#35692
⛔ Typia generates
oneOfwhen it encounters a union type (|) in the interface.
The issue withoneOfis that it requires exactly one schema to match—similar to XOR logic—so overlapping types fail validation.
To fix this, we need Typia to generateanyOfinstead ofoneOf. - statistics ⚠️ Blocked – RocketChat/Rocket.Chat#35692
⛔ Typia generates
oneOfwhen it encounters a union type (|) in the interface.
The issue withoneOfis that it requires exactly one schema to match—similar to XOR logic—so overlapping types fail validation.
To fix this, we need Typia to generateanyOfinstead ofoneOf. - engagement-dashboard/users/new-users 🧪 Testing – RocketChat/Rocket.Chat#35882
- engagement-dashboard/users/active-users 🧪 Testing – RocketChat/Rocket.Chat#35882
- engagement-dashboard/users/users-by-time-of-the-day-in-a-week 🧪 Testing – RocketChat/Rocket.Chat#35882
- engagement-dashboard/users/chat-busier/hourly-data 🧪 Testing – RocketChat/Rocket.Chat#35882
- engagement-dashboard/users/chat-busier/weekly-data 🧪 Testing – RocketChat/Rocket.Chat#35882
- engagement-dashboard/messages/messages-sent
- engagement-dashboard/messages/origin
- engagement-dashboard/messages/top-five-popular-channels
- engagement-dashboard/channels/list
- subscriptions.get
- subscriptions.getOne ✅ Ready for Review – RocketChat/Rocket.Chat#37085
- subscriptions.read
- subscriptions.unread
User Management (11 / 54 completed)
- permissions.listAll RocketChat/Rocket.Chat#35985
- permissions.update RocketChat/Rocket.Chat#35985
- roles.create
- roles.update
- roles.addUserToRole RocketChat/Rocket.Chat#39265
- roles.getUsersInRole RocketChat/Rocket.Chat#39265
- roles.list RocketChat/Rocket.Chat#39265
- roles.sync RocketChat/Rocket.Chat#39265
- roles.delete RocketChat/Rocket.Chat#39265
- roles.removeUserFromRole RocketChat/Rocket.Chat#39265
- roles.getUsersInPublicRoles RocketChat/Rocket.Chat#39265
- users.create RocketChat/Rocket.Chat#39647
- users.register RocketChat/Rocket.Chat#39676
- users.update
- users.updateOwnBasicInfo
- users.info
- users.list RocketChat/Rocket.Chat#39368
- users.setAvatar
- users.getAvatar
- users.resetAvatar
- users.setStatus RocketChat/Rocket.Chat#39485
- users.getStatus RocketChat/Rocket.Chat#39484
- users.setActiveStatus
- users.deactivateIdle
- users.getPresence RocketChat/Rocket.Chat#39503
- users.presence
- users.delete
- users.deleteOwnAccount
- users.createToken
- users.getPreferences RocketChat/Rocket.Chat#38325
- users.setPreferences
- users.forgotPassword
- users.getUsernameSuggestion
- users.checkUsernameAvailability
- users.generatePersonalAccessToken
- users.regeneratePersonalAccessToken
- users.getPersonalAccessTokens
- users.removePersonalAccessToken
- users.requestDataDownload
- users.logoutOtherClients
- users.autocomplete RocketChat/Rocket.Chat#39601
- users.removeOtherTokens
- users.resetE2EKey
- users.resetTOTP
- users.listTeams
- moderation.reportUser
- users.logout
- user.info
- ldap.syncNow
- ldap.testConnection RocketChat/Rocket.Chat#38883
- ldap.testSearch RocketChat/Rocket.Chat#38883
⛔ The ExtractValidation type to return 'never' instead of 'unknown' where the post method doesn't have body.
🕒 This bug will be fixed - avatar/{subject}
- users.sendWelcomeEmail
- users.listByStatus
Let me know if you'd like these grouped, categorized, or documented further.
📝 Secondary API Endpoints
These 2 endpoints are lower priority and can be migrated after core APIs: (calculating...)
Statistics (0 / 1 completed)
- statistics.telemetry ⚠️ Blocked – RocketChat/Rocket.Chat#35692
⛔ Typia generates
oneOfwhen it encounters a union type (|) in the interface.
The issue withoneOfis that it requires exactly one schema to match—similar to XOR logic—so overlapping types fail validation.
To fix this, we need Typia to generateanyOfinstead ofoneOf.
User Management (1 / 1 completed)
- users.getAvatarSuggestion RocketChat/Rocket.Chat#36402
Notifications (7 / 7 completed)
- push.test RocketChat/Rocket.Chat#36882
- calendar-events.list RocketChat/Rocket.Chat#39265
- calendar-events.info RocketChat/Rocket.Chat#39265
- calendar-events.create RocketChat/Rocket.Chat#39265
- calendar-events.import RocketChat/Rocket.Chat#39265
- calendar-events.update RocketChat/Rocket.Chat#39265
- calendar-events.delete RocketChat/Rocket.Chat#39265
📌 Important Notes
When migrating endpoints to the new OpenAPI + AJV pattern, keep these points in mind:
-
No Change in Logic
- The migration should only update the structure, validation, and documentation.
- The core business logic, permissions, and side effects must remain exactly the same
-
Error Response Codes
400(Bad Request) → Should be included for most endpoints with input validation.401(Unauthorized) → Add only if the endpointauthRequired: trueis included.403(Forbidden) → Add only if specific permission checks are performed.
-
Schema Validation
- Use AJV for request/response validation.
- Use Typia-generated
$refschemas where available instead of manually defining large object shapes.
-
Centralized Error Validators
- Use shared helpers like
validateBadRequestErrorResponseinstead of hard-coding error schemas. - This keeps error formats consistent across all endpoints.
- Use shared helpers like
-
Route Definition Style
- Use Route Method Chaining when defining multiple methods (
.post(),.get(), etc.). - This is possible because
ExtractRoutesFromAPIautomatically groups routes under the same path in the type definitions.
- Use Route Method Chaining when defining multiple methods (
-
Optional Properties
- If an endpoint doesn’t need
permissionsRequired, omit it. - Always explicitly declare query or body in the route options — even if the endpoint has no parameters, set it to query: undefined or body: undefined.
- Keep configs minimal — only include what’s necessary for that endpoint.
- If an endpoint doesn’t need
-
No More
rest-typingsor Manual Typings- All request/response schemas are now defined directly inside the route definition file.
- No schema definitions in
rest-typingsor other manual typing files — everything for an endpoint lives in one place for easier maintenance.
-
Swagger Documentation
- Confirm that both request and response schemas appear correctly in Swagger UI.
- Check that example values are meaningful and match real data structures.
📖 Example: Migrating from Old Pattern to New Pattern
This example shows how to migrate from the legacy addRoute approach (with manual typings and deprecated validation)
to the new typed API definition pattern using:
API.v1.post()/.get()chaining (so multiple HTTP methods for the same path are merged in typings)- AJV for request & response validation
- Typia
$reffor shared model schemas - Automatic route typings with
ExtractRoutesFromAPI(no more manual duplication) - Predefined error response validators instead of hard-coded schemas
Before – Old Pattern
// Route definition
API.v1.addRoute(
'baz.foo',
{
authRequired: true,
validateParams: ajv.compile<{ name: string }>({ // ❌ Deprecated
type: 'object',
properties: { name: { type: 'string' } },
required: ['name'],
additionalProperties: false,
}),
permissionsRequired: ['manage-foo'],
},
{
async post() {
const result = await updateFoo(this.bodyParams);
return API.v1.success(result);
},
},
);
API.v1.addRoute(
'baz.bar',
{
authRequired: true,
validateParams: ajv.compile<{ name: string }>({ // ❌ Deprecated
type: 'object',
properties: { name: { type: 'string' } },
required: ['name'],
additionalProperties: false,
}),
permissionsRequired: ['manage-bar'],
},
{
async get() {
return API.v1.success(await getBar(this.queryParams.name));
},
},
);
// Manual typings in rest-typings
export type BazEndpoint = {
POST: (params: FooParams) => FooResponse;
GET: (params: BarParams) => BarResponse;
};
Problems with the old approach:
- No request/response schemas → can't generate Swagger/OpenAPI documentation automatically.
validateParamsis deprecated → doesn’t integrate with the new pattern.- Manual typings in
rest-typings→ easy to forget or let them get out of sync. - Hard-coded error handling → duplicated schemas everywhere.
After – New Pattern
const bazEndpoints = API.v1
// POST /v1/baz.foo
.post(
'baz.foo',
{
authRequired: true,
body: ajv.compile<{ name: string }>({
type: 'object',
properties: { name: { type: 'string' } },
required: ['name'],
additionalProperties: false,
}),
permissionsRequired: ['manage-foo'],
response: {
400: validateBadRequestErrorResponse, // Common for validation errors
// 401: validateUnauthorizedErrorResponse, // Optional: if endpoint used with auth
// 403: validateForbiddenErrorResponse, // Optional: if specific permission checks exist
200: ajv.compile<{ foo: IBaz }>({
type: 'object',
properties: {
foo: { $ref: '#/components/schemas/IBaz' }, // ✅ Typia model reuse
success: { type: 'boolean', enum: [true] },
},
required: ['foo', 'success'],
additionalProperties: false,
}),
},
},
async function action() {
const result = await updateFoo(this.bodyParams);
return API.v1.success(result);
},
)
// GET /v1/baz.bar
.get(
'baz.bar',
{
authRequired: true,
query: ajv.compile<{ name: string }>({
type: 'object',
properties: { name: { type: 'string' } },
required: ['name'],
additionalProperties: false,
}),
permissionsRequired: ['manage-bar'],
response: {
400: validateBadRequestErrorResponse,
// 401: validateUnauthorizedErrorResponse, // Optional
// 403: validateForbiddenErrorResponse, // Optional
200: ajv.compile<{ foo: IBaz }>({
type: 'object',
properties: {
bar: { $ref: '#/components/schemas/IBaz' },
success: { type: 'boolean', enum: [true] },
},
required: ['bar', 'success'],
additionalProperties: false,
}),
},
},
async function action() {
return API.v1.success(await getBar(this.queryParams.name));
},
);
// ✅ Automatically generate typings for both GET and POST without duplication
export type BazEndpoints = ExtractRoutesFromAPI<typeof bazEndpoints>;
declare module '@rocket.chat/rest-typings' {
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface
interface Endpoints extends BazEndpoints {}
}
Key Improvements in the New Pattern
-
Single source of truth for typings
ExtractRoutesFromAPIpulls types directly from yourpost()/get()definitions — no more manual edits inrest-typings. -
Strong request validation with AJV
Bothbody(POST) andquery(GET) are validated against JSON schemas. -
Strong response validation
Ensures the API always returns objects that match the documented shape. -
Reusable schemas with Typia
$ref: '#/components/schemas/IBaz'links to a single IBaz schema used across APIs. -
Predefined error validators
validateBadRequestErrorResponse,validateUnauthorizedErrorResponse,validateForbiddenErrorResponsereplace repetitive error schemas. -
Chaining multiple methods on the same path
Calling.post().get()on the samefooEndpointsensures both methods are merged into one entry in the generatedEndpointsinterface.
🧪 Testing Plan
- Ensure all migrated endpoints validate request and response schemas using AJV.
- Confirm accurate documentation in Swagger UI for each endpoint.
- Run integration tests to avoid regressions.
💡 Notes
- This PR is for tracking and visibility; implementation PRs will be linked progressively.
- Please feel free to suggest changes to prioritization.