Skip to content

Conversation

ahmed-n-abdeltwab
Copy link

@ahmed-n-abdeltwab ahmed-n-abdeltwab commented Jul 17, 2025

🚀 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: XX (calculating...)
  • Migrated so far: 41
  • Remaining: XX (calculating...)

👥 Team

  • Ahmed Nasser – Contributor     Buy Me a Coffee
  • Matheus Cardoso – Mentor  
  • Guilherme Gazzo – Co-mentor

📢 Team Chat: Join our chat


🚨 Core API Endpoints

These are 500 endpoints critical for core features and should be migrated first:

Authentication (0 / 9 completed)
  • login
  • login - with Facebook
  • login - with Twitter
  • login - with Google
  • logout
  • me
  • users.2fa.enableEmail
  • users.2fa.sendEmailCode
  • users.2fa.disableEmail
Content Management (0 / 11 completed)
Integrations (7 / 13 completed)
Marketplace Apps (0 / 4 completed)
  • apps/public/{app-id}/incoming
  • apps/public/{appId}/templateMessage
  • apps
  • apps/logs
Messaging (3 / 39 completed)
Omnichannel (0 / 154 completed)
  • livechat/users/{type}
  • livechat/users/{type}/{_id}
  • 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
  • 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
  • livechat/integrations.settings
  • 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 (0 / 24 completed)
  • autotranslate.getSupportedLanguages
  • autotranslate.saveSettings
  • autotranslate.translateMessage
  • email-inbox.list
  • email-inbox
  • email-inbox/{_id}
  • email-inbox.search
  • email-inbox.send-test/{_id}
  • shield.svg
  • spotlight
  • stdout.queue
  • licenses.info
  • licenses.add
  • video-conference/jitsi.update-timeout
  • commands.get ✅ Ready for Reviewfeat: Add OpenAPI Support to commands.get API Rocket.Chat#36953
  • commands.list
  • commands.preview
  • commands.run
  • mailer
  • mailer.unsubscribe
  • removeInvite/{_id}
  • useInviteToken
  • validateInviteToken
  • method.call/{method}
Notifications (0 / 6 completed)
  • banners/{id}
  • banners
  • banners.dismiss
  • banners.getNew
  • push.get
  • push.token
Rooms (0 / 126 completed)
  • channels.create ⚠️ Blockedfeat: Add OpenAPI Support to channels ( create, list, list.joined, info) APIs 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 ⚠️ Blockedfeat: Add OpenAPI Support to channels ( create, list, list.joined, info) APIs 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 ⚠️ Blockedfeat: Add OpenAPI Support to channels ( create, list, list.joined, info) APIs 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 ⚠️ Blockedfeat: Add OpenAPI Support to channels ( create, list, list.joined, info) APIs 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
  • rooms.adminRooms
  • rooms.cleanHistory
  • rooms.info
  • rooms.getDiscussions
  • rooms.get
  • rooms.leave
  • rooms.delete
  • rooms.favorite ✅ Ready for Reviewfeat: Add OpenAPI Support to rooms.favorite API Rocket.Chat#35995
  • rooms.upload/{rid}
  • rooms.media/{rid}
  • rooms.autocomplete.availableForTeams
  • rooms.autocomplete.channelAndPrivate
  • rooms.autocomplete.adminRooms
  • rooms.adminRooms.getRoom
  • rooms.saveRoomSettings
  • rooms.nameExists
  • rooms.changeArchivationState
  • rooms.export
  • rooms.muteUser
  • rooms.unmuteUser
  • rooms.createDiscussion
  • rooms.images
  • audit/rooms.members
  • rooms.membersOrderedByRole
  • 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
  • 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
  • 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
  • rooms.hide
Settings (1 / 47 completed)
  • settings.public
  • settings.oauth
  • settings
  • settings.addCustomOAuth
  • settings/{_id}
  • service.configurations
  • cloud.manualRegister
  • dns.resolve.txt
  • dns.resolve.srv
  • e2e.fetchMyKeys ✅ Ready for Reviewfeat: Add OpenAPI Support to e2e.fetchMyKeys API Rocket.Chat#36779
  • e2e.getUsersOfRoomWithoutKey ✅ Ready for Reviewfeat: Add OpenAPI Support to e2e.getUsersOfRoomWithoutKey API Rocket.Chat#36786
  • e2e.setRoomKeyID feat: Add OpenAPI Support to e2e.setRoomKeyID API Rocket.Chat#36716
  • e2e.setUserPublicAndPrivateKeys
  • e2e.updateGroupKey
  • uploadImportFile
  • downloadPublicImportFile
  • startImport
  • getImportFileData
  • getImportProgress
  • getLatestImportOperations
  • downloadPendingFiles
  • downloadPendingAvatars
  • getCurrentImportOperation
  • import.new
  • import.addUsers
  • import.run
  • import.status
  • import.clear
  • 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
Statistics (0 / 15 completed)
User Management (2 / 52 completed)

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 ⚠️ Blockedfeat: Add OpenAPI Support to Statistics API Rocket.Chat#35692

    ⛔ Typia generates oneOf when it encounters a union type (|) in the interface.
    The issue with oneOf is that it requires exactly one schema to match—similar to XOR logic—so overlapping types fail validation.
    To fix this, we need Typia to generate anyOf instead of oneOf.

User Management (0 / 1 completed)
Notifications (0 / 1 completed)

📌 Important Notes

When migrating endpoints to the new OpenAPI + AJV pattern, keep these points in mind:

  1. 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
  2. Error Response Codes

    • 400 (Bad Request) → Should be included for most endpoints with input validation.
    • 401 (Unauthorized) → Add only if the endpoint authRequired: true is included.
    • 403 (Forbidden) → Add only if specific permission checks are performed.
  3. Schema Validation

    • Use AJV for request/response validation.
    • Use Typia-generated $ref schemas where available instead of manually defining large object shapes.
  4. Centralized Error Validators

    • Use shared helpers like validateBadRequestErrorResponse instead of hard-coding error schemas.
    • This keeps error formats consistent across all endpoints.
  5. Route Definition Style

    • Use Route Method Chaining when defining multiple methods (.post(), .get(), etc.).
    • This is possible because ExtractRoutesFromAPI automatically groups routes under the same path in the type definitions.
  6. 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.
  7. No More rest-typings or Manual Typings

    • All request/response schemas are now defined directly inside the route definition file.
    • No schema definitions in rest-typings or other manual typing files — everything for an endpoint lives in one place for easier maintenance.
  8. 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 $ref for 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:

  1. No request/response schemas → can't generate Swagger/OpenAPI documentation automatically.
  2. validateParams is deprecated → doesn’t integrate with the new pattern.
  3. Manual typings in rest-typings → easy to forget or let them get out of sync.
  4. 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 Bazpoints = 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

  1. Single source of truth for typings
    ExtractRoutesFromAPI pulls types directly from your post() / get() definitions — no more manual edits in rest-typings.

  2. Strong request validation with AJV
    Both body (POST) and query (GET) are validated against JSON schemas.

  3. Strong response validation
    Ensures the API always returns objects that match the documented shape.

  4. Reusable schemas with Typia
    $ref: '#/components/schemas/IBaz' links to a single IBaz schema used across APIs.

  5. Predefined error validators
    validateBadRequestErrorResponse, validateUnauthorizedErrorResponse, validateForbiddenErrorResponse replace repetitive error schemas.

  6. Chaining multiple methods on the same path
    Calling .post().get() on the same fooEndpoints ensures both methods are merged into one entry in the generated Endpoints interface.


🧪 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.

@CLAassistant
Copy link

CLAassistant commented Jul 17, 2025

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants