diff --git a/.gitignore b/.gitignore
index 70da15979ca0b33383ef01af9aee452eab36247f..89ec46735ca3b06e89627601da6563b7e8095067 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,4 +19,7 @@ schema.sql
 
 build
 .npm
-.config
\ No newline at end of file
+.config
+
+# VScode settings
+.vscode/settings.json
diff --git a/src/app.ts b/src/app.ts
index fe6b0c92c86ed7215a76018c74664ace5e602284..31c5d9254643a793c91c24bbcbff9c70ade624f2 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -9,17 +9,19 @@ import { AuthorizationChecker } from './middleware/authorizationChecker';
 import { Configuration } from './config/configuration';
 import * as sentry from './log/sentry';
 import * as swaggerUiExpress from 'swagger-ui-express';
-import { DevicesController } from './controllers/devices/controller';
 import { NotificationsController } from './controllers/notifications/controller';
+import { DevicesController } from './controllers/devices/controller';
+import { ChannelsController } from './controllers/channels/controller';
 import * as fs from 'fs';
 import * as https from 'https';
 
 Configuration.load();
 
-import defaultMetadataStorage from 'class-transformer/cjs/storage';
+// Note https://github.com/typestack/class-transformer/issues/563
+import { defaultMetadataStorage as classTransformerDefaultMetadataStorage } from 'class-transformer/cjs/storage';
 
 const routingControllersOptions = {
-  controllers: [DevicesController, NotificationsController],
+  controllers: [DevicesController, NotificationsController, ChannelsController],
 };
 
 // Check ROLES are defined in ENV for controller Authorizations
@@ -56,7 +58,7 @@ let server;
 
 // Parse class-validator classes into JSON Schema:
 const schemas = validationMetadatasToSchemas({
-  classTransformerMetadataStorage: defaultMetadataStorage,
+  classTransformerMetadataStorage: classTransformerDefaultMetadataStorage,
   refPointerPrefix: '#/components/schemas/',
 });
 
diff --git a/src/controllers/channels-controller.ts b/src/controllers/channels-controller.ts
deleted file mode 100644
index ec308adae8bfbb79b2af314496eac8345ed4976c..0000000000000000000000000000000000000000
--- a/src/controllers/channels-controller.ts
+++ /dev/null
@@ -1,261 +0,0 @@
-import {
-  JsonController,
-  Post,
-  BodyParam,
-  Get,
-  Req,
-  Authorized,
-  Param,
-  QueryParams,
-  Put,
-  Delete,
-} from 'routing-controllers';
-import { ServiceFactory } from '../services/services-factory';
-import { ChannelsService } from '../services/channels-service';
-import { Group } from '../models/group';
-import { NotificationsService } from '../services/notifications-service';
-import { Category } from '../models/category';
-import { Tag } from '../models/tag';
-
-@JsonController('/channels')
-export class ChannelsController {
-  channelsService: ChannelsService = ServiceFactory.getChannelsService();
-  notificationsService: NotificationsService =
-    ServiceFactory.getNotificationsService();
-
-  @Get()
-  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
-  async getAllChannels(@Req() req, @QueryParams() query) {
-    Object.keys(query).forEach(
-      key => query[key] === undefined && delete query[key],
-    );
-    return this.channelsService.getAllChannels(query, req.authorizationBag);
-  }
-
-  @Get('/:id')
-  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
-  async getChannelById(@Param('id') channelId: string, @Req() req) {
-    return this.channelsService.getChannelById(channelId, req.authorizationBag);
-  }
-
-  @Get('/:id/edit')
-  @Authorized([process.env.INTERNAL_ROLE])
-  async editChannelById(@Param('id') channelId: string, @Req() req) {
-    return this.channelsService.editChannelById(
-      channelId,
-      req.authorizationBag,
-    );
-  }
-
-  @Post()
-  @Authorized([process.env.INTERNAL_ROLE])
-  async createChannel(@BodyParam('channel') channel, @Req() req) {
-    return this.channelsService.createChannel(channel, req.authorizationBag);
-  }
-
-  @Delete('/:id')
-  @Authorized([process.env.INTERNAL_ROLE])
-  async deleteChannel(@Param('id') channelId: string, @Req() req) {
-    return this.channelsService.deleteChannel(channelId, req.authorizationBag);
-  }
-
-  @Put()
-  @Authorized([process.env.INTERNAL_ROLE])
-  async updateChannel(@BodyParam('channel') channel, @Req() req) {
-    return this.channelsService.updateChannel(channel, req.authorizationBag);
-  }
-
-  @Get('/:id/members')
-  @Authorized([process.env.INTERNAL_ROLE])
-  async getChannelMembers(
-    @Param('id') channelId: string,
-    @QueryParams() query,
-    @Req() req,
-  ) {
-    Object.keys(query).forEach(
-      key => query[key] === undefined && delete query[key],
-    );
-
-    return this.channelsService.getChannelMembers(
-      channelId,
-      query,
-      req.authorizationBag,
-    );
-  }
-
-  @Put('/:id/members')
-  @Authorized([process.env.INTERNAL_ROLE])
-  async addMemberToChannel(
-    @Param('id') channelId: string,
-    @BodyParam('username') username: string,
-    @Req() req,
-  ) {
-    return this.channelsService.addMemberToChannel(
-      username,
-      channelId,
-      req.authorizationBag,
-    );
-  }
-
-  @Delete('/:id/members')
-  @Authorized([process.env.INTERNAL_ROLE])
-  async removeMemberFromChannel(
-    @Param('id') channelId: string,
-    @BodyParam('userId') memberId: string,
-    @Req() req,
-  ) {
-    return this.channelsService.removeMemberFromChannel(
-      memberId,
-      channelId,
-      req.authorizationBag,
-    );
-  }
-
-  @Get('/:id/groups')
-  @Authorized([process.env.INTERNAL_ROLE])
-  async getChannelGroups(
-    @Param('id') channelId: string,
-    @QueryParams() query,
-    @Req() req,
-  ) {
-    Object.keys(query).forEach(
-      key => query[key] === undefined && delete query[key],
-    );
-    return this.channelsService.getChannelGroups(
-      channelId,
-      query,
-      req.authorizationBag,
-    );
-  }
-
-  @Put('/:id/groups')
-  @Authorized([process.env.INTERNAL_ROLE])
-  async addGroupToChannel(
-    @BodyParam('group') group: Group,
-    @Param('id') channelId: string,
-    @Req() req,
-  ) {
-    return this.channelsService.addGroupToChannel(
-      new Group(group),
-      channelId,
-      req.authorizationBag,
-    );
-  }
-
-  @Delete('/:id/groups')
-  @Authorized([process.env.INTERNAL_ROLE])
-  async removeGroupFromChannel(
-    @Param('id') channelId: string,
-    @BodyParam('groupId') groupId: string,
-    @Req() req,
-  ) {
-    return this.channelsService.removeGroupFromChannel(
-      groupId,
-      channelId,
-      req.authorizationBag,
-    );
-  }
-
-  @Put('/:id/admingroup')
-  @Authorized([process.env.INTERNAL_ROLE])
-  async addAdminGroupToChannel(
-    @BodyParam('group') group: Group,
-    @Param('id') id: string,
-    @Req() req,
-  ) {
-    return this.channelsService.updateChannelAdminGroup(
-      new Group(group),
-      id,
-      req.authorizationBag,
-    );
-  }
-
-  @Put('/:id/subscribe')
-  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
-  async subscribeToChannel(@Req() req, @Param('id') channelId: string) {
-    return this.channelsService.subscribeToChannel(
-      channelId,
-      req.authorizationBag,
-    );
-  }
-
-  @Put('/:id/unsubscribe')
-  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
-  async unsubscribeFromChannel(@Req() req, @Param('id') channelId: string) {
-    return this.channelsService.unsubscribeFromChannel(
-      channelId,
-      req.authorizationBag,
-    );
-  }
-
-  @Put('/:id/apikey')
-  @Authorized([process.env.INTERNAL_ROLE])
-  async generateApiKey(@Req() req, @Param('id') channelId: string) {
-    return this.channelsService.generateApiKey(channelId, req.authorizationBag);
-  }
-
-  @Put('/:id/category')
-  @Authorized([process.env.INTERNAL_ROLE])
-  async setCategory(
-    @Req() req,
-    @Param('id') channelId: string,
-    @BodyParam('category') category: Category,
-  ) {
-    return this.channelsService.setCategory(
-      channelId,
-      category,
-      req.authorizationBag,
-    );
-  }
-
-  @Put('/:id/tags')
-  @Authorized([process.env.INTERNAL_ROLE])
-  async setTags(
-    @Req() req,
-    @Param('id') channelId: string,
-    @BodyParam('tags') tags: Tag[],
-  ) {
-    return this.channelsService.setTags(channelId, tags, req.authorizationBag);
-  }
-
-  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
-  @Get('/:id/notifications')
-  async getNotifications(
-    @Req() req,
-    @Param('id') channelId: string,
-    @QueryParams() query,
-  ) {
-    Object.keys(query).forEach(
-      key => query[key] === undefined && delete query[key],
-    );
-
-    return this.notificationsService.findAllNotifications(
-      channelId,
-      query,
-      req.authorizationBag,
-    );
-  }
-
-  @Put('/:id/owner')
-  @Authorized([process.env.INTERNAL_ROLE, process.env.SUPPORTER_ROLE])
-  async setChannelOwner(
-    @Param('id') channelId: string,
-    @BodyParam('username') username: string,
-    @Req() req,
-  ) {
-    return this.channelsService.setChannelOwner(
-      username,
-      channelId,
-      req.authorizationBag,
-    );
-  }
-
-  @Get('/:id/stats')
-  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
-  async getChannelStats(@Param('id') channelId: string, @Req() req) {
-    return this.channelsService.getChannelStats(
-      channelId,
-      req.authorizationBag,
-    );
-  }
-}
diff --git a/src/controllers/channels/controller.ts b/src/controllers/channels/controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7fe6f1521fcc1bcf3fa6d0b44a5390b7d1bbdeca
--- /dev/null
+++ b/src/controllers/channels/controller.ts
@@ -0,0 +1,472 @@
+import {
+  JsonController,
+  Post,
+  BodyParam,
+  Get,
+  Req,
+  Authorized,
+  Param,
+  QueryParams,
+  Put,
+  Delete,
+  Body,
+} from 'routing-controllers';
+import { Category } from '../../models/category';
+import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
+import { ServiceFactory } from '../../services/services-factory';
+import { ChannelsService } from '../../services/channels-service';
+import { NotificationsService } from '../../services/notifications-service';
+import {
+  ChannelResponse,
+  ChannelsListResponse,
+  CreateChannelRequest,
+  UpdateChannelRequest,
+  ChannelsQuery,
+  Query,
+  MembersListResponse,
+  GroupsListResponse,
+  GroupResponse,
+  MemberResponse,
+  GetChannelResponse,
+  EditChannelResponse,
+  NotificationsListResponse,
+  ChannelStatsResponse,
+  setTagsRequest,
+} from './dto';
+import { StatusCodeDescriptions, StatusCodes } from '../../utils/status-codes';
+
+@JsonController('/channels')
+export class ChannelsController {
+  channelsService: ChannelsService = ServiceFactory.getChannelsService();
+  notificationsService: NotificationsService = ServiceFactory.getNotificationsService();
+
+  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
+  @OpenAPI({
+    summary: 'Lists all of the channels matching the query.',
+    description: 'Lists all channels.',
+    operationId: 'listChannels',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(ChannelsListResponse, { description: 'List of queried channels.' })
+  @Get()
+  async getAllChannels(@Req() req, @QueryParams() query: ChannelsQuery): Promise<ChannelsListResponse> {
+    Object.keys(query).forEach(key => query[key] === undefined && delete query[key]);
+    return this.channelsService.getAllChannels(query, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
+  @OpenAPI({
+    summary: 'Returns the channel with the provided channel id.',
+    description: 'Returns a channel.',
+    operationId: 'getChannelById',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(GetChannelResponse, { description: 'Requested channel.' })
+  @Get('/:id')
+  async getChannelById(@Param('id') channelId: string, @Req() req): Promise<GetChannelResponse> {
+    return await this.channelsService.getChannelById(channelId, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
+  @OpenAPI({
+    summary: 'Fetches all channel information for the settings page.',
+    description: 'Fetches channel information.',
+    operationId: 'getChannelEdit',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(EditChannelResponse, { description: "Requested channel's full settings page information." })
+  @Get('/:id/edit')
+  async editChannelById(@Param('id') channelId: string, @Req() req): Promise<EditChannelResponse> {
+    return this.channelsService.editChannelById(channelId, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: 'Creates a new channel.',
+    description: 'Creates a new channel.',
+    operationId: 'createChannel',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(ChannelResponse, { description: 'Channel created json object.' })
+  @Post()
+  async createChannel(@Body() channel: CreateChannelRequest, @Req() req): Promise<ChannelResponse> {
+    return this.channelsService.createChannel(channel, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: 'Deletes a channel.',
+    description: 'Deletes the channel with the provided channel id.',
+    operationId: 'deleteChannel',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(String)
+  @Delete('/:id')
+  //@OnUndefined(204) should we be using this and not return anything
+  async deleteChannel(@Param('id') channelId: string, @Req() req): Promise<string> {
+    return this.channelsService.deleteChannel(channelId, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: 'Updates a channel.',
+    description: 'Updates a channel with the provided information.',
+    operationId: 'updateChannel',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(ChannelResponse, { description: 'Updated channel json object.' })
+  @Put()
+  async updateChannel(@BodyParam('channel') channel: UpdateChannelRequest, @Req() req): Promise<ChannelResponse> {
+    return this.channelsService.updateChannel(channel, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: "Fetches a channel's members.",
+    description: 'Fetches the queried members from the channel with the provided channel id.',
+    operationId: 'listChannelMembers',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(MembersListResponse, { description: 'Channel member list json object.' })
+  @Get('/:id/members')
+  async getChannelMembers(
+    @Param('id') channelId: string,
+    @QueryParams() query: Query,
+    @Req() req,
+  ): Promise<MembersListResponse> {
+    Object.keys(query).forEach(key => query[key] === undefined && delete query[key]);
+    return this.channelsService.getChannelMembers(channelId, query, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: 'Adds a member to a channel.',
+    description: 'Adds a member with the provided username to the channel with the provided channel id.',
+    operationId: 'addMemberToChannel',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(MemberResponse, { description: 'Added member json object.' })
+  @Post('/:id/members/:username')
+  async addMemberToChannel(
+    @Param('id') channelId: string,
+    @Param('username') username: string,
+    @Req() req,
+  ): Promise<MemberResponse> {
+    return this.channelsService.addMemberToChannel(username, channelId, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: 'Removes a member from a channel.',
+    description: 'Removes a member with the provided username from the channel with the provided channel id.',
+    operationId: 'removeMemberFromChannel',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(String, { description: 'Removed member id' })
+  @Delete('/:id/members/:userId')
+  async removeMemberFromChannel(
+    @Param('id') channelId: string,
+    @Param('userId') memberId: string,
+    @Req() req,
+  ): Promise<string> {
+    return this.channelsService.removeMemberFromChannel(memberId, channelId, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: "Fetches a channel's member groups.",
+    description:
+      'Fetches the member groups of the channel with the provided channel id, according to the query parameters.',
+    operationId: 'listChannelGroups',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(GroupsListResponse, { description: 'List of channel member groups.' })
+  @Get('/:id/groups')
+  async getChannelGroups(
+    @Param('id') channelId: string,
+    @QueryParams() query: Query,
+    @Req() req,
+  ): Promise<GroupsListResponse> {
+    Object.keys(query).forEach(key => query[key] === undefined && delete query[key]);
+    return this.channelsService.getChannelGroups(channelId, query, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: 'Adds a group to a channel.',
+    description: 'Adds a group with the provided group name to the channel with the provided channel id.',
+    operationId: 'addGroupToChannel',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(GroupResponse, { description: 'Group added to the channel as member.' })
+  @Put('/:id/groups/:groupname')
+  async addGroupToChannel(@Param('id') channelId: string, @Param('groupname') groupName: string, @Req() req) {
+    return this.channelsService.addGroupToChannel(groupName, channelId, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: "Removes a channel's member group.",
+    description: 'Removes the member group with the provided group id from the channel with the provided channel id.',
+    operationId: 'removeGroupFromChannel',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(String, { description: 'Removed group id.' })
+  @Delete('/:id/groups/:groupid')
+  async removeGroupFromChannel(@Param('id') channelId: string, @Param('groupid') groupId: string, @Req() req) {
+    return this.channelsService.removeGroupFromChannel(groupId, channelId, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: "Sets a group as channel's admin group.",
+    description: "Sets the channel's admin group to the group with the provided group name.",
+    operationId: 'updateChannelAdminGroup',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(ChannelResponse, { description: 'Changed channel information.' })
+  @Put('/:id/admingroup/:group')
+  async addAdminGroupToChannel(@Param('id') id: string, @Param('group') groupName: string, @Req() req) {
+    return this.channelsService.updateChannelAdminGroup(groupName, id, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
+  @OpenAPI({
+    summary: 'Subscribes the caller to the channel.',
+    description: 'Subscribes the endpoint-calling user to the channel with the provided channel id.',
+    operationId: 'subscribeToChannel',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(ChannelResponse, { description: 'Channel after new user subscribed.' })
+  @Put('/:id/subscribe')
+  async subscribeToChannel(@Req() req, @Param('id') channelId: string) {
+    return this.channelsService.subscribeToChannel(channelId, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
+  @OpenAPI({
+    summary: 'Unsubscribes the caller from the channel.',
+    description: 'Unsubscribes the endpoint-calling user from the channel with the provided channel id.',
+    operationId: 'unsubcribeFromChannel',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(ChannelResponse, { description: 'Channel after new user subscribed.' })
+  @Put('/:id/unsubscribe')
+  async unsubscribeFromChannel(@Req() req, @Param('id') channelId: string) {
+    return this.channelsService.unsubscribeFromChannel(channelId, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: 'Generates an API Key for the channel.',
+    description: 'Generates an API Key belonging to the channel with the provided channel id.',
+    operationId: 'generateAPIKey',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(ChannelResponse, { description: 'Channel after API Key generation.' })
+  @Put('/:id/apikey')
+  async generateApiKey(@Req() req, @Param('id') channelId: string) {
+    return this.channelsService.generateApiKey(channelId, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: 'Sets a category for the channel.',
+    description: 'Sets the provided category, on the channel with the provided channel id.',
+    operationId: 'setChannelCategory',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(ChannelResponse, { description: 'Channel after setting a category.' })
+  @Put('/:id/category')
+  async setCategory(@Req() req, @Param('id') channelId: string, @BodyParam('category') category: Category) {
+    return this.channelsService.setCategory(channelId, category, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE])
+  @OpenAPI({
+    summary: 'Sets tags for the channel.',
+    description: 'Sets the provided tags, on the channel with the provided channel id.',
+    operationId: 'setChannelTags',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(ChannelResponse, { description: 'Channel after setting tags.' })
+  @Put('/:id/tags')
+  async setTags(@Req() req, @Param('id') channelId: string, @Body() tags: setTagsRequest): Promise<ChannelResponse> {
+    return this.channelsService.setTags(channelId, tags, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
+  @OpenAPI({
+    summary: "Fetches the channel's notifications.",
+    description:
+      'Fetches the notifications of the channel with the provided channel id, according to the query parameters.',
+    operationId: 'listChannelNotifications',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(NotificationsListResponse, { description: 'List of notifications on the channel and their count.' })
+  @Get('/:id/notifications')
+  async getNotifications(
+    @Req() req,
+    @Param('id') channelId: string,
+    @QueryParams() query: Query,
+  ): Promise<NotificationsListResponse> {
+    Object.keys(query).forEach(key => query[key] === undefined && delete query[key]);
+    return this.notificationsService.findAllNotifications(channelId, query, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE, process.env.SUPPORTER_ROLE])
+  @OpenAPI({
+    summary: "Sets the channel's owner.",
+    description: 'Sets the owner of the channel with the provided channel id, to the user with the provided username.',
+    operationId: 'setChannelOwner',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(ChannelResponse, { description: 'Channel after setting the owner.' })
+  @Put('/:id/owner')
+  async setChannelOwner(
+    @Param('id') channelId: string,
+    @BodyParam('username') username: string,
+    @Req() req,
+  ): Promise<ChannelResponse> {
+    return this.channelsService.setChannelOwner(username, channelId, req.authorizationBag);
+  }
+
+  @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE])
+  @OpenAPI({
+    summary: "Gets the channel's stats.",
+    description: 'Gets the stats of the channel with the provided channel id.',
+    operationId: 'getChannelStats',
+    security: [{ oauth2: [] }],
+    responses: {
+      [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK],
+      [StatusCodes.BadRequest]: StatusCodeDescriptions[StatusCodes.BadRequest],
+      [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized],
+      [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden],
+    },
+  })
+  @ResponseSchema(ChannelStatsResponse, { description: 'Channel stats json object.' })
+  @Get('/:id/stats')
+  async getChannelStats(@Param('id') channelId: string, @Req() req): Promise<ChannelStatsResponse> {
+    return this.channelsService.getChannelStats(channelId, req.authorizationBag);
+  }
+}
diff --git a/src/controllers/channels/dto.ts b/src/controllers/channels/dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..532923627afbe8a2d6e17b1c91d1252826855acc
--- /dev/null
+++ b/src/controllers/channels/dto.ts
@@ -0,0 +1,1645 @@
+import {
+  IsNotEmpty,
+  IsOptional,
+  IsString,
+  ValidateNested,
+  IsBoolean,
+  IsEnum,
+  IsEmail,
+  IsNumber,
+  MinLength,
+  MaxLength,
+  IsUUID,
+  Min,
+  IsDateString,
+} from 'class-validator';
+import { Type } from 'class-transformer';
+import { JSONSchema } from 'class-validator-jsonschema';
+import { ChannelFlags, SubmissionByEmail, SubmissionByForm, Visibility } from '../../models/channel-enums';
+import { Notification } from '../../models/notification';
+import { Channel } from '../../models/channel';
+import { Group } from '../../models/group';
+import { User } from '../../models/user';
+import { AuthorizationBag } from '../../models/authorization-bag';
+import { v4 } from 'uuid';
+
+export class CategoryResponse {
+  @IsString()
+  id: string;
+
+  @IsString()
+  name: string;
+}
+
+export class Tag {
+  @IsString()
+  id: string;
+
+  @IsString()
+  name: string;
+}
+
+@JSONSchema({
+  description: 'Member groups query parameters.',
+  example: {
+    take: 10,
+    skip: 20,
+    searchText: 'groupname',
+  },
+})
+export class Query {
+  @JSONSchema({
+    description: 'For pagination, number of items to skip in result. Default: 10.',
+  })
+  @IsOptional()
+  @IsNumber()
+  take: number;
+
+  @JSONSchema({
+    description: 'For pagination, number of items to skip in result. Default: 0.',
+  })
+  @IsOptional()
+  @IsNumber()
+  skip: number;
+
+  @JSONSchema({
+    description: 'Search text to be used.',
+  })
+  @IsOptional()
+  @IsString()
+  searchText: string;
+}
+
+@JSONSchema({
+  description: 'New Device json input.',
+  example: {
+    take: 10,
+    skip: 20,
+    searchText: 'ITMM minutes',
+    ownerFilter: false,
+    subscribedFilter: true,
+    favoritesFilter: false,
+    tags: ['News', 'Announcements'],
+    category: 'CDA',
+  },
+})
+export class ChannelsQuery extends Query {
+  @JSONSchema({
+    description: 'If channels returned must be owned by the user.',
+    example: false,
+  })
+  @IsBoolean()
+  ownerFilter: boolean;
+
+  @JSONSchema({
+    description: 'If channels returned must be subscribed to by the user.',
+    example: false,
+  })
+  @IsBoolean()
+  subscribedFilter: boolean;
+
+  @JSONSchema({
+    description: 'If channels returned must be favorited by the user.',
+    example: false,
+  })
+  @IsBoolean()
+  favoritesFilter: boolean;
+
+  @JSONSchema({
+    description: 'Tags that the returned channels must have.',
+  })
+  @IsOptional()
+  @IsString({ each: true })
+  tags: string[];
+
+  @JSONSchema({
+    description: 'Category that the returned channels must have.',
+  })
+  @IsOptional()
+  @IsString()
+  category: string;
+}
+
+@JSONSchema({
+  description: 'List of groups.',
+  example: {
+    groups: [
+      {
+        id: v4(),
+        groupIdentifier: 'group1name',
+      },
+      {
+        id: v4(),
+        groupIdentifier: 'group2name',
+      },
+    ],
+    totalNumberOfGroups: 2,
+  },
+})
+export class GroupsListResponse {
+  @JSONSchema({
+    description: 'List of groups.',
+    example: [
+      {
+        id: v4(),
+        groupIdentifier: 'group1name',
+      },
+      {
+        id: v4(),
+        groupIdentifier: 'group2name',
+      },
+    ],
+  })
+  @ValidateNested({ each: true })
+  @Type(() => GroupResponse)
+  groups: GroupResponse[];
+
+  @JSONSchema({
+    description: 'Number of groups in the list.',
+    example: 2,
+  })
+  @IsNumber()
+  totalNumberOfGroups: number;
+
+  constructor(list: GroupResponse[]) {
+    this.groups = list;
+    this.totalNumberOfGroups = this.groups.length;
+  }
+}
+
+@JSONSchema({
+  description: 'List of channel members json object.',
+  example: {
+    members: [
+      {
+        id: v4(),
+        username: 'member1username',
+        email: 'member1email',
+      },
+      {
+        id: v4(),
+        username: 'member2username',
+        email: 'member2email',
+      },
+    ],
+    totalNumberOfMembers: 2,
+  },
+})
+export class MembersListResponse {
+  @ValidateNested({ each: true })
+  @Type(() => MemberResponse)
+  @JSONSchema({
+    description: 'Members list.',
+    example: [
+      {
+        id: v4(),
+        username: 'member1username',
+        email: 'member1email',
+      },
+      {
+        id: v4(),
+        username: 'member2username',
+        email: 'member2email',
+      },
+    ],
+  })
+  members: MemberResponse[];
+
+  @IsNumber()
+  @JSONSchema({
+    description: 'Number of channel members returned.',
+    example: 2,
+  })
+  totalNumberOfMembers: number;
+
+  constructor(list: User[] = []) {
+    this.members = list.map(u => new MemberResponse(u));
+    this.totalNumberOfMembers = this.members.length;
+  }
+}
+
+@JSONSchema({
+  description: 'List of notifications json object.',
+  example: 2,
+})
+export class NotificationsListResponse {
+  @ValidateNested({ each: true })
+  @Type(() => Notification)
+  @JSONSchema({
+    description: 'List of notifications.',
+    example: [{}],
+  })
+  items: Notification[]; //would be NotificationResponse, but wait for other MR to be merged
+
+  @JSONSchema({
+    description: 'Number of notifications returned.',
+    example: 2,
+  })
+  @IsNumber()
+  count: number;
+
+  constructor(list: Notification[] = []) {
+    this.items = list;
+    this.count = this.items.length;
+  }
+}
+
+@JSONSchema({
+  description: 'Channel member json object.',
+  example: {
+    id: v4(),
+    username: 'memberusername',
+    email: 'memberemail',
+  },
+})
+export class MembersResponse {
+  @IsString()
+  @IsUUID('4')
+  @JSONSchema({
+    description: 'Channel member id.',
+    example: v4(),
+  })
+  id: string;
+
+  @IsString()
+  @IsNotEmpty()
+  @JSONSchema({
+    description: 'Channel member username.',
+    example: 'memberusername',
+  })
+  username: string;
+
+  @IsString()
+  @IsEmail()
+  @JSONSchema({
+    description: 'Channel member email.',
+    example: 'memberemail',
+  })
+  email: string;
+
+  constructor(member: User) {
+    this.id = member.id;
+    this.username = member.username;
+    this.email = member.email;
+  }
+}
+
+export class GroupsResponse {
+  @IsNumber()
+  count: number;
+
+  constructor(count: number) {
+    this.count = count;
+  }
+}
+
+@JSONSchema({
+  description: 'Channel stats object.',
+  example: {
+    id: v4(),
+    name: 'channelname',
+    members: 20,
+    unsubscribed: 2,
+    owner: {
+      id: v4(),
+      name: 'ownerusername',
+    },
+    creationDate: new Date(),
+    lastActivityDate: new Date(),
+    groups: 3,
+  },
+})
+export class ChannelStatsResponse {
+  @IsString()
+  @IsUUID('4')
+  @JSONSchema({
+    description: 'Channel id.',
+    example: v4(),
+  })
+  id: string;
+
+  @IsString()
+  @IsNotEmpty()
+  @JSONSchema({
+    description: 'Channel name.',
+    example: 'channelname',
+  })
+  name: string;
+
+  @ValidateNested({ each: true })
+  @Type(() => User)
+  @JSONSchema({
+    description: 'Channel members.',
+    example: 20,
+  })
+  members: number;
+
+  @IsNumber()
+  @Min(0)
+  @JSONSchema({
+    description: 'Number of unsubscribed channel members.',
+    example: 2,
+  })
+  unsubscribed: number;
+
+  @Type(() => User)
+  @JSONSchema({
+    description: 'Channel owner.',
+    example: {
+      id: v4(),
+      name: 'ownerusername',
+    },
+  })
+  owner: User;
+
+  @IsDateString()
+  @IsNotEmpty()
+  @JSONSchema({
+    description: 'The date the channel was created.',
+    example: new Date(),
+  })
+  creationDate: Date;
+
+  @IsDateString()
+  @IsNotEmpty()
+  @JSONSchema({
+    description: 'The date the channel was last active.',
+    example: new Date(),
+  })
+  lastActivityDate: Date;
+
+  @IsNumber()
+  @Min(0)
+  @JSONSchema({
+    description: 'Number of channel member groups.',
+    example: 3,
+  })
+  groups: number;
+
+  constructor(channel: Channel) {
+    this.id = channel.id;
+    this.members = channel.members.length;
+    this.unsubscribed = channel.unsubscribed.length;
+    this.owner = channel.owner;
+    this.creationDate = channel.creationDate;
+    this.lastActivityDate = channel.lastActivityDate;
+    this.groups = channel.groups.length;
+  }
+}
+
+export class Relationship {
+  @Type(() => NotificationsListResponse)
+  notifications: NotificationsListResponse;
+
+  members: MembersListResponse;
+
+  groups: GroupsResponse;
+
+  constructor(notif: NotificationsListResponse, members: MembersListResponse, groups: GroupsResponse) {
+    this.notifications = notif;
+    this.members = members;
+    this.groups = groups;
+  }
+}
+export class ListChannelsFilterObject {
+  @IsNotEmpty()
+  @IsString()
+  id: string;
+
+  @IsNotEmpty()
+  @IsString()
+  slug: string;
+
+  @IsNotEmpty()
+  @IsString()
+  @MaxLength(256)
+  description: string;
+
+  @IsNotEmpty()
+  @IsString()
+  @MinLength(4)
+  @MaxLength(128)
+  name: string;
+
+  @IsNotEmpty()
+  @IsEnum(Visibility)
+  visibility: Visibility;
+
+  @IsBoolean()
+  archive: boolean;
+
+  @IsBoolean()
+  subscribed: boolean;
+
+  @IsBoolean()
+  @IsOptional()
+  manage: boolean;
+
+  @IsBoolean()
+  @IsOptional()
+  send: boolean;
+
+  @Type(() => CategoryResponse)
+  @IsOptional()
+  category: CategoryResponse;
+
+  @IsOptional()
+  @ValidateNested({ each: true })
+  @Type(() => Tag)
+  tags: Tag[];
+
+  @IsOptional()
+  @Type(() => Relationship)
+  relationships: Relationship;
+
+  @IsEnum(ChannelFlags, { each: true })
+  @JSONSchema({
+    description: 'List of labels related to the channel.',
+    example: [ChannelFlags.critical, ChannelFlags.mandatory],
+  })
+  channelFlags: ChannelFlags[];
+
+  constructor(channel: Channel, subscribed: boolean, manage: boolean, send: boolean) {
+    this.id = channel.id;
+    this.slug = channel.slug;
+    this.description = channel.description;
+    this.name = channel.name;
+    this.visibility = channel.visibility;
+    this.archive = channel.archive;
+    this.subscribed = subscribed;
+    this.manage = manage;
+    this.send = send;
+    this.category = channel.category;
+    this.tags = channel.tags;
+    this.relationships = {
+      notifications: new NotificationsListResponse(channel.notifications),
+      members: new MembersListResponse(channel.members),
+      groups: {
+        count: channel.groups?.length || 0,
+      },
+    };
+    this.channelFlags = channel.channelFlags;
+  }
+}
+
+@JSONSchema({
+  description: 'Device json return.',
+  example: {
+    channels: [
+      {
+        id: v4(),
+        slug: 'channel1-name',
+        description: 'channel1description',
+        name: 'channel1 name',
+        visibility: Visibility.restricted,
+        archive: true,
+        subscribed: true,
+        manage: true,
+        send: true,
+        category: null,
+        tags: [],
+        relationships: {
+          notifications: {
+            count: 73,
+          },
+          members: {
+            count: 35,
+          },
+          groups: {
+            count: 2,
+          },
+        },
+      },
+      {
+        id: v4(),
+        slug: 'channel2-name',
+        description: 'channel2description',
+        name: 'channel2 name',
+        visibility: Visibility.restricted,
+        archive: true,
+        subscribed: true,
+        manage: true,
+        send: true,
+        category: null,
+        tags: [],
+        relationships: {
+          notifications: {
+            count: 13,
+          },
+          members: {
+            count: 2,
+          },
+          groups: {
+            count: 1,
+          },
+        },
+      },
+    ],
+    count: 2,
+  },
+})
+export class ChannelsListResponse {
+  @JSONSchema({
+    description: 'List of channels.',
+    example: [
+      {
+        id: v4(),
+        slug: 'channel1-name',
+        description: 'channel1description',
+        name: 'channel1 name',
+        visibility: Visibility.restricted,
+        archive: true,
+        subscribed: true,
+        manage: true,
+        send: true,
+        category: null,
+        tags: [],
+        relationships: {
+          notifications: {
+            count: 73,
+          },
+          members: {
+            count: 35,
+          },
+          groups: {
+            count: 2,
+          },
+        },
+      },
+      {
+        id: v4(),
+        slug: 'channel2-name',
+        description: 'channel2description',
+        name: 'channel2 name',
+        visibility: Visibility.restricted,
+        archive: true,
+        subscribed: true,
+        manage: true,
+        send: true,
+        category: null,
+        tags: [],
+        relationships: {
+          notifications: {
+            count: 13,
+          },
+          members: {
+            count: 2,
+          },
+          groups: {
+            count: 1,
+          },
+        },
+      },
+    ],
+  })
+  @ValidateNested({ each: true })
+  @Type(() => ListChannelsFilterObject)
+  channels: ListChannelsFilterObject[];
+
+  @IsNumber()
+  @JSONSchema({
+    description: 'Number of returned channels.',
+    example: 2,
+  })
+  count: number;
+
+  constructor(list: ListChannelsFilterObject[]) {
+    this.channels = list;
+    this.count = this.channels.length;
+  }
+}
+
+@JSONSchema({
+  description: 'Channel object json.',
+  example: {
+    id: v4(),
+    name: 'CERN Notifications Updates',
+    description: 'Service updates for CERN Notifications.',
+    category: {
+      name: 'CDA',
+      id: v4(),
+    },
+    tags: [
+      {
+        id: v4(),
+        name: 'Channel tag1',
+      },
+      {
+        id: v4(),
+        name: 'Channel tag2',
+      },
+    ],
+    send: true,
+  },
+})
+export class GetChannelResponse {
+  @IsNotEmpty()
+  @IsUUID('4')
+  @JSONSchema({
+    description: 'Channel id.',
+    example: v4(),
+  })
+  id: string;
+
+  @IsNotEmpty()
+  @IsString()
+  @JSONSchema({
+    description: 'Channel name.',
+    example: 'CERN Notifications Updates',
+  })
+  name: string;
+
+  @IsString()
+  @IsNotEmpty()
+  @JSONSchema({
+    description: 'Channel description.',
+    example: 'Service updates for CERN Notifications.',
+  })
+  description: string;
+
+  @Type(() => CategoryResponse)
+  @IsOptional()
+  @JSONSchema({
+    description: 'Channel category.',
+    example: {
+      name: 'CDA',
+      id: v4(),
+    },
+  })
+  category: CategoryResponse;
+
+  @IsOptional()
+  @ValidateNested({ each: true })
+  @Type(() => Tag)
+  @JSONSchema({
+    description: 'Channel Tags.',
+    example: [
+      {
+        id: v4(),
+        name: 'Channel tag1',
+      },
+      {
+        id: v4(),
+        name: 'Channel tag2',
+      },
+    ],
+  })
+  tags: Tag[];
+
+  @IsBoolean()
+  @IsOptional()
+  @JSONSchema({
+    description: 'User has send permission on the channel.',
+    example: true,
+  })
+  send: boolean;
+
+  @IsEnum(ChannelFlags, { each: true })
+  @JSONSchema({
+    description: 'List of labels related to the channel.',
+    example: [ChannelFlags.critical, ChannelFlags.mandatory],
+  })
+  channelFlags: ChannelFlags[];
+
+  constructor(channel: Channel) {
+    this.id = channel.id;
+    this.name = channel.name;
+    this.description = channel.description;
+    this.category = channel.category;
+    this.tags = channel.tags;
+    this.channelFlags = channel.channelFlags;
+  }
+
+  async setSendPermission(channel: Channel, authBag: AuthorizationBag): Promise<void> {
+    this.send = await channel.canSendByForm(authBag);
+  }
+}
+
+@JSONSchema({
+  description: 'Channel full settings page information.',
+  example: {
+    id: v4(),
+    name: 'CERN Notifications Updates',
+    slug: 'cern-notification-updates',
+    owner: {
+      id: v4(),
+      username: 'user',
+      email: 'useremail@cern.ch',
+      enabled: true,
+      created: new Date(),
+      lastLogin: new Date(),
+    },
+    description: 'Service updates for CERN Notifications.',
+    adminGroup: {
+      id: v4(),
+      groupIdentifier: 'admingroupname',
+    },
+    visibility: Visibility.public,
+    sendPrivate: false,
+    incomingEmail: 'someemail@cern.ch',
+    incomingEgroup: 'notifications-service-admins@cern.ch',
+    submissionByEmail: [
+      SubmissionByEmail.administrators,
+      SubmissionByEmail.members,
+      SubmissionByEmail.email,
+      SubmissionByEmail.egroup,
+    ],
+    submissionByForm: [SubmissionByForm.administrators, SubmissionByForm.members, SubmissionByForm.apikey],
+    archive: true,
+    APIKey: 'ck_' + v4() + '_' + v4(),
+    tags: [
+      {
+        id: v4(),
+        name: 'Channel tag1',
+      },
+      {
+        id: v4(),
+        name: 'Channel tag2',
+      },
+    ],
+    category: {
+      id: v4(),
+      name: 'CDA',
+    },
+  },
+})
+export class EditChannelResponse {
+  @IsNotEmpty()
+  @IsUUID('4')
+  @JSONSchema({
+    description: 'Channel id.',
+    example: v4(),
+  })
+  id: string;
+
+  @IsNotEmpty()
+  @IsString()
+  @JSONSchema({
+    description: 'Channel name.',
+    example: 'CERN Notifications Updates',
+  })
+  name: string;
+
+  @IsNotEmpty()
+  @IsString()
+  @JSONSchema({
+    description: 'Channel slug. Usually a slugified version of the name, eg channel-name.',
+    example: 'cern-notification-updates',
+  })
+  slug: string;
+
+  @IsNotEmpty()
+  @JSONSchema({
+    description: 'Channel owner.',
+    example: {
+      id: v4(),
+      username: 'user',
+      email: 'useremail@cern.ch',
+      enabled: true,
+      created: new Date(),
+      lastLogin: new Date(),
+    },
+  })
+  owner: User;
+
+  @IsString()
+  @JSONSchema({
+    description: 'Channel description.',
+    example: 'Service updates for CERN Notifications.',
+  })
+  description: string;
+
+  @IsOptional()
+  @JSONSchema({
+    description: 'Channel admin group.',
+    example: {
+      id: v4(),
+      groupIdentifier: 'admingroupname',
+    },
+  })
+  adminGroup: Group;
+
+  @IsNotEmpty()
+  @IsEnum(Visibility)
+  @JSONSchema({
+    description: 'Channel visibility.',
+    example: Visibility.public,
+  })
+  visibility: Visibility;
+
+  @IsBoolean()
+  @JSONSchema({
+    description: "The channel's setting for allowing private notifications.",
+    example: false,
+  })
+  sendPrivate: boolean;
+
+  @IsString()
+  @JSONSchema({
+    description: 'Emails which can send notifications by email to the channel.',
+    example: 'someemail@cern.ch',
+  })
+  incomingEmail: string;
+
+  @IsString()
+  @JSONSchema({
+    description: 'Egroup which can send notifications by email to the channel.',
+    example: 'notifications-service-admins@cern.ch',
+  })
+  incomingEgroup: string;
+
+  @IsEnum(SubmissionByEmail, { each: true })
+  @JSONSchema({
+    description: 'Determines who and how can send notifications by email.',
+    example: [
+      SubmissionByEmail.administrators,
+      SubmissionByEmail.members,
+      SubmissionByEmail.email,
+      SubmissionByEmail.egroup,
+    ],
+  })
+  submissionByEmail: SubmissionByEmail[];
+
+  @IsEnum(SubmissionByForm, { each: true })
+  @JSONSchema({
+    description: 'Who can send notifications via Web or API. Default is ADMINISTRATORS.',
+    example: [SubmissionByForm.administrators, SubmissionByForm.members, SubmissionByForm.apikey],
+  })
+  submissionByForm: SubmissionByForm[];
+
+  @IsBoolean()
+  @JSONSchema({
+    description:
+      "Allows enabling preservation of the channel's notifications into an external archive. Notifications are deleted from our system (not the archive) after 13 months in any case.",
+    example: true,
+  })
+  archive: boolean;
+
+  @IsString()
+  @JSONSchema({
+    description: 'API Key.',
+    example: 'ck_' + v4() + '_' + v4(),
+  })
+  APIKey: string;
+
+  @IsOptional()
+  @ValidateNested({ each: true })
+  @Type(() => Tag)
+  @JSONSchema({
+    description: "The channel's Tags.",
+    example: [
+      {
+        id: v4(),
+        name: 'Channel tag1',
+      },
+      {
+        id: v4(),
+        name: 'Channel tag2',
+      },
+    ],
+  })
+  tags: Tag[];
+
+  @Type(() => CategoryResponse)
+  @IsOptional()
+  @JSONSchema({
+    description: "The channel's Category.",
+    example: {
+      id: v4(),
+      name: 'CDA',
+    },
+  })
+  category: CategoryResponse;
+
+  constructor(channel: Channel) {
+    this.id = channel.id;
+    this.name = channel.name;
+    this.slug = channel.slug;
+    this.owner = channel.owner;
+    this.description = channel.description;
+    this.adminGroup = channel.adminGroup;
+    this.visibility = channel.visibility;
+    this.sendPrivate = channel.sendPrivate;
+    this.incomingEmail = channel.incomingEmail;
+    this.incomingEgroup = channel.incomingEgroup;
+    this.submissionByEmail = channel.submissionByEmail;
+    this.submissionByForm = channel.submissionByForm;
+    this.archive = channel.archive;
+    this.tags = channel.tags;
+    this.category = channel.category;
+    this.APIKey = channel.APIKey;
+  }
+}
+
+@JSONSchema({
+  description: 'Channel response json object.',
+  example: {
+    id: v4(),
+    name: 'CERN Notifications Updates',
+    slug: 'cern-notification-updates',
+    owner: {
+      id: v4(),
+      username: 'user',
+      email: 'useremail@cern.ch',
+      enabled: true,
+      created: new Date(),
+      lastLogin: new Date(),
+    },
+    description: 'Service updates for CERN Notifications.',
+    members: [
+      {
+        id: v4(),
+        username: 'user1',
+        email: 'user1email@cern.ch',
+        enabled: true,
+        created: new Date(),
+        lastLogin: new Date(),
+      },
+      {
+        id: v4(),
+        username: 'user2',
+        email: 'user2email@cern.ch',
+        enabled: true,
+        created: new Date(),
+        lastLogin: new Date(),
+      },
+    ],
+    adminGroup: {
+      id: v4(),
+      groupIdentifier: 'admingroupname',
+    },
+    unsubscribed: [
+      {
+        id: v4(),
+        username: 'user3',
+        email: 'user3email@cern.ch',
+        enabled: true,
+        created: new Date(),
+        lastLogin: new Date(),
+      },
+      {
+        id: v4(),
+        username: 'user4',
+        email: 'user4email@cern.ch',
+        enabled: true,
+        created: new Date(),
+        lastLogin: new Date(),
+      },
+    ],
+    groups: [
+      {
+        id: v4(),
+        groupIdentifier: 'group1',
+      },
+      {
+        id: v4(),
+        groupIdentifier: 'group2',
+      },
+    ],
+    visibility: Visibility.public,
+    incomingEmail: 'someemail@cern.ch',
+    incomingEgroup: 'notifications-service-admins@cern.ch',
+    submissionByEmail: [
+      SubmissionByEmail.administrators,
+      SubmissionByEmail.members,
+      SubmissionByEmail.email,
+      SubmissionByEmail.egroup,
+    ],
+    submissionByForm: [SubmissionByForm.administrators, SubmissionByForm.members, SubmissionByForm.apikey],
+    archive: true,
+    APIKey: 'ck_' + v4() + '_' + v4(),
+    sendPrivate: false,
+    creationDate: new Date(),
+    lastActivityDate: new Date(),
+    deleteDate: new Date(),
+    subscribed: true,
+    manage: true,
+    access: true,
+    send: true,
+    tags: [
+      {
+        id: v4(),
+        name: 'Channel tag1',
+      },
+      {
+        id: v4(),
+        name: 'Channel tag2',
+      },
+    ],
+    category: {
+      id: v4(),
+      name: 'CDA',
+    },
+    relationships: {
+      notifications: {
+        count: 13,
+      },
+      members: {
+        count: 2,
+      },
+      groups: {
+        count: 1,
+      },
+    },
+  },
+})
+export class ChannelResponse {
+  @IsNotEmpty()
+  @JSONSchema({
+    description: "The channel's id.",
+    example: v4(),
+  })
+  id: string;
+
+  @IsNotEmpty()
+  @IsString()
+  @JSONSchema({
+    description: 'Channel slug. Usually a slugified version of the name, eg channel-name.',
+    example: 'channelname',
+  })
+  slug: string;
+
+  @IsNotEmpty()
+  @JSONSchema({
+    description: "The channel's owner.",
+    example: {
+      id: v4(),
+      username: 'user',
+      email: 'useremail@cern.ch',
+      enabled: true,
+      created: new Date(),
+      lastLogin: new Date(),
+    },
+  })
+  owner: User;
+
+  @IsNotEmpty()
+  @IsString()
+  @JSONSchema({
+    description: "The channel's name.",
+    example: 'Channel name',
+  })
+  name: string;
+
+  @IsString()
+  @JSONSchema({
+    description: "The channel's description.",
+    example: 'This channel is not real.',
+  })
+  description: string;
+
+  @ValidateNested({ each: true })
+  @Type(() => User)
+  @JSONSchema({
+    description: "The channel's members.",
+    example: [
+      {
+        id: v4(),
+        username: 'user1',
+        email: 'user1email@cern.ch',
+        enabled: true,
+        created: new Date(),
+        lastLogin: new Date(),
+      },
+      {
+        id: v4(),
+        username: 'user2',
+        email: 'user2email@cern.ch',
+        enabled: true,
+        created: new Date(),
+        lastLogin: new Date(),
+      },
+    ],
+  })
+  members: User[];
+
+  @IsOptional()
+  @JSONSchema({
+    description: "The channel's admin group.",
+    example: {
+      id: v4(),
+      groupIdentifier: 'admingroupname',
+    },
+  })
+  adminGroup: Group;
+
+  @ValidateNested({ each: true })
+  @Type(() => User)
+  @JSONSchema({
+    description: "The channel's unsubscribed members.",
+    example: [
+      {
+        id: v4(),
+        username: 'user3',
+        email: 'user3email@cern.ch',
+        enabled: true,
+        created: new Date(),
+        lastLogin: new Date(),
+      },
+      {
+        id: v4(),
+        username: 'user4',
+        email: 'user4email@cern.ch',
+        enabled: true,
+        created: new Date(),
+        lastLogin: new Date(),
+      },
+    ],
+  })
+  unsubscribed: User[];
+
+  @ValidateNested({ each: true })
+  @Type(() => Group)
+  @JSONSchema({
+    description: "The channel's groups.",
+    example: [
+      {
+        id: v4(),
+        groupIdentifier: 'group1',
+      },
+      {
+        id: v4(),
+        groupIdentifier: 'group2',
+      },
+    ],
+  })
+  groups: Group[];
+
+  @IsNotEmpty()
+  @IsEnum(Visibility)
+  @JSONSchema({
+    description: "The channel's visibility.",
+    example: Visibility.restricted,
+  })
+  visibility: Visibility;
+
+  @IsBoolean()
+  @JSONSchema({
+    description: "The channel's archival option.",
+    example: true,
+  })
+  archive: boolean;
+
+  @IsString()
+  @JSONSchema({
+    description: "The channel's APIKey.",
+    example: 'ck_' + v4() + '_' + v4(),
+  })
+  APIKey: string;
+
+  @IsBoolean()
+  @JSONSchema({
+    description: "The channel's setting for allowing private notifications.",
+    example: false,
+  })
+  sendPrivate: boolean;
+
+  @IsDateString()
+  @JSONSchema({
+    description: "The channel's creation date.",
+    example: new Date(),
+  })
+  creationDate: Date;
+
+  @IsDateString()
+  @JSONSchema({
+    description: "The channel's last activity date.",
+    example: new Date(),
+  })
+  lastActivityDate: Date;
+
+  @IsEmail()
+  @JSONSchema({
+    description: "The channel's incoming email for notifications.",
+    example: 'channelname+level@cern.ch',
+  })
+  incomingEmail: string;
+
+  @IsString()
+  @JSONSchema({
+    description: "The channel's incoming egroup for notifications.",
+    example: 'incomingegroupname@cern.ch',
+  })
+  incomingEgroup: string;
+
+  @IsEnum(SubmissionByEmail, { each: true })
+  @JSONSchema({
+    description: 'Determines who and how can send notifications by email.',
+    example: [
+      SubmissionByEmail.administrators,
+      SubmissionByEmail.members,
+      SubmissionByEmail.email,
+      SubmissionByEmail.egroup,
+    ],
+  })
+  submissionByEmail: SubmissionByEmail[];
+
+  @IsEnum(SubmissionByForm, { each: true })
+  @JSONSchema({
+    description: 'Who can send notifications via Web or API. Default is ADMINISTRATORS.',
+    example: [SubmissionByForm.administrators, SubmissionByForm.members, SubmissionByForm.apikey],
+  })
+  submissionByForm: SubmissionByForm[];
+
+  @IsOptional()
+  @IsDateString()
+  @JSONSchema({
+    description: "The channel's deletion date.",
+    example: new Date(),
+  })
+  deleteDate: Date;
+
+  @IsBoolean()
+  @IsOptional()
+  @JSONSchema({
+    description: 'Whether the user is subscribed to this channel.',
+    example: true,
+  })
+  subscribed: boolean;
+
+  @IsBoolean()
+  @IsOptional()
+  @JSONSchema({
+    description: 'Whether the user can manage this channel.',
+    example: true,
+  })
+  manage: boolean;
+
+  @IsBoolean()
+  @IsOptional()
+  @JSONSchema({
+    description: 'Whether the user can access this channel.',
+    example: true,
+  })
+  access: boolean;
+
+  @IsBoolean()
+  @IsOptional()
+  @JSONSchema({
+    description: 'Whether the user can send notifications to this channel.',
+    example: true,
+  })
+  send: boolean;
+
+  @Type(() => CategoryResponse)
+  @IsOptional()
+  @JSONSchema({
+    description: 'Channel category.',
+    example: {
+      name: 'CDA',
+      id: v4(),
+    },
+  })
+  category: CategoryResponse;
+
+  @IsOptional()
+  @ValidateNested({ each: true })
+  @Type(() => Tag)
+  @JSONSchema({
+    description: "The channel's Tags.",
+    example: [
+      {
+        id: v4(),
+        name: 'Channel tag1',
+      },
+      {
+        id: v4(),
+        name: 'Channel tag2',
+      },
+    ],
+  })
+  tags: Tag[];
+
+  @IsOptional()
+  @Type(() => Relationship)
+  @JSONSchema({
+    description: "The channel's Tags.",
+    example: {
+      notifications: {
+        count: 13,
+      },
+      members: {
+        count: 2,
+      },
+      groups: {
+        count: 1,
+      },
+    },
+  })
+  relationships: Relationship;
+
+  @IsEnum(ChannelFlags, { each: true })
+  @JSONSchema({
+    description: 'List of labels related to the channel.',
+    example: [ChannelFlags.critical, ChannelFlags.mandatory],
+  })
+  channelFlags: ChannelFlags[];
+
+  constructor(channel: Channel) {
+    this.id = channel.id;
+    this.slug = channel.slug;
+    this.owner = channel.owner;
+    this.name = channel.name;
+    this.description = channel.description;
+    this.members = channel.members;
+    this.adminGroup = channel.adminGroup;
+    this.unsubscribed = channel.unsubscribed;
+    this.groups = channel.groups;
+    //this.notifications = channel.notifications;
+    this.visibility = channel.visibility;
+    this.archive = channel.archive;
+    this.APIKey = channel.APIKey;
+    this.creationDate = channel.creationDate;
+    this.lastActivityDate = channel.lastActivityDate;
+    this.incomingEmail = channel.incomingEmail;
+    this.incomingEgroup = channel.incomingEgroup;
+    this.submissionByEmail = channel.submissionByEmail;
+    this.submissionByForm = channel.submissionByForm;
+    this.deleteDate = channel.deleteDate;
+    this.category = channel.category;
+    this.tags = channel.tags;
+    this.sendPrivate = channel.sendPrivate;
+    this.relationships = new Relationship(
+      new NotificationsListResponse(channel.notifications),
+      new MembersListResponse(channel.members),
+      new GroupsResponse(channel.groups?.length || 0),
+    );
+    this.channelFlags = channel.channelFlags;
+  }
+
+  async channelFilters(authBag: AuthorizationBag, chan: Channel): Promise<void> {
+    //this.subscribed = await chan.isSubscribedByName(authBag.userName);
+    this.manage = await chan.isAdmin(authBag);
+    this.access = await chan.hasAccess(authBag);
+    this.send = await chan.canSendByForm(authBag);
+  }
+}
+
+@JSONSchema({
+  description: 'Channel member json object.',
+  example: {},
+})
+export class MemberResponse {
+  @IsString()
+  @IsUUID('4')
+  @IsNotEmpty()
+  @IsString()
+  @JSONSchema({
+    description: 'Member id.',
+    example: v4(),
+  })
+  id: string;
+
+  @IsNotEmpty()
+  @IsString()
+  @JSONSchema({
+    description: 'Member username.',
+    example: 'username',
+  })
+  username: string;
+
+  @IsNotEmpty()
+  @IsString()
+  @IsEmail()
+  @JSONSchema({
+    description: 'Member email.',
+    example: 'useremail@cern.ch',
+  })
+  email: string;
+
+  constructor(user: User) {
+    this.id = user.id;
+    this.username = user.username;
+    this.email = user.email;
+  }
+}
+
+export class GroupResponse {
+  @IsNotEmpty()
+  @IsString()
+  id: string;
+
+  @IsNotEmpty()
+  @IsString()
+  groupIdentifier: string;
+
+  constructor(group: Group) {
+    this.id = group.id;
+    this.groupIdentifier = group.groupIdentifier;
+  }
+}
+
+//REQUESTS
+export class GroupRequest {
+  @IsString()
+  groupIdentifier: string;
+}
+
+@JSONSchema({
+  description: 'Channel create request object.',
+  example: {
+    name: 'Channel name',
+    slug: 'channel-name',
+    description: 'This channel is not real.',
+    visibility: Visibility.restricted,
+    archive: true,
+    submissionByForm: [SubmissionByForm.administrators],
+    submissionByEmail: [SubmissionByEmail.administrators],
+  },
+})
+export class CreateChannelRequest {
+  @IsString()
+  @JSONSchema({
+    description: 'Channel name.',
+    example: 'Channel name',
+  })
+  name: string;
+
+  @IsString()
+  @JSONSchema({
+    description: 'Channel slug. Usually a slugified version of the name, eg channel-name.',
+    example: 'channel-name',
+  })
+  slug: string;
+
+  @IsString()
+  @JSONSchema({
+    description: 'Channel description.',
+    example: 'This channel is not real.',
+  })
+  description: string;
+
+  @IsOptional()
+  @IsString()
+  @JSONSchema({
+    description: 'Channel description.',
+    example: 'groupname',
+  })
+  adminGroup: string;
+
+  @IsEnum(Visibility)
+  @JSONSchema({
+    description: "Channel's visibility.",
+    example: Visibility.restricted,
+  })
+  visibility: Visibility;
+
+  @IsOptional()
+  @IsString()
+  @JSONSchema({
+    description:
+      "The channel's incoming egroup for notifications. Needs " +
+      SubmissionByEmail.egroup +
+      " to be set on 'submissionByEmail'.",
+    example: 'egroupname',
+  })
+  incomingEgroup: string;
+
+  @IsOptional()
+  @IsEmail()
+  @JSONSchema({
+    description: "The channel's incoming email for notifications.",
+    example: 'channelname+level@cern.ch',
+  })
+  incomingEmail: string;
+
+  @IsBoolean()
+  @JSONSchema({
+    description:
+      "Allows enabling preservation of the channel's notifications into an external archive. Notifications are deleted from our system (not the archive) after 13 months in any case.",
+    example: true,
+  })
+  archive: boolean;
+
+  @IsEnum(SubmissionByForm, { each: true })
+  @JSONSchema({
+    description: 'Who can send notifications via Web or API. Default is ADMINISTRATORS.',
+    example: [SubmissionByForm.administrators, SubmissionByForm.members, SubmissionByForm.apikey],
+  })
+  submissionByForm: SubmissionByForm[];
+
+  @IsEnum(SubmissionByEmail, { each: true })
+  @JSONSchema({
+    description:
+      'Who and how can send notifications by email. To use ' +
+      SubmissionByEmail.egroup +
+      " option, needs 'incomingEgroup' to be set.",
+    example: [
+      SubmissionByEmail.administrators,
+      SubmissionByEmail.members,
+      SubmissionByEmail.email,
+      SubmissionByEmail.egroup,
+    ],
+  })
+  submissionByEmail: SubmissionByEmail[];
+}
+
+@JSONSchema({
+  description: 'Information that the channel is to be updated with.',
+  example: {
+    id: v4(),
+    description: 'New channel description.',
+    name: 'New Channel name',
+    visibility: Visibility.public,
+    sendPrivate: false,
+    archive: true,
+    submissionByEmail: [],
+    submissionByForm: [SubmissionByForm.administrators],
+  },
+})
+export class UpdateChannelRequest {
+  @IsBoolean()
+  @JSONSchema({
+    description:
+      "Allows enabling preservation of the channel's notifications into an external archive. Notifications are deleted from our system (not the archive) after 13 months in any case.",
+    example: true,
+  })
+  archive: boolean;
+
+  @IsString()
+  @JSONSchema({
+    description: 'Channel description.',
+    example: 'Service updates for CERN Notifications.',
+  })
+  description: string;
+
+  @IsNotEmpty()
+  @IsUUID('4')
+  @JSONSchema({
+    description: 'Channel id.',
+    example: v4(),
+  })
+  id: string;
+
+  @IsOptional()
+  @IsString()
+  @JSONSchema({
+    description:
+      "The channel's incoming egroup for notifications. Needs " +
+      SubmissionByEmail.egroup +
+      " to be set on 'submissionByEmail'.",
+    example: 'egroupname',
+  })
+  incomingEgroup: string;
+
+  @IsOptional()
+  @IsEmail()
+  @JSONSchema({
+    description: "The channel's incoming email for notifications.",
+    example: 'channelname+level@cern.ch',
+  })
+  incomingEmail: string;
+
+  @IsString()
+  @JSONSchema({
+    description: "The channel's name.",
+    example: 'Channel name',
+  })
+  name: string;
+
+  @IsBoolean()
+  @JSONSchema({
+    description: "The channel's setting for allowing private notifications.",
+    example: false,
+  })
+  sendPrivate: boolean;
+
+  @IsEnum(SubmissionByEmail, { each: true })
+  @JSONSchema({
+    description:
+      'Determines who and how can send notifications by email. To use ' +
+      SubmissionByEmail.egroup +
+      " option, needs 'incomingEgroup' to be set.",
+    example: [SubmissionByEmail.administrators],
+  })
+  submissionByEmail: SubmissionByEmail[];
+
+  @IsEnum(SubmissionByForm, { each: true })
+  @JSONSchema({
+    description: 'Who can send notifications via Web or API. Default is ADMINISTRATORS.',
+    example: [SubmissionByForm.administrators],
+  })
+  submissionByForm: SubmissionByForm[];
+
+  @IsEnum(Visibility)
+  @JSONSchema({
+    description: "The channel's visibility.",
+    example: Visibility.restricted,
+  })
+  visibility: Visibility;
+}
+
+@JSONSchema({
+  description: 'List of Tag ids.',
+  example: {
+    tagIds: [v4(), v4()],
+  },
+})
+export class setTagsRequest {
+  @JSONSchema({
+    description: 'List of Tag ids.',
+    example: [v4(), v4()],
+  })
+  @IsUUID('4', { each: true })
+  tagIds: string[];
+}
diff --git a/src/controllers/notifications/dto.ts b/src/controllers/notifications/dto.ts
index c0a90cab4f96520d14acc8d25765c242dd40aca0..1c74641dd1677dd3fb3e0103c90cdcd9958e15e4 100644
--- a/src/controllers/notifications/dto.ts
+++ b/src/controllers/notifications/dto.ts
@@ -11,9 +11,9 @@ import {
   ValidateNested,
 } from 'class-validator';
 import { Type } from 'class-transformer';
+import { JSONSchema } from 'class-validator-jsonschema';
 import { Notification } from '../../models/notification';
 import { PriorityLevel, Source } from '../../models/notification-enums';
-import { JSONSchema } from 'class-validator-jsonschema';
 import { v4 } from 'uuid';
 
 const swaggerChannelId = process.env.SWAGGER_CHANNEL_ID;
diff --git a/src/models/cern-authorization-service.ts b/src/models/cern-authorization-service.ts
index 099af70c88735056e08651540a11e521dedff80e..91c5f67bf12fd25d3a43fb4cd8b9023893604088 100644
--- a/src/models/cern-authorization-service.ts
+++ b/src/models/cern-authorization-service.ts
@@ -71,8 +71,7 @@ export class CernAuthorizationService {
         console.error(ex.error);
       }
     }
-
-    return;
+    return undefined; //search was unsuccesful
   }
 
   static async getGroupIdNoCache(groupIdentifier: string) {
diff --git a/src/models/channel.ts b/src/models/channel.ts
index 23ea87b2948b75e52ce1ec1702919d315d596621..ce11786f0615bf1026e0a5e11ca4ca2406f7070f 100644
--- a/src/models/channel.ts
+++ b/src/models/channel.ts
@@ -21,7 +21,7 @@ import { AlphaNumericLowercase, AlphaNumericPunctuationChannelName, FromEmailSet
 import { AuthorizationBag } from './authorization-bag';
 import { SubmissionByEmail, SubmissionByForm, SubscriptionPolicy, Visibility, ChannelFlags } from './channel-enums';
 import { ChannelHelpers } from './channel-helpers';
-
+import { Type } from 'class-transformer';
 import { ApiKeyObject } from './api-key-object';
 import { APIKeyTypeEnum } from '../middleware/api-key';
 
@@ -52,6 +52,7 @@ export class Channel extends ApiKeyObject {
   })
   //  @JoinTable()
   @JoinTable({ name: 'channels_members__users' })
+  @Type(() => User)
   members: User[];
 
   @IsOptional()
@@ -61,28 +62,29 @@ export class Channel extends ApiKeyObject {
   @ManyToMany(type => User, user => user.unsubscribed, {
     cascade: true,
   })
-  // @JoinTable()
   @JoinTable({ name: 'channels_unsubscribed__users' })
+  @Type(() => User)
   unsubscribed: User[];
 
   @ManyToMany(type => Group)
-  // @JoinTable()
   @JoinTable({ name: 'channels_groups__groups' })
+  @Type(() => Group)
   groups: Group[];
 
   @OneToMany(type => Notification, notification => notification.target, {
     cascade: true,
   })
+  @Type(() => Notification)
   notifications: Notification[];
 
   @Column({ enum: Visibility, default: Visibility.restricted })
-  visibility: string;
+  visibility: Visibility;
 
   @Column({
     enum: SubscriptionPolicy,
     default: SubscriptionPolicy.selfSubscription,
   })
-  subscriptionPolicy: string;
+  subscriptionPolicy: SubscriptionPolicy;
 
   @Column({ default: true })
   archive: boolean;
@@ -122,6 +124,7 @@ export class Channel extends ApiKeyObject {
     nullable: false,
     default: [],
   })
+  //@Type(() => enum)
   submissionByEmail: SubmissionByEmail[];
 
   @Column({
@@ -131,6 +134,7 @@ export class Channel extends ApiKeyObject {
     nullable: false,
     default: [SubmissionByForm.administrators],
   })
+  //@Type(() => SubmissionByForm)
   submissionByForm: SubmissionByForm[];
 
   @DeleteDateColumn()
@@ -162,6 +166,7 @@ export class Channel extends ApiKeyObject {
   constructor(channel) {
     super();
     if (channel) {
+      this.id = channel.id;
       this.slug = channel.slug;
       this.name = channel.name;
       this.description = channel.description;
diff --git a/src/services/channels-service.ts b/src/services/channels-service.ts
index 27ecc6eacf9cb74deb167a876097ce3a7bfd21bb..f98d50f2581ea1dcfb3e2c501dc267a3755607d0 100644
--- a/src/services/channels-service.ts
+++ b/src/services/channels-service.ts
@@ -1,101 +1,73 @@
-import { User } from '../models/user';
-import { Channel } from '../models/channel';
-import { Group } from '../models/group';
 import { Category } from '../models/category';
-import { Tag } from '../models/tag';
 import { AuthorizationBag } from '../models/authorization-bag';
 import { ApiKeyService } from './api-key-service';
+import {
+  ChannelResponse,
+  ChannelsListResponse,
+  CreateChannelRequest,
+  UpdateChannelRequest,
+  ChannelsQuery,
+  Query,
+  MembersListResponse,
+  GroupsListResponse,
+  GroupResponse,
+  GetChannelResponse,
+  EditChannelResponse,
+  setTagsRequest,
+  ChannelStatsResponse,
+  MemberResponse,
+} from '../controllers/channels/dto';
 
 export interface ChannelsService extends ApiKeyService {
-  getAllChannels(query, authorizationBag: AuthorizationBag): Promise<Channel[]>;
+  getAllChannels(query: ChannelsQuery, authorizationBag: AuthorizationBag): Promise<ChannelsListResponse>;
 
-  getAllPublicChannels(query): Promise<Channel[]>;
+  getAllPublicChannels(query): Promise<ChannelsListResponse>; //public controller
 
-  getChannelById(
-    channelId: string,
-    authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  getChannelById(channelId: string, authorizationBag: AuthorizationBag): Promise<GetChannelResponse>;
 
-  editChannelById(
-    channelId: string,
-    authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  editChannelById(channelId: string, authorizationBag: AuthorizationBag): Promise<EditChannelResponse>;
 
-  createChannel(
-    channel: Channel,
-    authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  createChannel(channel: CreateChannelRequest, authorizationBag: AuthorizationBag): Promise<ChannelResponse>;
 
-  deleteChannel(
-    channelId: string,
-    authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  deleteChannel(channelId: string, authorizationBag: AuthorizationBag): Promise<string>;
 
-  updateChannel(
-    channel: Channel,
-    authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  updateChannel(channel: UpdateChannelRequest, authorizationBag: AuthorizationBag): Promise<ChannelResponse>;
 
-  getChannelMembers(
-    channelId: String,
-    query,
-    authorizationBag: AuthorizationBag,
-  ): Promise<User[]>;
+  updateChannel(channel: UpdateChannelRequest, authorizationBag: AuthorizationBag): Promise<ChannelResponse>;
 
-  getChannelGroups(
-    channelId: String,
-    query,
-    authorizationBag: AuthorizationBag,
-  ): Promise<Group[]>;
+  getChannelMembers(channelId: string, query: Query, authorizationBag: AuthorizationBag): Promise<MembersListResponse>;
 
-  addGroupToChannel(
-    group: Group,
-    channelId: string,
-    authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  getChannelGroups(channelId: string, query: Query, authorizationBag: AuthorizationBag): Promise<GroupsListResponse>;
 
-  removeGroupFromChannel(groupId: string, channalId: string, authorizationBag: AuthorizationBag): Promise<Channel>;
+  addGroupToChannel(groupName: string, channelId: string, authorizationBag: AuthorizationBag): Promise<GroupResponse>;
+
+  removeGroupFromChannel(groupId: string, channelId: string, authorizationBag: AuthorizationBag): Promise<string>;
 
   updateChannelAdminGroup(
-    group: Group,
+    groupName: string,
     channelId: string,
     authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  ): Promise<ChannelResponse>;
 
   addMemberToChannel(
     membername: string,
     channelId: string,
     authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  ): Promise<MemberResponse>;
 
-  removeMemberFromChannel(memberId: string, channelId: string, authorizationBag: AuthorizationBag): Promise<Channel>;
+  removeMemberFromChannel(memberId: string, channelId: string, authorizationBag: AuthorizationBag): Promise<string>;
 
-  subscribeToChannel(
-    channelId: string,
-    authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  subscribeToChannel(channelId: string, authorizationBag: AuthorizationBag): Promise<ChannelResponse>;
 
-  unsubscribeFromChannel(
-    channelId: string,
-    authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  unsubscribeFromChannel(channelId: string, authorizationBag: AuthorizationBag): Promise<ChannelResponse>;
 
-  setCategory(channelId: string, category: Category, authorizationBag: AuthorizationBag): Promise<Channel>;
+  generateApiKey(channelId: string, authorizationBag: AuthorizationBag): Promise<string>;
 
-  setTags(
-    channelId: string,
-    tags: Tag[],
-    authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  setCategory(channelId: string, category: Category, authorizationBag: AuthorizationBag): Promise<ChannelResponse>;
 
-  setChannelOwner(
-    username: string,
-    channelId: string,
-    authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  setTags(channelId: string, tags: setTagsRequest, authorizationBag: AuthorizationBag): Promise<ChannelResponse>;
 
-  getChannelStats(
-    channelId: string,
-    authorizationBag: AuthorizationBag,
-  ): Promise<Channel>;
+  setChannelOwner(username: string, channelId: string, authorizationBag: AuthorizationBag): Promise<ChannelResponse>;
+
+  getChannelStats(channelId: string, authorizationBag: AuthorizationBag): Promise<ChannelStatsResponse>;
 }
diff --git a/src/services/impl/channels-service-impl.ts b/src/services/impl/channels-service-impl.ts
index 62347f84f9a3029a13423a1ce14db22095dd0d48..557dd6d6bec8cd157f23edba490765cf750c58e9 100644
--- a/src/services/impl/channels-service-impl.ts
+++ b/src/services/impl/channels-service-impl.ts
@@ -1,3 +1,7 @@
+import { SetChannelOwner } from './channels/set-channel-owner';
+import { ApiKeyService } from '../api-key-service';
+import { VerifyApiKey } from './api-key/verify-api-key';
+import { GetChannelStats } from './channels/get-channel-stats';
 import { ChannelsService } from '../channels-service';
 import { AbstractService } from './abstract-service';
 import { Channel } from '../../models/channel';
@@ -6,12 +10,10 @@ import { FindChannelById } from './channels/find-channel-by-id';
 import { EditChannelById } from './channels/edit-channel-by-id';
 import { CreateChannel } from './channels/create-channel';
 import { AddGroupToChannel } from './channels/add-group-to-channel';
-import { Group } from '../../models/group';
 import { SubscribeToChannel } from './channels/subscribe-to-channel';
 import { UnsubscribeFromChannel } from './channels/unsubscribe-from-channel';
 import { UpdateChannel } from './channels/update-channel';
 import { GetChannelMembers } from './channels/get-channel-members';
-import { User } from '../../models/user';
 import { GetChannelGroups } from './channels/get-channel-groups';
 import { FindPublicChannels } from './channels/find-public-channels';
 import { AddMemberToChannel } from './channels/add-member-to-channel';
@@ -22,94 +24,111 @@ import { UpdateChannelAdminGroup } from './channels/update-channel-admin-group';
 import { GenerateApiKey } from './channels/generate-api-key';
 import { AuthorizationBag } from '../../models/authorization-bag';
 import { Category } from '../../models/category';
-import { Tag } from '../../models/tag';
 import { SetCategory } from './channels/set-category';
 import { SetTags } from './channels/set-tags';
-import { SetChannelOwner } from './channels/set-channel-owner';
-import { ApiKeyService } from '../api-key-service';
-import { VerifyApiKey } from './api-key/verify-api-key';
-import { GetChannelStats } from './channels/get-channel-stats';
-
+import {
+  ChannelResponse,
+  ChannelsListResponse,
+  CreateChannelRequest,
+  UpdateChannelRequest,
+  MembersListResponse,
+  GroupsListResponse,
+  ChannelsQuery,
+  GroupResponse,
+  GetChannelResponse,
+  EditChannelResponse,
+  setTagsRequest,
+  ChannelStatsResponse,
+  MemberResponse,
+  Query,
+} from '../../controllers/channels/dto';
 
 export class ChannelsServiceImpl extends AbstractService implements ChannelsService, ApiKeyService {
-  getAllChannels(query, authorizationBag: AuthorizationBag): Promise<Channel[]> {
+  getAllChannels(query: ChannelsQuery, authorizationBag: AuthorizationBag): Promise<ChannelsListResponse> {
     return this.commandExecutor.execute(new FindAllChannels(query, authorizationBag));
   }
 
-  getAllPublicChannels(query): Promise<Channel[]> {
+  getAllPublicChannels(query: Query): Promise<ChannelsListResponse> {
     return this.commandExecutor.execute(new FindPublicChannels(query));
   }
 
-  getChannelById(channelId: string, authorizationBag: AuthorizationBag): Promise<Channel> {
+  getChannelById(channelId: string, authorizationBag: AuthorizationBag): Promise<GetChannelResponse> {
     return this.commandExecutor.execute(new FindChannelById(channelId, authorizationBag));
   }
-
-  editChannelById(channelId: string, authorizationBag: AuthorizationBag): Promise<Channel> {
+  editChannelById(channelId: string, authorizationBag: AuthorizationBag): Promise<EditChannelResponse> {
     return this.commandExecutor.execute(new EditChannelById(channelId, authorizationBag));
   }
 
-  createChannel(channel: Channel, authorizationBag: AuthorizationBag): Promise<Channel> {
+  createChannel(channel: CreateChannelRequest, authorizationBag: AuthorizationBag): Promise<ChannelResponse> {
     return this.commandExecutor.execute(new CreateChannel(channel, authorizationBag));
   }
 
-  deleteChannel(channelId: string, authorizationBag: AuthorizationBag): Promise<Channel> {
+  deleteChannel(channelId: string, authorizationBag: AuthorizationBag): Promise<string> {
     return this.commandExecutor.execute(new DeleteChannel(channelId, authorizationBag));
   }
 
-  updateChannel(channel: Channel, authorizationBag: AuthorizationBag): Promise<Channel> {
+  updateChannel(channel: UpdateChannelRequest, authorizationBag: AuthorizationBag): Promise<ChannelResponse> {
     return this.commandExecutor.execute(new UpdateChannel(channel, authorizationBag));
   }
 
-  getChannelMembers(channelId: string, query, authorizationBag: AuthorizationBag): Promise<User[]> {
+  getChannelMembers(channelId: string, query: Query, authorizationBag: AuthorizationBag): Promise<MembersListResponse> {
     return this.commandExecutor.execute(new GetChannelMembers(channelId, query, authorizationBag));
   }
 
-  getChannelGroups(channelId: string, query, authorizationBag: AuthorizationBag): Promise<Group[]> {
+  getChannelGroups(channelId: string, query: Query, authorizationBag: AuthorizationBag): Promise<GroupsListResponse> {
     return this.commandExecutor.execute(new GetChannelGroups(channelId, query, authorizationBag));
   }
 
-  addGroupToChannel(group: Group, channelId: string, authorizationBag: AuthorizationBag): Promise<Channel> {
-    return this.commandExecutor.execute(new AddGroupToChannel(group, channelId, authorizationBag));
+  addGroupToChannel(groupName: string, channelId: string, authorizationBag: AuthorizationBag): Promise<GroupResponse> {
+    return this.commandExecutor.execute(new AddGroupToChannel(groupName, channelId, authorizationBag));
   }
 
-  removeGroupFromChannel(groupId: string, channelId: string, authorizationBag: AuthorizationBag): Promise<Channel> {
+  removeGroupFromChannel(groupId: string, channelId: string, authorizationBag: AuthorizationBag): Promise<string> {
     return this.commandExecutor.execute(new RemoveGroupFromChannel(groupId, channelId, authorizationBag));
   }
 
-  updateChannelAdminGroup(group: Group, channelId: string, authorizationBag: AuthorizationBag): Promise<Channel> {
-    return this.commandExecutor.execute(new UpdateChannelAdminGroup(group, channelId, authorizationBag));
+  updateChannelAdminGroup(
+    groupName: string,
+    channelId: string,
+    authorizationBag: AuthorizationBag,
+  ): Promise<ChannelResponse> {
+    return this.commandExecutor.execute(new UpdateChannelAdminGroup(groupName, channelId, authorizationBag));
   }
 
-  addMemberToChannel(membername: string, channelId: string, authorizationBag: AuthorizationBag): Promise<Channel> {
+  addMemberToChannel(
+    membername: string,
+    channelId: string,
+    authorizationBag: AuthorizationBag,
+  ): Promise<MemberResponse> {
     return this.commandExecutor.execute(new AddMemberToChannel(membername, channelId, authorizationBag));
   }
 
-  removeMemberFromChannel(memberId: string, channelId: string, authorizationBag: AuthorizationBag): Promise<Channel> {
+  removeMemberFromChannel(memberId: string, channelId: string, authorizationBag: AuthorizationBag): Promise<string> {
     return this.commandExecutor.execute(
       // missing username to check permission
       new RemoveUserFromChannel(memberId, channelId, authorizationBag),
     );
   }
 
-  subscribeToChannel(channelId: string, authorizationBag: AuthorizationBag): Promise<Channel> {
+  subscribeToChannel(channelId: string, authorizationBag: AuthorizationBag): Promise<ChannelResponse> {
     return this.commandExecutor.execute(new SubscribeToChannel(authorizationBag.userId, channelId, authorizationBag));
   }
 
-  unsubscribeFromChannel(channelId: string, authorizationBag: AuthorizationBag): Promise<Channel> {
+  unsubscribeFromChannel(channelId: string, authorizationBag: AuthorizationBag): Promise<ChannelResponse> {
     return this.commandExecutor.execute(
       new UnsubscribeFromChannel(authorizationBag.userId, channelId, authorizationBag),
     );
   }
 
-  setCategory(channelId: string, category: Category, authorizationBag: AuthorizationBag): Promise<Channel> {
+  setCategory(channelId: string, category: Category, authorizationBag: AuthorizationBag): Promise<ChannelResponse> {
     return this.commandExecutor.execute(new SetCategory(channelId, category, authorizationBag));
   }
 
-  setTags(channelId: string, tags: Tag[], authorizationBag: AuthorizationBag): Promise<Channel> {
+  setTags(channelId: string, tags: setTagsRequest, authorizationBag: AuthorizationBag): Promise<ChannelResponse> {
     return this.commandExecutor.execute(new SetTags(channelId, tags, authorizationBag));
   }
 
-  setChannelOwner(username: string, channelId: string, authorizationBag: AuthorizationBag): Promise<Channel> {
+  setChannelOwner(username: string, channelId: string, authorizationBag: AuthorizationBag): Promise<ChannelResponse> {
     return this.commandExecutor.execute(new SetChannelOwner(username, channelId, authorizationBag));
   }
 
@@ -121,12 +140,7 @@ export class ChannelsServiceImpl extends AbstractService implements ChannelsServ
     return this.commandExecutor.execute(new VerifyApiKey(id, key, Channel));
   }
 
-  getChannelStats(
-    channelId: string,
-    authorizationBag: AuthorizationBag,
-  ): Promise<Channel> {
-    return this.commandExecutor.execute(
-      new GetChannelStats(channelId, authorizationBag),
-    );
+  getChannelStats(channelId: string, authorizationBag: AuthorizationBag): Promise<ChannelStatsResponse> {
+    return this.commandExecutor.execute(new GetChannelStats(channelId, authorizationBag));
   }
-}
\ No newline at end of file
+}
diff --git a/src/services/impl/channels/add-group-to-channel.ts b/src/services/impl/channels/add-group-to-channel.ts
index be9c4df04d2bb9d77673e1746e3d634850338327..e850a794de0f0fbcdf7a9029e27e53dddbd83a6f 100644
--- a/src/services/impl/channels/add-group-to-channel.ts
+++ b/src/services/impl/channels/add-group-to-channel.ts
@@ -1,19 +1,22 @@
 import { Command } from '../command';
 import { EntityManager } from 'typeorm';
 import { Channel } from '../../../models/channel';
-import { Group } from '../../../models/group';
 import { ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
 import { ServiceFactory } from '../../services-factory';
 import { GroupsServiceInterface } from '../../groups-service';
 import { AuditChannels } from '../../../log/auditing';
+import { ChannelResponse } from '../../../controllers/channels/dto';
+import { Group } from "../../../models/group";
+import { GroupResponse } from "../../../controllers/channels/dto";
+import { CernAuthorizationService } from "../../../models/cern-authorization-service";
 
 export class AddGroupToChannel implements Command {
-  constructor(private group: Group, private channelId: string, private authorizationBag: AuthorizationBag) {}
-
+  constructor(private groupName: string, private channelId: string, private authorizationBag: AuthorizationBag) { }
   groupsService: GroupsServiceInterface = ServiceFactory.getGroupService();
+  
 
-  async execute(transactionManager: EntityManager): Promise<Channel> {
+  async execute(transactionManager: EntityManager): Promise<GroupResponse> {
     const channel = await transactionManager.findOne(Channel, {
       relations: ['owner', 'adminGroup', 'groups'],
       where: {
@@ -28,19 +31,19 @@ export class AddGroupToChannel implements Command {
       throw new ForbiddenError("You don't have the rights to manage this channel.");
 
     //if added group was already added
-    if (channel.groups.filter(g => g.groupIdentifier === this.group.groupIdentifier).length > 0) {
-      throw new ForbiddenError(`Group ${this.group.groupIdentifier} is already a member.`);
+    if (channel.groups.filter(g => g.groupIdentifier === this.groupName).length > 0) {
+      throw new ForbiddenError(`Group ${this.groupName} is already a member.`);
     }
 
-    const groupToAdd = await this.groupsService.GetOrCreateGroup(this.group.groupIdentifier);
+    const groupToAdd = await this.groupsService.GetOrCreateGroup(this.groupName);
     channel.addGroup(groupToAdd);
     const updatedChannel = await transactionManager.save(channel);
     await AuditChannels.setValue(updatedChannel.id, {
       event: 'AddGroup',
       user: this.authorizationBag.email,
-      groupIdentifier: this.group.groupIdentifier,
+      groupIdentifier: this.groupName,
     });
 
-    return updatedChannel;
+    return new GroupResponse(groupToAdd);
   }
 }
diff --git a/src/services/impl/channels/add-member-to-channel.ts b/src/services/impl/channels/add-member-to-channel.ts
index bf2d1336d8c46e09d1a924f876b220416b661907..a71b37b0cc42d7ba8a48459600ec6a5239ca1d74 100644
--- a/src/services/impl/channels/add-member-to-channel.ts
+++ b/src/services/impl/channels/add-member-to-channel.ts
@@ -7,6 +7,9 @@ import { ServiceFactory } from '../../services-factory';
 import { AuthorizationBag } from '../../../models/authorization-bag';
 import { UsersServiceInterface } from '../../users-service';
 import { AuditChannels } from '../../../log/auditing';
+import { AuthService } from "../../../services/auth-service";
+import { CernAuthorizationService } from "../../../models/cern-authorization-service";
+import { MemberResponse, ChannelResponse } from "../../../controllers/channels/dto";
 
 export class AddMemberToChannel implements Command {
   constructor(private membername: string, private channelId: string, private authorizationBag: AuthorizationBag) {}
@@ -19,7 +22,7 @@ export class AddMemberToChannel implements Command {
     return new User({ username: this.membername });
   }
 
-  async execute(transactionManager: EntityManager): Promise<Channel> {
+  async execute(transactionManager: EntityManager): Promise<MemberResponse> {
     const channel = await transactionManager.findOne(Channel, {
       relations: ['owner', 'adminGroup', 'members', 'groups'],
       where: {
@@ -55,6 +58,6 @@ export class AddMemberToChannel implements Command {
       membername: this.membername,
     });
 
-    return channel;
+    return new MemberResponse(userToAdd);
   }
 }
diff --git a/src/services/impl/channels/create-channel.ts b/src/services/impl/channels/create-channel.ts
index b47ccf1ada3e371fa78c90323547364042b8b772..16d492b5da5f7ec78325fe195a34293eaa4fda1a 100644
--- a/src/services/impl/channels/create-channel.ts
+++ b/src/services/impl/channels/create-channel.ts
@@ -8,11 +8,13 @@ import { User } from '../../../models/user';
 import { AuthorizationBag } from '../../../models/authorization-bag';
 import { prepareValidationErrorList } from './validation-utils';
 import { AuditChannels } from '../../../log/auditing';
+import { ChannelResponse, CreateChannelRequest } from '../../../controllers/channels/dto';
+import { SubscriptionPolicy } from '../../../models/channel-enums';
 
 export class CreateChannel implements Command {
-  constructor(private channel: Channel, private authorizationBag: AuthorizationBag) {}
+  constructor(private channel: CreateChannelRequest, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager): Promise<Channel> {
+  async execute(transactionManager: EntityManager): Promise<ChannelResponse> {
     // Get owner (user who have created the channel) to set as user by default
     const user = await transactionManager.findOne(User, {
       id: this.authorizationBag.userId,
@@ -20,9 +22,9 @@ export class CreateChannel implements Command {
 
     const channel = new Channel({
       ...this.channel,
-      adminGroup: this.channel.adminGroup.groupIdentifier !== '' ? new Group(this.channel.adminGroup) : undefined,
       owner: this.authorizationBag.userId,
       members: [user],
+      subscriptionPolicy: SubscriptionPolicy.dynamic, //TODO TO be removed once feature is added
     });
     channel.name = channel.name.trim();
 
@@ -48,8 +50,12 @@ export class CreateChannel implements Command {
       throw new BadRequestError('Channel slug already exists');
     }
 
-    if (channel.adminGroup && !(await channel.adminGroup.exists())) {
-      throw new NotFoundError('Admin group does not exist');
+    if (this.channel.adminGroup) {
+      const adminGroup = await transactionManager.findOne(Group, { groupIdentifier: this.channel.adminGroup });
+      if (!adminGroup) throw new NotFoundError('Admin group does not exist');
+      if (adminGroup.groupIdentifier != this.channel.adminGroup)
+        throw new Error('Admin group name does not match provided id.');
+      channel.adminGroup = adminGroup;
     }
 
     const validationErrors = await validate(channel);
@@ -60,6 +66,7 @@ export class CreateChannel implements Command {
 
     await AuditChannels.setValue(createdChannel.id, { event: 'Create', user: this.authorizationBag.email });
 
-    return createdChannel;
+    //TODO could go wrong if transaction manager fails. test this
+    return new ChannelResponse(createdChannel);
   }
 }
diff --git a/src/services/impl/channels/delete-channel.ts b/src/services/impl/channels/delete-channel.ts
index b02335c8a3814faa264706df31119217777066c3..057cd928f1d9da022ae43f7d457583f94b927a56 100644
--- a/src/services/impl/channels/delete-channel.ts
+++ b/src/services/impl/channels/delete-channel.ts
@@ -1,5 +1,5 @@
 import { Command } from '../command';
-import { EntityManager } from 'typeorm';
+import { EntityManager, UpdateResult } from 'typeorm';
 import { Channel } from '../../../models/channel';
 import { ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
@@ -8,7 +8,7 @@ import { AuditChannels } from '../../../log/auditing';
 export class DeleteChannel implements Command {
   constructor(private channelId: string, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager): Promise<any> {
+  async execute(transactionManager: EntityManager): Promise<string> {
     const channel = await transactionManager.findOne(Channel, {
       relations: ['members', 'groups', 'owner', 'adminGroup'],
       where: {
@@ -27,9 +27,12 @@ export class DeleteChannel implements Command {
     channel.slug = `${channel.slug}-DELETED-${Date.now()}`;
     await transactionManager.save(channel);
 
-    const deletedChannel = await transactionManager.softDelete(Channel, channel.id);
-    await AuditChannels.setValue(channel.id, { event: 'Delete', user: this.authorizationBag.email });
-
-    return deletedChannel;
+    let result = await transactionManager.softDelete(Channel, channel.id);
+    if (result.affected === 1) {
+      await AuditChannels.setValue(channel.id, { event: 'Delete', user: this.authorizationBag.email });
+      return channel.id;
+    }
+    else
+      throw new ForbiddenError("The channel does not exist or you are not allowed to delete it.");
   }
 }
diff --git a/src/services/impl/channels/edit-channel-by-id.ts b/src/services/impl/channels/edit-channel-by-id.ts
index e422ac4d031ea0e48d65af49857561d1c51aaded..df99635746f233044fba784ea98ffbd93431bd31 100644
--- a/src/services/impl/channels/edit-channel-by-id.ts
+++ b/src/services/impl/channels/edit-channel-by-id.ts
@@ -1,32 +1,26 @@
-import { Command } from "../command";
-import { EntityManager } from "typeorm";
-import { Channel } from "../../../models/channel";
-import { ForbiddenError, NotFoundError } from "routing-controllers";
-import { AuthorizationBag } from "../../../models/authorization-bag";
+import { Command } from '../command';
+import { EntityManager } from 'typeorm';
+import { Channel } from '../../../models/channel';
+import { ForbiddenError, NotFoundError } from 'routing-controllers';
+import { AuthorizationBag } from '../../../models/authorization-bag';
+import { EditChannelResponse } from '../../../controllers/channels/dto';
 
 export class EditChannelById implements Command {
-  constructor(private channelId: string, private authorizationBag: AuthorizationBag) { }
+  constructor(private channelId: string, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager): Promise<any> {
-    let channel = await transactionManager.findOne(Channel, {
-      //relations: ["adminGroup"]
+  async execute(transactionManager: EntityManager): Promise<EditChannelResponse> {
+    const channel = await transactionManager.findOne(Channel, {
       // Specify needed joinColumns targets here, or use eager=true in model column def.
-      relations: ["members", "groups", "owner", "adminGroup", "category", "tags"],
+      relations: ['members', 'groups', 'owner', 'adminGroup', 'category', 'tags'], //needs owner and more otherwise isAdmin wont work
       where: {
         id: this.channelId,
       },
     });
 
-    if (!channel)
-      throw new NotFoundError("Channel does not exist");
+    if (!channel) throw new NotFoundError('Channel does not exist');
 
-    if (!(await channel.isAdmin(this.authorizationBag)))
-      throw new ForbiddenError("Access to Channel not Authorized !");
+    if (!(await channel.isAdmin(this.authorizationBag))) throw new ForbiddenError('Access to Channel not Authorized !');
 
-    return {
-      ...channel,
-      manage: true,
-      send: await channel.canSendByForm(this.authorizationBag),
-    };
+    return new EditChannelResponse(channel);
   }
 }
diff --git a/src/services/impl/channels/find-all-channels.ts b/src/services/impl/channels/find-all-channels.ts
index 61ff413f4c05c0d44798760665897dd9b2ec4e58..b7e2056358e938277ba4b440f859c67c25662896 100644
--- a/src/services/impl/channels/find-all-channels.ts
+++ b/src/services/impl/channels/find-all-channels.ts
@@ -5,14 +5,14 @@ import { AuthorizationBag } from '../../../models/authorization-bag';
 import { CernAuthorizationService } from '../../../models/cern-authorization-service';
 import { UserChannelCollection, UserChannelCollectionType } from '../../../models/user-channel-collection';
 import { UnauthorizedError } from 'routing-controllers';
+import { ChannelsListResponse, ListChannelsFilterObject, ChannelsQuery } from '../../../controllers/channels/dto';
 
 export class FindAllChannels implements Command {
-  constructor(private query, private authorizationBag: AuthorizationBag) {}
+  constructor(private query: ChannelsQuery, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager): Promise<any> {
-    if (!this.authorizationBag) {
+  async execute(transactionManager: EntityManager): Promise<ChannelsListResponse> {
+    if (!this.authorizationBag)
       throw new UnauthorizedError('Access unauthorized. Please authenticate or use /public/channels instead.');
-    }
 
     console.time('get-all-channels:total:' + this.authorizationBag.userName);
 
@@ -69,7 +69,7 @@ export class FindAllChannels implements Command {
     // Filter by member, group, admin, etc.
     qb.andWhere(
       new Brackets(channelsQB => {
-        if (this.query.ownerFilter === 'true') {
+        if (this.query.ownerFilter) {
           channelsQB.orWhere('owner.id = :userId', {
             userId: this.authorizationBag.userId,
           });
@@ -80,7 +80,7 @@ export class FindAllChannels implements Command {
 
           return;
         }
-        if (this.query.subscribedFilter === 'true') {
+        if (this.query.subscribedFilter) {
           channelsQB.orWhere(':userId IN (members.id)', {
             userId: this.authorizationBag.userId,
           });
@@ -117,7 +117,7 @@ export class FindAllChannels implements Command {
       }),
     );
 
-    if (this.query.subscribedFilter === 'true') {
+    if (this.query.subscribedFilter) {
       // exclude all unsubscribed channels
       qb.andWhere(sqb => {
         const subQuery = sqb
@@ -133,7 +133,7 @@ export class FindAllChannels implements Command {
       });
     }
 
-    if (this.query.favoritesFilter === 'true') {
+    if (this.query.favoritesFilter) {
       qb.andWhere(sqb => {
         const subQuery = sqb
           .subQuery()
@@ -172,8 +172,8 @@ export class FindAllChannels implements Command {
     qb.select(['channel.id', 'channel.name']) // select channel_id
       .orderBy('channel.name', 'ASC')
       .distinct()
-      .limit(parseInt(this.query.skip) + parseInt(this.query.take) || 10)
-      .offset(parseInt(this.query.skip) || 0);
+      .limit(this.query.skip + this.query.take || 10)
+      .offset(this.query.skip || 0);
 
     const channel_ids = await qb.getRawMany();
     console.timeEnd('get-all-channels:query:' + this.authorizationBag.userName);
@@ -200,12 +200,21 @@ export class FindAllChannels implements Command {
     console.timeEnd('get-all-channels:query-page:' + this.authorizationBag.userName);
 
     console.time('get-all-channels:build-return:' + this.authorizationBag.userName);
-    const returnChannels = await Promise.all(
+    const returnChannels: ListChannelsFilterObject[] = await Promise.all(
       channels.map(async channel => {
         // Clearing privacy items, used in relation above for permissions queries
         // But should not be returned to the frontend for display
         // at least don't return channel.owner, channel.adminGroup, channel.members, channel.groups
-        return {
+
+        return new ListChannelsFilterObject(
+          channel,
+          await channel.isSubscribedWithCache(this.authorizationBag.user, userGroups),
+          await channel.isAdminWithCache(this.authorizationBag, userGroups),
+          await channel.canSendByFormWithCache(this.authorizationBag, userGroups),
+        );
+
+        /*
+        let filteredChannel = {
           id: channel.id,
           slug: channel.slug,
           name: channel.name,
@@ -234,14 +243,12 @@ export class FindAllChannels implements Command {
           },
           sendPrivate: channel.sendPrivate,
         };
+        return ChannelResponse(filteredChannel);*/
       }),
     );
     console.timeEnd('get-all-channels:build-return:' + this.authorizationBag.userName);
 
     console.timeEnd('get-all-channels:total:' + this.authorizationBag.userName);
-    return {
-      channels: returnChannels,
-      count: count,
-    };
+    return new ChannelsListResponse(returnChannels);
   }
 }
diff --git a/src/services/impl/channels/find-channel-by-id.ts b/src/services/impl/channels/find-channel-by-id.ts
index f6c2bf7b8aa5ce4004568c55733b4fdc19bd328d..892a2e2fad4293a5f84c7aab8623be49db0edfc3 100644
--- a/src/services/impl/channels/find-channel-by-id.ts
+++ b/src/services/impl/channels/find-channel-by-id.ts
@@ -1,13 +1,14 @@
-import { Command } from '../command';
-import { EntityManager } from 'typeorm';
-import { Channel } from '../../../models/channel';
-import { ForbiddenError, NotFoundError } from 'routing-controllers';
-import { AuthorizationBag } from '../../../models/authorization-bag';
+import { Command } from "../command";
+import { EntityManager } from "typeorm";
+import { Channel } from "../../../models/channel";
+import { ForbiddenError, NotFoundError } from "routing-controllers";
+import { AuthorizationBag } from "../../../models/authorization-bag";
+import {ChannelResponse, GetChannelResponse} from "../../../controllers/channels/dto";
 
 export class FindChannelById implements Command {
   constructor(private channelId: string, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager): Promise<any> {
+  async execute(transactionManager: EntityManager): Promise<GetChannelResponse> {
     let channel = await transactionManager.findOne(Channel, {
       // Specify needed joinColumns targets here, or use eager=true in model column def.
       relations: ['members', 'groups', 'owner', 'adminGroup', 'category', 'tags'],
@@ -25,7 +26,7 @@ export class FindChannelById implements Command {
     // Clearing privacy items, used in relation above for permissions queries
     // But should not be returned to the frontend for display
     // at least don't return channel.owner, channel.adminGroup, channel.members, channel.groups
-    return {
+    let chan = new Channel({
       id: channel.id,
       slug: channel.slug,
       name: channel.name,
@@ -40,6 +41,8 @@ export class FindChannelById implements Command {
       tags: channel.tags,
       sendPrivate: channel.sendPrivate,
       channelFlags: channel.channelFlags,
-    }; // as Channel;
+    });
+
+    return new GetChannelResponse(chan);
   }
 }
diff --git a/src/services/impl/channels/find-public-channels.ts b/src/services/impl/channels/find-public-channels.ts
index e8c11235f6718e715143024803cc3bac4c27bda9..8145554bac6f045b4daaab35ebd3a12f013f22e0 100644
--- a/src/services/impl/channels/find-public-channels.ts
+++ b/src/services/impl/channels/find-public-channels.ts
@@ -2,11 +2,12 @@ import { Command } from '../command';
 import { EntityManager, Like } from 'typeorm';
 import { Channel } from '../../../models/channel';
 import { Visibility } from '../../../models/channel-enums';
+import { ChannelResponse, ChannelsListResponse } from "../../../controllers/channels/dto";
 
 export class FindPublicChannels implements Command {
   constructor(private query) {}
 
-  async execute(transactionManager: EntityManager): Promise<any> {
+  async execute(transactionManager: EntityManager): Promise<ChannelsListResponse> {
     const [channels, count] = await transactionManager.findAndCount(Channel, {
       relations: ['owner', 'category', 'tags'],
       where: [
@@ -29,10 +30,6 @@ export class FindPublicChannels implements Command {
         name: 'ASC',
       },
     });
-
-    return {
-      channels,
-      count,
-    };
+    return new ChannelsListResponse(channels.map(channel => new ChannelResponse(channel)));
   }
 }
diff --git a/src/services/impl/channels/generate-api-key.ts b/src/services/impl/channels/generate-api-key.ts
index 4b74ca3fde039c9f6b9cad0abe8e0513fe373aeb..c1910cfaac46568ddefdfa76e13099e4aab52c8e 100644
--- a/src/services/impl/channels/generate-api-key.ts
+++ b/src/services/impl/channels/generate-api-key.ts
@@ -4,11 +4,12 @@ import { Channel } from '../../../models/channel';
 import { ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
 import { AuditChannels } from '../../../log/auditing';
+import { ChannelResponse } from "../../../controllers/channels/dto";
 
 export class GenerateApiKey implements Command {
   constructor(private channelId: string, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager): Promise<String> {
+  async execute(transactionManager: EntityManager): Promise<string> {
     const channel = await transactionManager.findOne(Channel, {
       relations: ['owner', 'adminGroup'],
       where: {
diff --git a/src/services/impl/channels/get-channel-groups.ts b/src/services/impl/channels/get-channel-groups.ts
index a48faa07d043acfe2b2b268adf2625b984495c20..0b4a62b94094ac85a3312fc0eeeb14048e6d3160 100644
--- a/src/services/impl/channels/get-channel-groups.ts
+++ b/src/services/impl/channels/get-channel-groups.ts
@@ -3,15 +3,12 @@ import { Channel } from '../../../models/channel';
 import { EntityManager } from 'typeorm';
 import { ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
+import { GroupsListResponse, Query } from '../../../controllers/channels/dto';
 
 export class GetChannelGroups implements Command {
-  constructor(
-    private channelId: string,
-    private query,
-    private authorizationBag: AuthorizationBag,
-  ) {}
+  constructor(private channelId: string, private query: Query, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager): Promise<any> {
+  async execute(transactionManager: EntityManager): Promise<GroupsListResponse> {
     const channel = await transactionManager.findOne(Channel, {
       relations: ['owner', 'adminGroup', 'groups'],
       where: {
@@ -23,20 +20,13 @@ export class GetChannelGroups implements Command {
 
     // you need to be channel admin
     if (!(await channel.isAdmin(this.authorizationBag)))
-      throw new ForbiddenError(
-        "You don't have the rights to manage this channel.",
-      );
+      throw new ForbiddenError("You don't have the rights to manage this channel.");
 
-    let returnGroups = channel.groups.filter(g =>
-      g.groupIdentifier.includes(this.query.searchText),
-    );
+    const returnGroups = channel.groups.filter(g => g.groupIdentifier.includes(this.query.searchText || ''));
 
     return {
       totalNumberOfGroups: returnGroups.length,
-      groups: returnGroups.splice(
-        parseInt(this.query.skip) || 0,
-        parseInt(this.query.take) || 10,
-      ),
+      groups: returnGroups.splice(this.query.skip || 0, this.query.take || 10),
     };
   }
 }
diff --git a/src/services/impl/channels/get-channel-members.ts b/src/services/impl/channels/get-channel-members.ts
index bfbeed2f3b37934c63fba0ab9f3577807b5b2f38..06bd14390853e936744a7dfde6acb090b20e8dcc 100644
--- a/src/services/impl/channels/get-channel-members.ts
+++ b/src/services/impl/channels/get-channel-members.ts
@@ -3,15 +3,12 @@ import { Channel } from '../../../models/channel';
 import { EntityManager } from 'typeorm';
 import { ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
+import { MembersListResponse, Query } from '../../../controllers/channels/dto';
 
 export class GetChannelMembers implements Command {
-  constructor(
-    private channelId: string,
-    private query,
-    private authorizationBag: AuthorizationBag,
-  ) {}
+  constructor(private channelId: string, private query: Query, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager): Promise<any> {
+  async execute(transactionManager: EntityManager): Promise<MembersListResponse> {
     const channel = await transactionManager.findOne(Channel, {
       relations: ['owner', 'adminGroup', 'members'],
       where: {
@@ -23,22 +20,17 @@ export class GetChannelMembers implements Command {
 
     // you need to be channel admin
     if (!(await channel.isAdmin(this.authorizationBag)))
-      throw new ForbiddenError(
-        "You don't have the rights to manage this channel.",
-      );
+      throw new ForbiddenError("You don't have the rights to manage this channel.");
 
-    let returnMembers = channel.members.filter(
+    const returnMembers = channel.members.filter(
       m =>
-        (m.username && m.username.includes(this.query.searchText)) ||
-        m.email.includes(this.query.searchText),
+        (m.username && m.username.includes(this.query.searchText || '')) ||
+        m.email.includes(this.query.searchText || ''),
     );
 
     return {
       totalNumberOfMembers: returnMembers.length,
-      members: returnMembers.splice(
-        parseInt(this.query.skip) || 0,
-        parseInt(this.query.take) || 10,
-      ),
+      members: returnMembers.splice(this.query.skip || 0, this.query.take || 10),
     };
   }
 }
diff --git a/src/services/impl/channels/get-channel-stats.ts b/src/services/impl/channels/get-channel-stats.ts
index 900e2e6492c9d9a03ff3241c62a73b35bfc2b412..a145a12e668b7ff5119de7944b4608a102386e74 100644
--- a/src/services/impl/channels/get-channel-stats.ts
+++ b/src/services/impl/channels/get-channel-stats.ts
@@ -3,15 +3,13 @@ import { EntityManager } from 'typeorm';
 import { Channel } from '../../../models/channel';
 import { ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
+import { ChannelStatsResponse } from '../../../controllers/channels/dto';
 
 export class GetChannelStats implements Command {
-  constructor(
-    private channelId: string,
-    private authorizationBag: AuthorizationBag,
-  ) {}
+  constructor(private channelId: string, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager): Promise<any> {
-    let channel = await transactionManager.findOne(Channel, {
+  async execute(transactionManager: EntityManager): Promise<ChannelStatsResponse> {
+    const channel = await transactionManager.findOne(Channel, {
       // Specify needed joinColumns targets here, or use eager=true in model column def.
       relations: ['members', 'groups', 'owner', 'unsubscribed'],
       where: {
@@ -24,15 +22,6 @@ export class GetChannelStats implements Command {
     if (!(await channel.hasAccess(this.authorizationBag)))
       throw new ForbiddenError('Access to Channel not Authorized !');
 
-    return {
-      id: channel.id,
-      name: channel.name,
-      members: channel.members.length,
-      unsubscribed: channel.unsubscribed.length,
-      owner: channel.owner,
-      creationDate: channel.creationDate,
-      lastActivityDate: channel.lastActivityDate,
-      groups: channel.groups.length,
-    }; // as Channel;
+    return new ChannelStatsResponse(channel);
   }
 }
diff --git a/src/services/impl/channels/remove-group-from-channel.ts b/src/services/impl/channels/remove-group-from-channel.ts
index 9149680f19d1b27fdfef5b5c43837e8d2297b886..9866cb78d7e90b13e8de8cd73ae20f9a14f13efa 100644
--- a/src/services/impl/channels/remove-group-from-channel.ts
+++ b/src/services/impl/channels/remove-group-from-channel.ts
@@ -2,14 +2,20 @@ import { Command } from '../command';
 import { EntityManager } from 'typeorm';
 import { Channel } from '../../../models/channel';
 import { Group } from '../../../models/group';
-import { ForbiddenError, NotFoundError } from 'routing-controllers';
+import { BadRequestError, ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
 import { AuditChannels } from '../../../log/auditing';
+import { isUUID } from 'class-validator';
 
 export class RemoveGroupFromChannel implements Command {
   constructor(private groupId: string, private channelId: string, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager) {
+  async execute(transactionManager: EntityManager): Promise<string> {
+    if (!isUUID(this.channelId))
+      throw new BadRequestError('Provided channel id: "' + this.channelId + '" is not a UUID.');
+
+    if (!isUUID(this.groupId)) throw new BadRequestError('Provided group id: "' + this.groupId + '" is not a UUID.');
+
     const channel = await transactionManager.findOne(Channel, {
       relations: ['owner', 'adminGroup', 'groups'],
       where: {
@@ -32,12 +38,14 @@ export class RemoveGroupFromChannel implements Command {
     channel.removeGroup(group);
 
     const updatedChannel = await transactionManager.save(channel);
+    if (!updatedChannel) throw new Error('Was not able to update the channel.');
+
     await AuditChannels.setValue(updatedChannel.id, {
       event: 'RemoveGroup',
       user: this.authorizationBag.email,
       groupId: this.groupId,
     });
 
-    return updatedChannel;
+    return this.groupId;
   }
 }
diff --git a/src/services/impl/channels/remove-user-from-channel.ts b/src/services/impl/channels/remove-user-from-channel.ts
index 6c8c12cf33ca8aef60de99f7dcaaf4cca7744485..ae9bbe7ecbeed43ce7d2e94c13e1194ca9542241 100644
--- a/src/services/impl/channels/remove-user-from-channel.ts
+++ b/src/services/impl/channels/remove-user-from-channel.ts
@@ -5,11 +5,12 @@ import { User } from '../../../models/user';
 import { ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
 import { AuditChannels } from '../../../log/auditing';
+import { ChannelResponse } from "../../../controllers/channels/dto";
 
 export class RemoveUserFromChannel implements Command {
   constructor(private memberId: string, private channelId: string, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager) {
+  async execute(transactionManager: EntityManager): Promise<String> {
     const channel = await transactionManager.findOne(
       Channel,
       { id: this.channelId },
@@ -39,6 +40,6 @@ export class RemoveUserFromChannel implements Command {
       memberId: this.memberId,
     });
 
-    return channel;
+    return user.id;
   }
 }
diff --git a/src/services/impl/channels/set-category.ts b/src/services/impl/channels/set-category.ts
index 912e2ef0d2ebba86912b930024316bc156feeb3c..24119257003fbcff79c12d82fed1e2176efbeaac 100644
--- a/src/services/impl/channels/set-category.ts
+++ b/src/services/impl/channels/set-category.ts
@@ -5,11 +5,12 @@ import { Category } from '../../../models/category';
 import { ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
 import { AuditChannels } from '../../../log/auditing';
+import { ChannelResponse } from "../../../controllers/channels/dto";
 
 export class SetCategory implements Command {
   constructor(private channelId: string, private category: Category, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager): Promise<Channel> {
+  async execute(transactionManager: EntityManager): Promise<ChannelResponse> {
     let channel = await transactionManager.findOne(Channel, {
       relations: ['owner', 'adminGroup', 'category'],
       where: {
@@ -40,6 +41,6 @@ export class SetCategory implements Command {
       category: this.category,
     });
 
-    return channel;
+    return new ChannelResponse(channel);
   }
 }
diff --git a/src/services/impl/channels/set-tags.ts b/src/services/impl/channels/set-tags.ts
index a07c8ac1060c31b53114e7ded798ddd4639e3995..e9e34b72c3a146b471c30d9744dbd9ca150be433 100644
--- a/src/services/impl/channels/set-tags.ts
+++ b/src/services/impl/channels/set-tags.ts
@@ -2,16 +2,20 @@ import { Command } from '../command';
 import { EntityManager, In } from 'typeorm';
 import { Channel } from '../../../models/channel';
 import { Tag } from '../../../models/tag';
-import { ForbiddenError, NotFoundError } from 'routing-controllers';
+import { BadRequestError, ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
-import { isUUID } from 'class-validator';
 import { AuditChannels } from '../../../log/auditing';
+import { ChannelResponse, setTagsRequest } from '../../../controllers/channels/dto';
+import { isUUID } from 'class-validator';
 
 export class SetTags implements Command {
-  constructor(private channelId: string, private tags: Tag[], private authorizationBag: AuthorizationBag) {}
+  constructor(private channelId: string, private tags: setTagsRequest, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager): Promise<Channel> {
-    let channel = await transactionManager.findOne(Channel, {
+  async execute(transactionManager: EntityManager): Promise<ChannelResponse> {
+    if (!isUUID(this.channelId))
+      throw new BadRequestError('Provided channel id: "' + this.channelId + '" is not a UUID.');
+
+    const channel = await transactionManager.findOne(Channel, {
       relations: ['owner', 'adminGroup', 'tags'],
       where: {
         id: this.channelId,
@@ -24,20 +28,15 @@ export class SetTags implements Command {
     if (!(await channel.isAdmin(this.authorizationBag)))
       throw new ForbiddenError("You don't have the rights to edit this channel.");
 
-    if (this.tags) {
-      const tagCleaned = this.tags.map(data => {
-        const tagid = data.id || data;
-        if (isUUID(tagid as string)) return tagid;
-      });
-
-      channel.tags = await transactionManager.find(Tag, {
-        where: { id: In(tagCleaned) },
-      });
-    } else channel.tags = null;
+    this.tags
+      ? (channel.tags = await transactionManager.find(Tag, {
+          where: { id: In(this.tags.tagIds) },
+        }))
+      : (channel.tags = null);
 
     await transactionManager.save(channel);
     await AuditChannels.setValue(channel.id, { event: 'AddTags', user: this.authorizationBag.email, tags: this.tags });
 
-    return channel;
+    return new ChannelResponse(channel);
   }
 }
diff --git a/src/services/impl/channels/subscribe-to-channel.ts b/src/services/impl/channels/subscribe-to-channel.ts
index e4ff134e6976e43ed66d05ea4947b3fd91e7efe6..ba14e453c082cbd9406ddb25460edf8d37a27ad8 100644
--- a/src/services/impl/channels/subscribe-to-channel.ts
+++ b/src/services/impl/channels/subscribe-to-channel.ts
@@ -5,11 +5,12 @@ import { Channel } from '../../../models/channel';
 import { ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
 import { AuditChannels } from '../../../log/auditing';
+import { ChannelResponse } from "../../../controllers/channels/dto";
 
 export class SubscribeToChannel implements Command {
   constructor(private memberId: string, private channelId: string, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager) {
+  async execute(transactionManager: EntityManager): Promise<ChannelResponse> {
     const channel = await transactionManager.findOne(
       Channel,
       { id: this.channelId },
@@ -37,6 +38,6 @@ export class SubscribeToChannel implements Command {
       memberId: this.memberId,
     });
 
-    return updatedChannel;
+    return new ChannelResponse(updatedChannel);
   }
 }
diff --git a/src/services/impl/channels/unsubscribe-from-channel.ts b/src/services/impl/channels/unsubscribe-from-channel.ts
index 3a01eb80488afbb60daf83f92240181e33f2ffc5..61f14b7ebe7670513980dd080d0c3e0c7fa3e1fa 100644
--- a/src/services/impl/channels/unsubscribe-from-channel.ts
+++ b/src/services/impl/channels/unsubscribe-from-channel.ts
@@ -6,11 +6,12 @@ import { ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
 import { AuditChannels } from '../../../log/auditing';
 import { ChannelFlags } from '../../../models/channel-enums';
+import { ChannelResponse } from "../../../controllers/channels/dto";
 
 export class UnsubscribeFromChannel implements Command {
   constructor(private memberId: string, private channelId: string, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager) {
+  async execute(transactionManager: EntityManager): Promise<ChannelResponse>{
     const channel = await transactionManager.findOne(
       Channel,
       { id: this.channelId },
@@ -43,6 +44,6 @@ export class UnsubscribeFromChannel implements Command {
       memberId: this.memberId,
     });
 
-    return updatedChannel;
+    return new ChannelResponse(updatedChannel);
   }
 }
diff --git a/src/services/impl/channels/update-channel-admin-group.ts b/src/services/impl/channels/update-channel-admin-group.ts
index da76b014005ec349df72d738bb587ac399c86ec7..64904672a0c81c7d80b9946deaeadd7fb008e111 100644
--- a/src/services/impl/channels/update-channel-admin-group.ts
+++ b/src/services/impl/channels/update-channel-admin-group.ts
@@ -1,21 +1,25 @@
-import { Command } from '../command';
-import { EntityManager } from 'typeorm';
-import { Channel } from '../../../models/channel';
-import { Group } from '../../../models/group';
-import { ForbiddenError, NotFoundError } from 'routing-controllers';
-import { AuthorizationBag } from '../../../models/authorization-bag';
-import { AuditChannels } from '../../../log/auditing';
+import { Command } from "../command";
+import { EntityManager } from "typeorm";
+import { Channel } from "../../../models/channel";
+import { Group } from "../../../models/group";
+import { ForbiddenError, NotFoundError } from "routing-controllers";
+import { AuthorizationBag } from "../../../models/authorization-bag";
+import { ChannelResponse } from "../../../controllers/channels/dto";
+import { AuditChannels } from "../../../log/auditing";
 
 export class UpdateChannelAdminGroup implements Command {
-  constructor(private group: Group, private channelId: string, private authorizationBag: AuthorizationBag) {}
-
-  async execute(transactionManager: EntityManager): Promise<Channel> {
-    const channel = await transactionManager.findOne(Channel, {
-      relations: ['owner', 'adminGroup'],
-      where: {
-        id: this.channelId,
-      },
-    });
+  constructor(private groupName: string, private channelId: string, private authorizationBag: AuthorizationBag) { }
+
+  async execute(transactionManager: EntityManager): Promise<ChannelResponse> {
+    const channel = await transactionManager.findOne(
+      Channel,
+      {
+        relations: ["owner", "adminGroup"],
+        where: {
+          id: this.channelId,
+        },
+      }
+    );
 
     if (!channel) throw new NotFoundError('Channel does not exist');
 
@@ -23,12 +27,14 @@ export class UpdateChannelAdminGroup implements Command {
     if (!(await channel.isAdmin(this.authorizationBag)))
       throw new ForbiddenError("You don't have the rights to manage this channel.");
 
-    if (this.group.groupIdentifier) {
+    let newGroup = new Group({groupIdentifier: this.groupName});
+
+    if (!(this.groupName === "" || this.groupName === null)) {
       let groupToAdd = await transactionManager.findOne(Group, {
-        groupIdentifier: this.group.groupIdentifier,
+        groupIdentifier: this.groupName,
       });
-      if (!groupToAdd && (await this.group.exists())) {
-        groupToAdd = this.group;
+      if (!groupToAdd && (await newGroup.exists())) {
+        groupToAdd = newGroup;
         groupToAdd = await transactionManager.save(groupToAdd);
       }
       if (!groupToAdd) throw new NotFoundError('The group does not exist');
@@ -45,9 +51,9 @@ export class UpdateChannelAdminGroup implements Command {
     await AuditChannels.setValue(updatedChannel.id, {
       event: 'SetAdminGroup',
       user: this.authorizationBag.email,
-      groupIdentifier: this.group.groupIdentifier,
+      groupIdentifier: this.groupName,
     });
 
-    return updatedChannel;
+    return new ChannelResponse(updatedChannel);
   }
 }
diff --git a/src/services/impl/channels/update-channel.ts b/src/services/impl/channels/update-channel.ts
index 721c811f935bd3ba31a4a27b3ceb41c19d567343..a2b65c6347f5a40984f85d3c322a8715862540c2 100644
--- a/src/services/impl/channels/update-channel.ts
+++ b/src/services/impl/channels/update-channel.ts
@@ -1,27 +1,30 @@
 import { Command } from '../command';
-import { EntityManager, Not, In } from 'typeorm';
-import { isUUID, validate } from 'class-validator';
+import { EntityManager, Not } from 'typeorm';
 import { Channel } from '../../../models/channel';
-import { Tag } from '../../../models/tag';
-import { Visibility, SubscriptionPolicy } from '../../../models/channel-enums';
 import { BadRequestError, ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
 import { prepareValidationErrorList } from './validation-utils';
 import { AuditChannels } from '../../../log/auditing';
+import { UpdateChannelRequest, ChannelResponse } from '../../../controllers/channels/dto';
+import { validate } from 'class-validator';
 
 export class UpdateChannel implements Command {
-  constructor(private channel: Channel, private authorizationBag: AuthorizationBag) {
-    if (channel.visibility === Visibility.restricted) channel.subscriptionPolicy === SubscriptionPolicy.dynamic;
-  }
+  constructor(private channel: UpdateChannelRequest, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager): Promise<Channel> {
-    let channel = await transactionManager.findOne(Channel, {
+  async execute(transactionManager: EntityManager): Promise<ChannelResponse> {
+    const channel = await transactionManager.findOne(Channel, {
       relations: ['adminGroup', 'groups', 'owner'],
       where: {
         id: this.channel.id,
       },
     });
 
+    validate(channel).then(errors => {
+      console.log(errors);
+    });
+
+    if (!channel) throw new NotFoundError('The channel does not exist.');
+
     this.channel.name = this.channel.name.trim();
 
     if (this.channel.name.length < 4 || this.channel.name.length > 128) {
@@ -31,8 +34,6 @@ export class UpdateChannel implements Command {
       throw new BadRequestError("Channel's description should be less than 256 characters.");
     }
 
-    if (!channel) throw new NotFoundError('The channel does not exist.');
-
     if (
       await transactionManager.findOne(
         Channel,
@@ -47,20 +48,6 @@ export class UpdateChannel implements Command {
     ) {
       throw new BadRequestError('Channel name already exists');
     }
-    if (
-      await transactionManager.findOne(
-        Channel,
-        {
-          id: Not(channel.id),
-          slug: this.channel.slug,
-        },
-        {
-          withDeleted: true,
-        },
-      )
-    ) {
-      throw new BadRequestError('Channel slug already exists');
-    }
 
     // you need to be channel admin
     if (!(await channel.isAdmin(this.authorizationBag)))
@@ -68,10 +55,10 @@ export class UpdateChannel implements Command {
 
     // TODO Loop should one day keep only updated properties
     // but it impacts the returned object. To link with DTOs
-    for (let key in this.channel) {
+    for (const key in this.channel) {
       if (key != 'tags') channel[key] = this.channel[key];
     }
-
+    /*
     if (this.channel.tags) {
       const tagCleaned = this.channel.tags.map(data => {
         const tagid = data.id || data;
@@ -83,7 +70,7 @@ export class UpdateChannel implements Command {
           id: In(tagCleaned),
         },
       });
-    }
+    }*/
 
     if (!this.authorizationBag.isSupporter) channel.channelFlags = undefined;
 
@@ -94,6 +81,6 @@ export class UpdateChannel implements Command {
     const updatedChannel = await transactionManager.save(channel);
     await AuditChannels.setValue(updatedChannel.id, { event: 'Update', user: this.authorizationBag.email });
 
-    return updatedChannel;
+    return new ChannelResponse(updatedChannel);
   }
 }
diff --git a/src/services/impl/notifications-service-impl.ts b/src/services/impl/notifications-service-impl.ts
index f172f8031a6eea5bba001d24cf4a6d1deb3b0f05..9fa1c0a6f50b30cf927231c32dd54a816bdb7b5d 100644
--- a/src/services/impl/notifications-service-impl.ts
+++ b/src/services/impl/notifications-service-impl.ts
@@ -1,5 +1,4 @@
 import { NotificationsService } from '../notifications-service';
-import { Notification } from '../../models/notification';
 import { SendNotification } from './notifications/send-notification';
 import { FindAllNotifications } from './notifications/find-all-notifications';
 import { GetById } from './notifications/get-by-id';
@@ -9,6 +8,7 @@ import { UpdateUserNotification } from './notifications/update-user-notification
 import { AuthorizationBag } from '../../models/authorization-bag';
 import { RetryNotification } from './notifications/retry-notification';
 import { GetNotificationResponse, SendNotificationRequest } from '../../controllers/notifications/dto';
+import { NotificationsListResponse, Query } from '../../controllers/channels/dto';
 
 export class NotificationsServiceImpl extends AbstractService implements NotificationsService {
   sendNotification(
@@ -22,7 +22,11 @@ export class NotificationsServiceImpl extends AbstractService implements Notific
     return this.commandExecutor.execute(new RetryNotification(notificationId, authorizationBag));
   }
 
-  findAllNotifications(channelId: string, query: any, authorizationBag: AuthorizationBag): Promise<Notification[]> {
+  findAllNotifications(
+    channelId: string,
+    query: Query,
+    authorizationBag: AuthorizationBag,
+  ): Promise<NotificationsListResponse> {
     return this.commandExecutor.execute(new FindAllNotifications(channelId, query, authorizationBag));
   }
 
diff --git a/src/services/impl/notifications/find-all-notifications.ts b/src/services/impl/notifications/find-all-notifications.ts
index 39aeeb243ac7fca43c8c916e94810cb2728afad1..64ba0d6dcea9aab09168f386ff53b111a613eab5 100644
--- a/src/services/impl/notifications/find-all-notifications.ts
+++ b/src/services/impl/notifications/find-all-notifications.ts
@@ -5,11 +5,12 @@ import { Channel } from '../../../models/channel';
 import { ForbiddenError, NotFoundError } from 'routing-controllers';
 import { AuthorizationBag } from '../../../models/authorization-bag';
 import { CernAuthorizationService } from '../../../models/cern-authorization-service';
+import { NotificationsListResponse, Query } from '../../../controllers/channels/dto';
 
 export class FindAllNotifications implements Command {
-  constructor(private channelId: string, private query: any, private authorizationBag: AuthorizationBag) {}
+  constructor(private channelId: string, private query: Query, private authorizationBag: AuthorizationBag) {}
 
-  async execute(transactionManager: EntityManager) {
+  async execute(transactionManager: EntityManager): Promise<NotificationsListResponse> {
     if (this.authorizationBag) console.time('get-all-notifications:total:' + this.authorizationBag.userName);
 
     if (this.authorizationBag) console.time('get-all-notifications:grappa:' + this.authorizationBag.userName);
@@ -114,8 +115,8 @@ export class FindAllNotifications implements Command {
       .orderBy('notification.sentAt', 'DESC', 'NULLS FIRST')
       .addOrderBy('notification.sendAt', 'ASC')
       .distinct()
-      .limit(parseInt(this.query.skip) + parseInt(this.query.take) || 10)
-      .offset(parseInt(this.query.skip) || 0);
+      .limit(this.query.skip + this.query.take || 10)
+      .offset(this.query.skip || 0);
 
     const notifications_ids = await notificationQB.getRawMany();
 
diff --git a/src/services/notifications-service.ts b/src/services/notifications-service.ts
index 46cce9eda6bb9960c0d04460822a904b33b150f8..f1a58880496e970d175d3858365d72e2ba1036ee 100644
--- a/src/services/notifications-service.ts
+++ b/src/services/notifications-service.ts
@@ -1,7 +1,7 @@
-import { Notification } from '../models/notification';
 import { UserNotification } from '../models/user-notification';
 import { AuthorizationBag } from '../models/authorization-bag';
 import { GetNotificationResponse, SendNotificationRequest } from '../controllers/notifications/dto';
+import { NotificationsListResponse, Query } from '../controllers/channels/dto';
 
 export interface NotificationsService {
   sendNotification(
@@ -11,7 +11,11 @@ export interface NotificationsService {
 
   retryNotification(notificationId: string, authorizationBag: AuthorizationBag): Promise<void>;
 
-  findAllNotifications(channelId: string, query: any, authorizationBag: AuthorizationBag): Promise<Notification[]>;
+  findAllNotifications(
+    channelId: string,
+    query: Query,
+    authorizationBag: AuthorizationBag,
+  ): Promise<NotificationsListResponse>;
 
   getById(notificationId: string, authorizationBag: AuthorizationBag): Promise<GetNotificationResponse>;
 
diff --git a/src/utils/status-codes.ts b/src/utils/status-codes.ts
index f5648507adaa92b5560a8e05c6510e206451b100..1c3103d862f4078b4750bef9df808854767e9053 100644
--- a/src/utils/status-codes.ts
+++ b/src/utils/status-codes.ts
@@ -57,7 +57,7 @@ export const enum StatusCodes {
   MisdirectedRequest = '421',
 }
 
-export const StatusCodeDescriptions: Record<string, object> = {
+export const StatusCodeDescriptions: Record<string, unknown> = {
   [StatusCodes.Accepted]: { description: 'Accepted' },
   [StatusCodes.BadGateway]: { description: 'Bad Gateway' },
   [StatusCodes.BadRequest]: { description: 'Bad Request' },