diff --git a/.gitignore b/.gitignore
index a60b41b70d05fc80340cc59824669e40ab84e290..58b0057200d389ee28a40be1e1dd97c346ee003c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -389,3 +389,7 @@ Temporary Items
 # Local
 .env
 dist
+
+# Migrations
+.migrate
+src/migrations/data/
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 79869be065068220ec5a1da241c04e416590fc95..bb71762235a1187989c66f732efe2aa222b128a2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -35,12 +35,17 @@ deploy_dev:
     - docker-compose pull service-ram
     - docker-compose up -d --force-recreate service-ram
     - docker system prune -a -f
+  environment:
+    name: dev
+    url: https://resin-dev.grandlyon.com
 
 test:
   stage: test
   image: node:14.15.4
   before_script:
     - export GHOST_HOST_AND_PORT=http://localhost:2368
+    - export GHOST_ADMIN_API_KEY=60142bc9e33940000156bccc:6217742e2671e322612e89cac9bab61fcd01822709fe5d8f5e6a5b3e54d5e6bb
+    - export SALT=$TEST_SALT
   script:
     - npm i
     - npm run test:cov
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5c9270be77d32993eeba2f1bd9ca3d2aaecc4f57..a59f202c1f017b324006555b280e9291176e900f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,28 @@
 
 All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
 
+## [1.7.0](https://forge.grandlyon.com///compare/v1.6.1...v1.7.0) (2021-04-12)
+
+
+### Features
+
+* add aptic service offer query ([7602b41](https://forge.grandlyon.com///commit/7602b412f08047cd8dea2cb99af2e47b83d08c08))
+* add endpoint for sending error report for structure ([bc64158](https://forge.grandlyon.com///commit/bc64158c06f6c1ecad7753dbac62a19a81f84a42))
+* add newsletter data insert script ([a1a743d](https://forge.grandlyon.com///commit/a1a743d5c3391c2c54ec095a6cf12e4f2aeaae58))
+* add newsletter subscription ([c507f53](https://forge.grandlyon.com///commit/c507f53646c9f10d40ebe1d6ea518b7aeea27876))
+* edit request for structure service (not working) ([a156819](https://forge.grandlyon.com///commit/a1568197036f8a87d902b2563fc35430a79d13c3))
+* TU for admin mailer and posts ([1b1724e](https://forge.grandlyon.com///commit/1b1724efcca24c2e5706e122f13f6740bb9b4ee9))
+* zoom on town ([7eec8f1](https://forge.grandlyon.com///commit/7eec8f160286746c713b451d7d8576bc50314c9b))
+
+
+### Bug Fixes
+
+* migration script path ([0b549b8](https://forge.grandlyon.com///commit/0b549b86d961d25f95d3977cab2c2f86f5b0e6b1))
+* typo in .gitignore ([ede567e](https://forge.grandlyon.com///commit/ede567e7fdd4542f5c5ccced51b3a627434bfd98))
+* **structures:** prevent access to deleted structures ([ea0bf7b](https://forge.grandlyon.com///commit/ea0bf7bf698486cda875113852d6cc5c957f1f55))
+* update search filter query ([44bb26e](https://forge.grandlyon.com///commit/44bb26ea836d95c3c5bc5437553eb633cf0092f1))
+* **mail:** add link to structure in admin new structure mail ([b067483](https://forge.grandlyon.com///commit/b067483a91fb97ec7506f17a4999cd4fd65614be))
+
 ### [1.6.1](https://forge.grandlyon.com///compare/v1.6.0...v1.6.1) (2021-04-01)
 
 
diff --git a/package-lock.json b/package-lock.json
index fe3585896802626aeef6f70056c64594d82e9e7d..d787d4c561026f1861332126e9218bc649e0b94c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "ram_server",
-  "version": "1.6.1",
+  "version": "1.7.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -9731,6 +9731,33 @@
         }
       }
     },
+    "migrate": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/migrate/-/migrate-1.7.0.tgz",
+      "integrity": "sha512-I63YykITgWyI+ET4KO8xGePYkR9U7CtSe/RrR13vLbZSpUcAh4/ry2GswNv7Lywcsp3BaDHj7YdjC7ihVYCFmw==",
+      "requires": {
+        "chalk": "^2.4.1",
+        "commander": "^2.19.0",
+        "dateformat": "^3.0.3",
+        "dotenv": "^6.1.0",
+        "inherits": "^2.0.3",
+        "minimatch": "^3.0.4",
+        "mkdirp": "^0.5.1",
+        "slug": "^0.9.2"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.20.3",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+          "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+        },
+        "dotenv": {
+          "version": "6.2.0",
+          "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz",
+          "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w=="
+        }
+      }
+    },
     "miller-rabin": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
@@ -11975,6 +12002,14 @@
       "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
       "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E="
     },
+    "slug": {
+      "version": "0.9.4",
+      "resolved": "https://registry.npmjs.org/slug/-/slug-0.9.4.tgz",
+      "integrity": "sha512-3YHq0TeJ4+AIFbJm+4UWSQs5A1mmeWOTQqydW3OoPmQfNKxlO96NDRTIrp+TBkmvEsEFrd+Z/LXw8OD/6OlZ5g==",
+      "requires": {
+        "unicode": ">= 0.3.1"
+      }
+    },
     "snapdragon": {
       "version": "0.8.2",
       "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -13260,6 +13295,11 @@
         "debug": "^2.2.0"
       }
     },
+    "unicode": {
+      "version": "13.0.0",
+      "resolved": "https://registry.npmjs.org/unicode/-/unicode-13.0.0.tgz",
+      "integrity": "sha512-osNPLT4Lqna/sV6DQikrB8m4WxR61/k0fnhfKnkPGcZImczW3IysRXvWxfdqGUjh0Ju2o/tGGgu46mlfc/cpZw=="
+    },
     "union-value": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
diff --git a/package.json b/package.json
index c241c3f401eecdef10bc1888cd8b9331afa67f8a..79114f2ed061d6b141ac421bafe63a3df12d5d55 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "ram_server",
   "private": true,
-  "version": "1.6.1",
+  "version": "1.7.0",
   "description": "Nest TypeScript starter repository",
   "license": "MIT",
   "scripts": {
@@ -19,7 +19,10 @@
     "test:watch": "jest --watch",
     "test:cov": "jest --config ./test/jest.json --coverage --ci --reporters=default --reporters=jest-junit",
     "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
-    "test:e2e": "jest --config ./test/jest-e2e.json"
+    "test:e2e": "jest --config ./test/jest-e2e.json",
+    "migrate:create": "migrate create --template-file ./src/migrations/migrations-utils/template.ts --migrations-dir=\"./src/migrations/scripts\" --compiler=\"ts:./src/migrations/migrations-utils/ts-compiler.js\"",
+    "migrate:up": "migrate --migrations-dir=\"./src/migrations/scripts\" --compiler=\"ts:./src/migrations/migrations-utils/ts-compiler.js\" up",
+    "migrate:down": "migrate --migrations-dir=\"./src/migrations/scripts\" --compiler=\"ts:./src/migrations/migrations-utils/ts-compiler.js\" down"
   },
   "dependencies": {
     "@nestjs/common": "^7.6.13",
@@ -39,6 +42,7 @@
     "ejs": "^3.1.5",
     "form-data": "^3.0.0",
     "luxon": "^1.25.0",
+    "migrate": "^1.7.0",
     "mongoose": "^5.10.15",
     "passport": "^0.4.1",
     "passport-jwt": "^4.0.0",
diff --git a/src/admin/admin.controller.spec.ts b/src/admin/admin.controller.spec.ts
index 65e9b88e4fb7b56a04dfa670134a6d6a69475f55..84dbf424a36ea0a54a4e59cf28ff657f8d546082 100644
--- a/src/admin/admin.controller.spec.ts
+++ b/src/admin/admin.controller.spec.ts
@@ -1,13 +1,16 @@
-import { HttpModule } from '@nestjs/common';
+import { HttpException, HttpModule, HttpStatus } from '@nestjs/common';
 import { getModelToken } from '@nestjs/mongoose';
 import { Test, TestingModule } from '@nestjs/testing';
 import { ConfigurationModule } from '../configuration/configuration.module';
 import { MailerService } from '../mailer/mailer.service';
+import { NewsletterSubscription } from '../newsletter/newsletter-subscription.schema';
+import { NewsletterService } from '../newsletter/newsletter.service';
 import { Structure } from '../structures/schemas/structure.schema';
 import { StructuresService } from '../structures/services/structures.service';
 import { User } from '../users/schemas/user.schema';
 import { UsersService } from '../users/users.service';
 import { AdminController } from './admin.controller';
+import { PendingStructureDto } from './dto/pending-structure.dto';
 
 describe('AdminController', () => {
   let controller: AdminController;
@@ -18,11 +21,16 @@ describe('AdminController', () => {
       providers: [
         UsersService,
         StructuresService,
+        NewsletterService,
         MailerService,
         {
           provide: getModelToken('User'),
           useValue: User,
         },
+        {
+          provide: getModelToken('NewsletterSubscription'),
+          useValue: NewsletterSubscription,
+        },
         {
           provide: getModelToken('Structure'),
           useValue: Structure,
@@ -37,4 +45,24 @@ describe('AdminController', () => {
   it('should be defined', () => {
     expect(controller).toBeDefined();
   });
+
+  it('should get pending attachments', async () => {
+    const result = [{name: "MJC Route de vienne", address: "14 chemin des platanes"}, {name: "Mairie Lyon 7eme", address: "21 boulevard martin"}];
+    jest.spyOn(controller, 'getPendingAttachments').mockImplementation(async (): Promise<any> => result);
+    expect(await controller.getPendingAttachments()).toBe(result);
+  });
+
+  it('should validate pending structure', async () => {
+    const result = [{name: "MJC Route de vienne", address: "14 chemin des platanes"}];
+    const structure: PendingStructureDto = {userEmail:"martin@mjc.fr", structureId: "1", structureName:"MJC Route de vienne"};
+    jest.spyOn(controller, 'validatePendingStructure').mockImplementation(async (): Promise<any> => result);
+    expect(await controller.validatePendingStructure(structure)).toBe(result);
+  });
+
+  it('should refuse pending structure', async () => {
+    const result = [{name: "MJC Route de vienne", address: "14 chemin des platanes"}, {name: "Mairie Lyon 7eme", address: "21 boulevard martin"}];
+    const structure: PendingStructureDto = {userEmail:"martin@mjc.fr", structureId: "1", structureName:"MJC Route de vienne"};
+    jest.spyOn(controller, 'refusePendingStructure').mockImplementation(async (): Promise<any> => result);
+    expect(await controller.refusePendingStructure(structure)).toBe(result);
+  });
 });
diff --git a/src/admin/admin.controller.ts b/src/admin/admin.controller.ts
index 3947dd029646e816d889b6445a0e142aa4b17619..03442f8e55e2210e3bf1fc52f96e0c4617ff3add 100644
--- a/src/admin/admin.controller.ts
+++ b/src/admin/admin.controller.ts
@@ -2,6 +2,7 @@ import { Body, Delete, Param } from '@nestjs/common';
 import { Controller, Get, Post, UseGuards } from '@nestjs/common';
 import { ApiOperation, ApiParam } from '@nestjs/swagger';
 import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
+import { NewsletterService } from '../newsletter/newsletter.service';
 import { StructuresService } from '../structures/services/structures.service';
 import { Roles } from '../users/decorators/roles.decorator';
 import { RolesGuard } from '../users/guards/roles.guard';
@@ -10,7 +11,11 @@ import { PendingStructureDto } from './dto/pending-structure.dto';
 
 @Controller('admin')
 export class AdminController {
-  constructor(private usersService: UsersService, private structuresService: StructuresService) {}
+  constructor(
+    private usersService: UsersService,
+    private structuresService: StructuresService,
+    private newsletterService: NewsletterService
+  ) {}
 
   @UseGuards(JwtAuthGuard, RolesGuard)
   @Roles('admin')
@@ -96,4 +101,21 @@ export class AdminController {
       return this.usersService.searchUsers(searchString.searchString);
     else return this.usersService.findAll();
   }
+
+  @UseGuards(JwtAuthGuard, RolesGuard)
+  @Roles('admin')
+  @Post('searchNewsletterSubscriptions')
+  public async getNewsletterSubscriptions(@Body() searchString: { searchString: string }) {
+    if (searchString && searchString.searchString.length > 0)
+      return this.newsletterService.searchNewsletterSubscription(searchString.searchString);
+    else return this.newsletterService.findAll();
+  }
+
+  @UseGuards(JwtAuthGuard, RolesGuard)
+  @Roles('admin')
+  @Delete('newsletterSubscription/:email')
+  @ApiParam({ name: 'email', type: String, required: true })
+  public async unsubscribeUserFromNewsletter(@Param() params) {
+    return await this.newsletterService.deleteOneEmail(params.email);
+  }
 }
diff --git a/src/admin/admin.module.ts b/src/admin/admin.module.ts
index a13616a1802d24dc4f58d513714a9647193c591b..7854c19b255df99cc35837f56d9f9dfd637d7bf5 100644
--- a/src/admin/admin.module.ts
+++ b/src/admin/admin.module.ts
@@ -1,11 +1,12 @@
 import { Module } from '@nestjs/common';
+import { NewsletterModule } from '../newsletter/newsletter.module';
 import { StructuresModule } from '../structures/structures.module';
 import { UsersModule } from '../users/users.module';
 import { AdminController } from './admin.controller';
 import { AdminService } from './admin.service';
 
 @Module({
-  imports: [UsersModule, StructuresModule],
+  imports: [UsersModule, StructuresModule, NewsletterModule],
   controllers: [AdminController],
   providers: [AdminService],
 })
diff --git a/src/app.module.ts b/src/app.module.ts
index 467fe404c31f6fdeadc948aec96bdf136aa676f3..ed6a03004e4e90edc2decf97c41b7f3478ca4f69 100644
--- a/src/app.module.ts
+++ b/src/app.module.ts
@@ -12,6 +12,7 @@ import { TclModule } from './tcl/tcl.module';
 import { AdminModule } from './admin/admin.module';
 import { PostsModule } from './posts/posts.module';
 import { TempUserModule } from './temp-user/temp-user.module';
+import { NewsletterModule } from './newsletter/newsletter.module';
 @Module({
   imports: [
     ConfigurationModule,
@@ -28,6 +29,7 @@ import { TempUserModule } from './temp-user/temp-user.module';
     AdminModule,
     PostsModule,
     TempUserModule,
+    NewsletterModule
   ],
   controllers: [AppController],
 })
diff --git a/src/categories/categories.module.ts b/src/categories/categories.module.ts
index 380468396bceb0829ce6ad1a93e5b029a4722799..bc6f3c86841008cb9bed60f0c70bbd517b947403 100644
--- a/src/categories/categories.module.ts
+++ b/src/categories/categories.module.ts
@@ -19,6 +19,7 @@ import { CategoriesAccompagnement, CategoriesAccompagnementSchema } from './sche
     ]),
   ],
   controllers: [CategoriesFormationsController, CategoriesAccompagnementController, CategoriesOthersController],
+  exports: [CategoriesFormationsService],
   providers: [CategoriesFormationsService, CategoriesAccompagnementService, CategoriesOthersService],
 })
 export class CategoriesModule {}
diff --git a/src/categories/services/categories-formations.service.ts b/src/categories/services/categories-formations.service.ts
index 05ecae4af07f33c273ecdd2ded6a89c734358c8c..71a6c1073ad5245541c651836b272195dd96397c 100644
--- a/src/categories/services/categories-formations.service.ts
+++ b/src/categories/services/categories-formations.service.ts
@@ -16,4 +16,8 @@ export class CategoriesFormationsService {
   public async findAll(): Promise<CategoriesFormations[]> {
     return this.structureModel.find().exec();
   }
+
+  public findOne(categoryId: string): Promise<any> {
+    return this.structureModel.findOne({ id: categoryId }).select({ 'modules.id': 1 }).exec();
+  }
 }
diff --git a/src/configuration/config.ts b/src/configuration/config.ts
index 3d6969e35463dda86086db80ee2bd8f9c762132a..0506dc2ae8a5b211ad5883852d50858f71e84fda 100644
--- a/src/configuration/config.ts
+++ b/src/configuration/config.ts
@@ -49,5 +49,9 @@ export const config = {
       ejs: 'adminStructureCreate.ejs',
       json: 'adminStructureCreate.json',
     },
+    structureErrorReport: {
+      ejs: 'structureErrorReport.ejs',
+      json: 'structureErrorReport.json',
+    },
   },
 };
diff --git a/src/mailer/mail-templates/adminStructureCreate.ejs b/src/mailer/mail-templates/adminStructureCreate.ejs
index 41d58470fa059925b790e440caec3c5606dbda1c..47b79daa15b999739415094be15f42e9f641ede0 100644
--- a/src/mailer/mail-templates/adminStructureCreate.ejs
+++ b/src/mailer/mail-templates/adminStructureCreate.ejs
@@ -1,3 +1,9 @@
 Bonjour<br />
 <br />
-Une nouvelle structure a été créé: <strong><%= name %></strong>.
+Une nouvelle structure a été créé:
+<a href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/acteurs?id=<%= id %>"
+  ><strong><%= name %></strong></a
+>
+<br />
+Il est possible que la structure ne soit pas immédiatement visible sur la carto. L'utilisateur doit valider son compte
+pour que celle-ci soit visible.
diff --git a/src/mailer/mail-templates/structureErrorReport.ejs b/src/mailer/mail-templates/structureErrorReport.ejs
new file mode 100644
index 0000000000000000000000000000000000000000..829029e95f157b6034c60889e7ee0798f2518d48
--- /dev/null
+++ b/src/mailer/mail-templates/structureErrorReport.ejs
@@ -0,0 +1,11 @@
+Bonjour<br />
+<br />
+Un utilisateur de Res'in a relevé une erreur sur la fiche de votre structure (<%= structureName %>).
+<br />
+Voici le message:<br />
+<br />
+<strong><%= content %></strong><br />
+<br />
+<a href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/acteurs?id=<%= id %>"
+  >Acceder à votre structure</a
+>.
diff --git a/src/mailer/mail-templates/structureErrorReport.json b/src/mailer/mail-templates/structureErrorReport.json
new file mode 100644
index 0000000000000000000000000000000000000000..3faeacf33799d9891558bedba0a2771ef7344782
--- /dev/null
+++ b/src/mailer/mail-templates/structureErrorReport.json
@@ -0,0 +1,3 @@
+{
+  "subject": "Une erreur a été remontée sur votre structure, Réseau des Acteurs de la Médiation Numérique de la Métropole de Lyon"
+}
diff --git a/src/mailer/mail-templates/structureOutdatedInfo.ejs b/src/mailer/mail-templates/structureOutdatedInfo.ejs
index 589ec35605915f4ffb5a62ce3a6d5c7827d1064f..5427f8b22825c64ab25c3da4d903b3dc27742cbd 100644
--- a/src/mailer/mail-templates/structureOutdatedInfo.ejs
+++ b/src/mailer/mail-templates/structureOutdatedInfo.ejs
@@ -3,6 +3,6 @@ Bonjour<br />
 Vous recevez ce message, parce que votre structure <strong><%= name %></strong> est référencée sur RES'in, le réseau des
 acteurs de l'inclusion numérique de la Métropole de Lyon. Pouvez-vous nous aider en vérifiant que vos données sont bien
 à jour en
-<a href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/home?id=<%= id %>"
+<a href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/acteurs?id=<%= id %>"
   >cliquant ici</a
 >.
diff --git a/src/mailer/mailer.service.spec.ts b/src/mailer/mailer.service.spec.ts
index 086056cab0a7d8efe90a3fde1e61ac0b833e84ee..2fd12e46668bdc0cc4a32940715c2a7fc9043cdf 100644
--- a/src/mailer/mailer.service.spec.ts
+++ b/src/mailer/mailer.service.spec.ts
@@ -18,4 +18,28 @@ describe('MailerService', () => {
   it('should be defined', () => {
     expect(service).toBeDefined();
   });
+  
+  it('should send email', async () => {
+    const result = "AxiosResponse"
+    jest.spyOn(service, 'send').mockImplementation(async (): Promise<any> => result);
+    expect(await service.send("to", "subject", "html")).toBe(result);
+  });
+
+  it('should get template location', async () => {
+    const result = "/path/to/template";
+    jest.spyOn(service, 'getTemplateLocation').mockImplementation(() => {return result});
+    expect(await service.getTemplateLocation("filename")).toBe(result);
+  });
+
+  it('should load Json Config', async () => {
+    const result = null;
+    jest.spyOn(service, 'loadJsonConfig').mockImplementation(async (): Promise<any> => result);
+    expect(await service.loadJsonConfig("filename")).toBe(result);
+  });
+
+  it('should add signature', async () => {
+    const result = "signed html";
+    jest.spyOn(service, 'addSignature').mockImplementation(() => {return result});
+    expect(await service.addSignature("html")).toBe(result);
+  });
 });
diff --git a/src/mailer/mailer.service.ts b/src/mailer/mailer.service.ts
index 0650bc48c0255866d15f86ed725254b665ad01bf..c19d4b2b2830a1ba83c34669ab195df78c90a9ff 100644
--- a/src/mailer/mailer.service.ts
+++ b/src/mailer/mailer.service.ts
@@ -21,15 +21,15 @@ export class MailerService {
    * @param {string} html
    * @param {string} text
    */
-  public async send(to: string, subject: string, html: string): Promise<AxiosResponse<any>> {
+  public async send(to: string | { email: string }[], subject: string, html: string): Promise<AxiosResponse<any>> {
+    const emailsToSend = typeof to === 'string' ? [{ email: to }] : to;
     const formData = new FormData();
-
     const data = JSON.stringify({
       // eslint-disable-next-line camelcase
       from_email: this.config.from,
       // eslint-disable-next-line camelcase
       from_name: this.config.from_name,
-      to: [{ email: to }],
+      to: emailsToSend,
       reply_to: 'inclusionnumerique@grandlyon.com',
       subject: subject,
       content: this.addSignature(html),
diff --git a/src/migrations/migrations-utils/db.ts b/src/migrations/migrations-utils/db.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4a5284a096c12204c8e3d1b9e3b5b620729d0092
--- /dev/null
+++ b/src/migrations/migrations-utils/db.ts
@@ -0,0 +1,11 @@
+import { MongoClient } from 'mongodb';
+import { config } from 'dotenv';
+
+export const getDb = async () => {
+  config(); // Required for reading .env
+  const client: MongoClient = await MongoClient.connect(
+    `mongodb://${process.env.MONGO_NON_ROOT_USERNAME}:${process.env.MONGO_NON_ROOT_PASSWORD}@${process.env.MONGO_DB_HOST_AND_PORT}/ram`,
+    { useUnifiedTopology: true }
+  );
+  return client.db();
+};
diff --git a/src/migrations/migrations-utils/template.ts b/src/migrations/migrations-utils/template.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d3f00960db09db7b37081642dadc328fa876ce13
--- /dev/null
+++ b/src/migrations/migrations-utils/template.ts
@@ -0,0 +1,13 @@
+import { Db } from 'mongodb';
+import { getDb } from '../migrations-utils/db';
+
+export const up = async () => {
+  const db: Db = await getDb();
+};
+
+export const down = async () => {
+  const db: Db = await getDb();
+  /*
+      Code you downgrade script here!
+   */
+};
diff --git a/src/migrations/migrations-utils/ts-compiler.js b/src/migrations/migrations-utils/ts-compiler.js
new file mode 100644
index 0000000000000000000000000000000000000000..1424f7e7c44aef954e45570d48ac921490e0f583
--- /dev/null
+++ b/src/migrations/migrations-utils/ts-compiler.js
@@ -0,0 +1,3 @@
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const tsNode = require('ts-node');
+module.exports = tsNode.register;
diff --git a/src/migrations/scripts/1617284203579-apticid.ts b/src/migrations/scripts/1617284203579-apticid.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b7d5efcf3f43e4000ae6805540ed185073f392df
--- /dev/null
+++ b/src/migrations/scripts/1617284203579-apticid.ts
@@ -0,0 +1,394 @@
+import { getDb } from '../migrations-utils/db';
+
+class ApticModule {
+  id: string;
+  display_id: string;
+  display_name: string;
+  url: string;
+  last_update_time: string;
+  receipt_time: string;
+}
+
+class ApticDoc {
+  id: string;
+  modules: ApticModule[];
+}
+
+export const up = async () => {
+  const db = await getDb();
+  await updateStructuresId(db);
+  await updateApticReferential(db);
+};
+
+export const down = async () => {
+  const db = await getDb();
+  await downgradeApticReferential(db);
+  await downgradeStructuresId(db);
+};
+
+async function downgradeStructuresId(db) {
+  await db
+    .collection('structures')
+    .find({})
+    .forEach((doc) => {
+      const newDoc = downgradeStructure(doc);
+      db.collection('structures').updateMany({ _id: doc._id }, [{ $set: newDoc }]);
+    });
+}
+
+async function updateStructuresId(db) {
+  await db
+    .collection('structures')
+    .find({})
+    .forEach((doc) => {
+      const newDoc = updateStructure(doc);
+      db.collection('structures').updateMany({ _id: doc._id }, [{ $set: newDoc }]);
+    });
+}
+
+function updateStructure(structure): any {
+  let newArray = [];
+  // Social and professional
+  newArray.push(switchStructureId(structure.socialAndProfessional, '254', '6'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '240', '20'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '194', '66'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '193', '67'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '192', '68'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '191', '69'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '262', '124'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '263', '125'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '003', '127'));
+  // Remove null cases
+  newArray = newArray.filter((obj) => obj);
+  structure.socialAndProfessional = newArray;
+
+  // Base Skills
+  newArray = [];
+  newArray.push(switchStructureId(structure.baseSkills, '260', '260')); //TODO:
+  newArray.push(switchStructureId(structure.baseSkills, '259', '1'));
+  newArray.push(switchStructureId(structure.baseSkills, '261', '11'));
+  newArray.push(switchStructureId(structure.baseSkills, '222', '38'));
+  newArray.push(switchStructureId(structure.baseSkills, '212', '48'));
+  newArray.push(switchStructureId(structure.baseSkills, '186', '74'));
+  newArray.push(switchStructureId(structure.baseSkills, '183', '77'));
+  // Remove null cases
+  newArray = newArray.filter((obj) => obj);
+  structure.baseSkills = newArray;
+
+  // Access Right
+  newArray = [];
+  newArray.push(switchStructureId(structure.accessRight, '176', '84'));
+  newArray.push(switchStructureId(structure.accessRight, '175', '85'));
+  newArray.push(switchStructureId(structure.accessRight, '174', '86'));
+  newArray.push(switchStructureId(structure.accessRight, '173', '87'));
+  newArray.push(switchStructureId(structure.accessRight, '172', '88'));
+  newArray.push(switchStructureId(structure.accessRight, '171', '89'));
+  newArray.push(switchStructureId(structure.accessRight, '167', '93'));
+  newArray.push(switchStructureId(structure.accessRight, '165', '95'));
+  // Remove null cases
+  newArray = newArray.filter((obj) => obj);
+  structure.accessRight = newArray;
+
+  // Parenting
+  newArray = [];
+  newArray.push(switchStructureId(structure.parentingHelp, '257', '3'));
+  newArray.push(switchStructureId(structure.parentingHelp, '238', '22'));
+  newArray.push(switchStructureId(structure.parentingHelp, '178', '82'));
+  newArray.push(switchStructureId(structure.parentingHelp, '166', '94'));
+  // Remove null cases
+  newArray = newArray.filter((obj) => obj);
+  structure.parentingHelp = newArray;
+
+  // Digital Security
+  newArray = [];
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '264', '2'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '255', '5'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '265', '9'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '232', '28'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '225', '34'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '221', '39'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '218', '42'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '209', '51'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '208', '52'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '206', '54'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '195', '65'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '164', '96'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '163', '97'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '162', '98'));
+  // Remove null cases
+  newArray = newArray.filter((obj) => obj);
+  structure.digitalCultureSecurity = newArray;
+
+  return structure;
+}
+
+function downgradeStructure(structure): any {
+  let newArray = [];
+  // Social and professional
+  newArray.push(switchStructureId(structure.socialAndProfessional, '6', '254'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '20', '240'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '66', '194'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '67', '193'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '68', '192'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '69', '191'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '124', '262'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '125', '263'));
+  newArray.push(switchStructureId(structure.socialAndProfessional, '127', '003'));
+  // Remove null cases
+  newArray = newArray.filter((obj) => obj);
+  structure.socialAndProfessional = newArray;
+
+  // Base Skills
+  newArray = [];
+  newArray.push(switchStructureId(structure.baseSkills, '260', '260'));
+  newArray.push(switchStructureId(structure.baseSkills, '1', '259'));
+  newArray.push(switchStructureId(structure.baseSkills, '11', '261'));
+  newArray.push(switchStructureId(structure.baseSkills, '38', '222'));
+  newArray.push(switchStructureId(structure.baseSkills, '48', '212'));
+  newArray.push(switchStructureId(structure.baseSkills, '74', '186'));
+  newArray.push(switchStructureId(structure.baseSkills, '77', '183'));
+  // Remove null cases
+  newArray = newArray.filter((obj) => obj);
+  structure.baseSkills = newArray;
+
+  // Access Right
+  newArray = [];
+  newArray.push(switchStructureId(structure.accessRight, '84', '176'));
+  newArray.push(switchStructureId(structure.accessRight, '85', '175'));
+  newArray.push(switchStructureId(structure.accessRight, '86', '174'));
+  newArray.push(switchStructureId(structure.accessRight, '87', '173'));
+  newArray.push(switchStructureId(structure.accessRight, '88', '172'));
+  newArray.push(switchStructureId(structure.accessRight, '89', '171'));
+  newArray.push(switchStructureId(structure.accessRight, '93', '167'));
+  newArray.push(switchStructureId(structure.accessRight, '95', '165'));
+  // Remove null cases
+  newArray = newArray.filter((obj) => obj);
+  structure.accessRight = newArray;
+
+  // Parenting
+  newArray = [];
+  newArray.push(switchStructureId(structure.parentingHelp, '3', '257'));
+  newArray.push(switchStructureId(structure.parentingHelp, '22', '238'));
+  newArray.push(switchStructureId(structure.parentingHelp, '82', '178'));
+  newArray.push(switchStructureId(structure.parentingHelp, '94', '166'));
+  // Remove null cases
+  newArray = newArray.filter((obj) => obj);
+  structure.parentingHelp = newArray;
+
+  // Digital Security
+  newArray = [];
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '2', '264'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '5', '255'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '9', '265'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '28', '232'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '34', '225'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '39', '221'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '42', '218'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '51', '209'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '52', '208'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '54', '206'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '65', '195'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '96', '164'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '97', '163'));
+  newArray.push(switchStructureId(structure.digitalCultureSecurity, '98', '162'));
+  // Remove null cases
+  newArray = newArray.filter((obj) => obj);
+  structure.digitalCultureSecurity = newArray;
+
+  return structure;
+}
+
+async function updateApticReferential(db) {
+  // Base Skills
+  let newModule = { modules: [] };
+  const baseSkills: ApticDoc = await db.collection('categoriesformations').findOne({ id: 'baseSkills' });
+  newModule.modules.push(switchId(baseSkills.modules, '260', '260'));
+  newModule.modules.push(switchId(baseSkills.modules, '259', '1'));
+  newModule.modules.push(switchId(baseSkills.modules, '261', '11'));
+  newModule.modules.push(switchId(baseSkills.modules, '222', '38'));
+  newModule.modules.push(switchId(baseSkills.modules, '212', '48'));
+  newModule.modules.push(switchId(baseSkills.modules, '186', '74'));
+  newModule.modules.push(switchId(baseSkills.modules, '183', '77'));
+  db.collection('categoriesformations').findOneAndUpdate(
+    { id: 'baseSkills' },
+    { $set: { modules: newModule.modules } }
+  );
+
+  // Access Right
+  newModule = { modules: [] };
+  const accessRight: ApticDoc = await db.collection('categoriesformations').findOne({ id: 'accessRight' });
+  newModule.modules.push(switchId(accessRight.modules, '176', '84'));
+  newModule.modules.push(switchId(accessRight.modules, '175', '85'));
+  newModule.modules.push(switchId(accessRight.modules, '174', '86'));
+  newModule.modules.push(switchId(accessRight.modules, '173', '87'));
+  newModule.modules.push(switchId(accessRight.modules, '172', '88'));
+  newModule.modules.push(switchId(accessRight.modules, '171', '89'));
+  newModule.modules.push(switchId(accessRight.modules, '167', '93'));
+  newModule.modules.push(switchId(accessRight.modules, '165', '95'));
+  db.collection('categoriesformations').findOneAndUpdate(
+    { id: 'accessRight' },
+    { $set: { modules: newModule.modules } }
+  );
+
+  // Parenting
+  newModule = { modules: [] };
+  const parentingHelp: ApticDoc = await db.collection('categoriesformations').findOne({ id: 'parentingHelp' });
+  newModule.modules.push(switchId(parentingHelp.modules, '257', '3'));
+  newModule.modules.push(switchId(parentingHelp.modules, '238', '22'));
+  newModule.modules.push(switchId(parentingHelp.modules, '178', '82'));
+  newModule.modules.push(switchId(parentingHelp.modules, '166', '94'));
+  db.collection('categoriesformations').findOneAndUpdate(
+    { id: 'parentingHelp' },
+    { $set: { modules: newModule.modules } }
+  );
+
+  // Social and professional
+  newModule = { modules: [] };
+  const socialAndProfessional: ApticDoc = await db
+    .collection('categoriesformations')
+    .findOne({ id: 'socialAndProfessional' });
+  newModule.modules.push(switchId(socialAndProfessional.modules, '254', '6'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '240', '20'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '194', '66'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '193', '67'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '192', '68'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '191', '69'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '262', '124'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '263', '125'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '003', '127'));
+  db.collection('categoriesformations').findOneAndUpdate(
+    { id: 'socialAndProfessional' },
+    { $set: { modules: newModule.modules } }
+  );
+
+  // Digital security
+  newModule = { modules: [] };
+  const digitalCultureSecurity: ApticDoc = await db
+    .collection('categoriesformations')
+    .findOne({ id: 'digitalCultureSecurity' });
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '264', '2'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '255', '5'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '265', '9'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '232', '28'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '225', '34'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '221', '39'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '218', '42'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '209', '51'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '208', '52'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '206', '54'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '195', '65'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '164', '96'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '163', '97'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '162', '98'));
+  db.collection('categoriesformations').findOneAndUpdate(
+    { id: 'digitalCultureSecurity' },
+    { $set: { modules: newModule.modules } }
+  );
+}
+
+async function downgradeApticReferential(db) {
+  // Base Skills
+  let newModule = { modules: [] };
+  const baseSkills: ApticDoc = await db.collection('categoriesformations').findOne({ id: 'baseSkills' });
+  newModule.modules.push(switchId(baseSkills.modules, '260', '260')); //TODO:
+  newModule.modules.push(switchId(baseSkills.modules, '1', '259'));
+  newModule.modules.push(switchId(baseSkills.modules, '11', '261'));
+  newModule.modules.push(switchId(baseSkills.modules, '38', '222'));
+  newModule.modules.push(switchId(baseSkills.modules, '48', '212'));
+  newModule.modules.push(switchId(baseSkills.modules, '74', '186'));
+  newModule.modules.push(switchId(baseSkills.modules, '77', '183'));
+  db.collection('categoriesformations').findOneAndUpdate(
+    { id: 'baseSkills' },
+    { $set: { modules: newModule.modules } }
+  );
+
+  // Access Right
+  newModule = { modules: [] };
+  const accessRight: ApticDoc = await db.collection('categoriesformations').findOne({ id: 'accessRight' });
+  newModule.modules.push(switchId(accessRight.modules, '84', '176'));
+  newModule.modules.push(switchId(accessRight.modules, '85', '175'));
+  newModule.modules.push(switchId(accessRight.modules, '86', '174'));
+  newModule.modules.push(switchId(accessRight.modules, '87', '173'));
+  newModule.modules.push(switchId(accessRight.modules, '88', '172'));
+  newModule.modules.push(switchId(accessRight.modules, '89', '171'));
+  newModule.modules.push(switchId(accessRight.modules, '93', '167'));
+  newModule.modules.push(switchId(accessRight.modules, '95', '165'));
+  db.collection('categoriesformations').findOneAndUpdate(
+    { id: 'accessRight' },
+    { $set: { modules: newModule.modules } }
+  );
+
+  // Parenting
+  newModule = { modules: [] };
+  const parentingHelp: ApticDoc = await db.collection('categoriesformations').findOne({ id: 'parentingHelp' });
+  newModule.modules.push(switchId(parentingHelp.modules, '3', '257'));
+  newModule.modules.push(switchId(parentingHelp.modules, '22', '238'));
+  newModule.modules.push(switchId(parentingHelp.modules, '82', '178'));
+  newModule.modules.push(switchId(parentingHelp.modules, '94', '166'));
+  db.collection('categoriesformations').findOneAndUpdate(
+    { id: 'parentingHelp' },
+    { $set: { modules: newModule.modules } }
+  );
+
+  // Social and professional
+  newModule = { modules: [] };
+  const socialAndProfessional: ApticDoc = await db
+    .collection('categoriesformations')
+    .findOne({ id: 'socialAndProfessional' });
+  newModule.modules.push(switchId(socialAndProfessional.modules, '6', '254'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '20', '240'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '66', '194'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '67', '193'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '68', '192'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '69', '191'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '124', '262'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '125', '263'));
+  newModule.modules.push(switchId(socialAndProfessional.modules, '127', '003'));
+  db.collection('categoriesformations').findOneAndUpdate(
+    { id: 'socialAndProfessional' },
+    { $set: { modules: newModule.modules } }
+  );
+
+  // Digital security
+  newModule = { modules: [] };
+  const digitalCultureSecurity: ApticDoc = await db
+    .collection('categoriesformations')
+    .findOne({ id: 'digitalCultureSecurity' });
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '2', '264'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '5', '255'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '9', '265'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '28', '232'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '34', '225'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '39', '221'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '42', '218'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '51', '209'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '52', '208'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '54', '206'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '65', '195'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '96', '164'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '97', '163'));
+  newModule.modules.push(switchId(digitalCultureSecurity.modules, '98', '162'));
+  db.collection('categoriesformations').findOneAndUpdate(
+    { id: 'digitalCultureSecurity' },
+    { $set: { modules: newModule.modules } }
+  );
+}
+
+function switchStructureId(moduleArray: [], originalId: string, newId: string): string {
+  const module = moduleArray.find((id) => id === originalId);
+  if (module) {
+    return newId;
+  }
+  return null;
+}
+
+function switchId(moduleArray: ApticModule[], originalId: string, newId: string): ApticModule {
+  const module = moduleArray.find((skill) => skill.id === originalId);
+  module.id = newId;
+  module.display_id = newId;
+  module.display_name = `Modules APTIC - n°${newId}`;
+  delete module.receipt_time;
+  delete module.last_update_time;
+  delete module.url;
+  return module;
+}
diff --git a/src/migrations/scripts/1617962328658-add-newsletter-data.ts b/src/migrations/scripts/1617962328658-add-newsletter-data.ts
new file mode 100644
index 0000000000000000000000000000000000000000..17821b927c47a2d58fb3544f34b98917d7f465e4
--- /dev/null
+++ b/src/migrations/scripts/1617962328658-add-newsletter-data.ts
@@ -0,0 +1,17 @@
+import { Db } from 'mongodb';
+import { getDb } from '../migrations-utils/db';
+import * as fs from 'fs';
+
+export const up = async () => {
+  const db: Db = await getDb();
+  const data = fs.readFileSync('/app/src/migrations/data/newsletter-data.json', 'utf8');
+  const parsedData = JSON.parse(data);
+  db.collection('newslettersubscriptions').insertMany(parsedData);
+};
+
+export const down = async () => {
+  const db: Db = await getDb();
+  /*
+      Code you downgrade script here!
+   */
+};
diff --git a/src/newsletter/interface/newsletter-subscription.interface.ts b/src/newsletter/interface/newsletter-subscription.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..382e0911c13768f56e091db0c420ba4df013b3fd
--- /dev/null
+++ b/src/newsletter/interface/newsletter-subscription.interface.ts
@@ -0,0 +1,5 @@
+import { Document } from 'mongoose';
+
+export interface INewsletterSubscription extends Document {
+  email: string;
+}
diff --git a/src/newsletter/newsletter-subscription.schema.ts b/src/newsletter/newsletter-subscription.schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9ff18ab015a2e00b018ac1929bed0fb0a7a7ac48
--- /dev/null
+++ b/src/newsletter/newsletter-subscription.schema.ts
@@ -0,0 +1,12 @@
+import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
+import { Document } from 'mongoose';
+
+export type NewsletterSubscriptionDocument = NewsletterSubscription & Document;
+
+@Schema({ timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' } })
+export class NewsletterSubscription {
+  @Prop({ required: true })
+  email: string;
+}
+
+export const NewsletterSubscriptionSchema = SchemaFactory.createForClass(NewsletterSubscription);
diff --git a/src/newsletter/newsletter.controller.spec.ts b/src/newsletter/newsletter.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2e5adbb61affbb5606e32e2768ee9c60e09f1173
--- /dev/null
+++ b/src/newsletter/newsletter.controller.spec.ts
@@ -0,0 +1,44 @@
+import { HttpModule } from '@nestjs/common';
+import { getModelToken } from '@nestjs/mongoose';
+import { Test, TestingModule } from '@nestjs/testing';
+import { ConfigurationModule } from '../configuration/configuration.module';
+import { NewsletterSubscription } from './newsletter-subscription.schema';
+import { NewsletterController } from './newsletter.controller';
+import { NewsletterService } from './newsletter.service';
+describe('NewsletterController', () => {
+  let controller: NewsletterController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [ConfigurationModule, HttpModule],
+      providers: [
+        NewsletterService,
+        {
+          provide: getModelToken('NewsletterSubscription'),
+          useValue: NewsletterSubscription,
+        },
+      ],
+      controllers: [NewsletterController],
+    }).compile();
+
+    controller = module.get<NewsletterController>(NewsletterController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+
+  it('should subscribe user', async () => {
+    const result = { email: 'email@test.com' };
+    jest.spyOn(controller, 'newsletterSubscribe').mockImplementation(async (): Promise<{ email }> => result);
+    const email = { email: 'email@test.com' };
+    expect(await controller.newsletterSubscribe(email)).toBe(result);
+  });
+
+  it('should unsubscribe user', async () => {
+    const result = { email: 'email@test.com' };
+    jest.spyOn(controller, 'newsletterUnsubscribe').mockImplementation(async (): Promise<{ email }> => result);
+    const email = { email: 'email@test.com' };
+    expect(await controller.newsletterUnsubscribe(email)).toBe(result);
+  });
+});
diff --git a/src/newsletter/newsletter.controller.ts b/src/newsletter/newsletter.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8a7c77d35689d37df3e66c9c42d9405cc35c6655
--- /dev/null
+++ b/src/newsletter/newsletter.controller.ts
@@ -0,0 +1,17 @@
+import { Body, Controller, Post } from '@nestjs/common';
+import { NewsletterService } from './newsletter.service';
+
+@Controller('newsletter')
+export class NewsletterController {
+  constructor(private newsletterService: NewsletterService) {}
+
+  @Post('subscribe')
+  public async newsletterSubscribe(@Body() email: { email: string }) {
+    return this.newsletterService.newsletterSubscribe(email.email);
+  }
+
+  @Post('unsubscribe')
+  public async newsletterUnsubscribe(@Body() email: { email: string }) {
+    return this.newsletterService.newsletterUnsubscribe(email.email);
+  }
+}
diff --git a/src/newsletter/newsletter.module.ts b/src/newsletter/newsletter.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2f949c385cffe4e07c54b645c30d0221f41b31f8
--- /dev/null
+++ b/src/newsletter/newsletter.module.ts
@@ -0,0 +1,15 @@
+import { HttpModule, Module } from '@nestjs/common';
+import { MongooseModule } from '@nestjs/mongoose';
+import { NewsletterService } from './newsletter.service';
+import { NewsletterController } from './newsletter.controller';
+import { NewsletterSubscription, NewsletterSubscriptionSchema } from './newsletter-subscription.schema';
+@Module({
+  imports: [
+    MongooseModule.forFeature([{ name: NewsletterSubscription.name, schema: NewsletterSubscriptionSchema }]),
+    HttpModule,
+  ],
+  providers: [NewsletterService],
+  exports: [NewsletterService],
+  controllers: [NewsletterController],
+})
+export class NewsletterModule {}
diff --git a/src/newsletter/newsletter.service.ts b/src/newsletter/newsletter.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c1ca5efc0e07368586b7a3ac99c7248c43d8a85f
--- /dev/null
+++ b/src/newsletter/newsletter.service.ts
@@ -0,0 +1,50 @@
+import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
+import { InjectModel } from '@nestjs/mongoose';
+import { Model, Types } from 'mongoose';
+import { INewsletterSubscription } from './interface/newsletter-subscription.interface';
+import { NewsletterSubscription } from './newsletter-subscription.schema';
+
+@Injectable()
+export class NewsletterService {
+  constructor(
+    @InjectModel(NewsletterSubscription.name) private newsletterSubscriptionModel: Model<INewsletterSubscription>
+  ) {}
+
+  public async newsletterSubscribe(email: string): Promise<NewsletterSubscription> {
+    const existingEmail = await this.findOne(email);
+    if (existingEmail) {
+      throw new HttpException('Email already exists', HttpStatus.BAD_REQUEST);
+    }
+    const createSubscription = new this.newsletterSubscriptionModel({ email: email });
+    createSubscription.save();
+    return await this.findOne(email);
+  }
+
+  public async newsletterUnsubscribe(email: string): Promise<NewsletterSubscription> {
+    const subscription = await this.newsletterSubscriptionModel.findOne({ email: email }).exec();
+    if (!subscription) {
+      throw new HttpException('Invalid email', HttpStatus.BAD_REQUEST);
+    }
+    return subscription.deleteOne();
+  }
+
+  public async findOne(mail: string): Promise<NewsletterSubscription | undefined> {
+    return this.newsletterSubscriptionModel.findOne({ email: mail }).exec();
+  }
+
+  public async searchNewsletterSubscription(searchString: string) {
+    return this.newsletterSubscriptionModel.find({ email: new RegExp(searchString, 'i') }).exec();
+  }
+
+  public async deleteOneEmail(mail: string): Promise<NewsletterSubscription | undefined> {
+    const subscription = await this.newsletterSubscriptionModel.findOne({ email: mail }).exec();
+    if (!subscription) {
+      throw new HttpException('Invalid  email', HttpStatus.BAD_REQUEST);
+    }
+    return subscription.deleteOne();
+  }
+
+  public async findAll(): Promise<NewsletterSubscription[]> {
+    return await this.newsletterSubscriptionModel.find().exec();
+  }
+}
diff --git a/src/posts/posts.controller.spec.ts b/src/posts/posts.controller.spec.ts
index 0c512080183b819060a4fe2451f6433a4a784e8a..39b091029861448339fa4a8c755dd634664a6d77 100644
--- a/src/posts/posts.controller.spec.ts
+++ b/src/posts/posts.controller.spec.ts
@@ -3,6 +3,7 @@ import { Test, TestingModule } from '@nestjs/testing';
 import { ConfigurationModule } from '../configuration/configuration.module';
 import { PostsController } from './posts.controller';
 import { PostsService } from './posts.service';
+import { PostWithMeta } from './schemas/postWithMeta.schema';
 
 describe('PostsController', () => {
   let controller: PostsController;
@@ -20,4 +21,24 @@ describe('PostsController', () => {
   it('should be defined', () => {
     expect(controller).toBeDefined();
   });
+
+  it('should get pending attachments', async () => {
+    const result:PostWithMeta = {posts:[], meta:{pagination: null}};
+    const query = "";
+    jest.spyOn(controller, 'findAll').mockImplementation(async (): Promise<any> => result);
+    expect(await controller.findAll(query)).toBe(result);
+  });
+
+  it('should get pending attachments', async () => {
+    const result = { posts:[] };
+    jest.spyOn(controller, 'findAllTags').mockImplementation(async (): Promise<any> => result);
+    expect(await controller.findAllTags()).toBe(result);
+  });
+
+  it('should get pending attachments', async () => {
+    const result = { public:[], comune:[], others:[] };
+    const id = "78945945"
+    jest.spyOn(controller, 'getPostbyId').mockImplementation(async (): Promise<any> => result);
+    expect(await controller.getPostbyId(id)).toBe(result);
+  });
 });
diff --git a/src/structures/schemas/aptic-catalog.schema.ts b/src/structures/schemas/aptic-catalog.schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa98f194fb72dbf15ae1888b07753845d97c1614
--- /dev/null
+++ b/src/structures/schemas/aptic-catalog.schema.ts
@@ -0,0 +1,4 @@
+export class ApticCatalog {
+  code: string;
+  label: string;
+}
diff --git a/src/structures/schemas/aptic-structure.schema.ts b/src/structures/schemas/aptic-structure.schema.ts
index afb95430cb303041c529327f43d417c6d46792b3..cd8b6b56dcb6af349084b8c7cec68346e01853e6 100644
--- a/src/structures/schemas/aptic-structure.schema.ts
+++ b/src/structures/schemas/aptic-structure.schema.ts
@@ -1,43 +1,37 @@
-export class ApticStructure {
-  presence_id: string;
-
-  presence_name: string;
-
-  presence_phone: string;
-
-  presence_address: string;
-
-  organization_id: string;
-
-  organization_legal_status: string;
-
-  organization_type: string;
+import { ApticCatalog } from './aptic-catalog.schema';
 
-  gps_lat: number;
-
-  gps_lng: number;
-
-  postal_code: string;
-
-  city: string;
-
-  city_lat: number;
-
-  city_lng: number;
+export class ApticStructure {
+  id: string;
 
-  department: string;
+  name: string;
 
-  department_code: string;
+  phone: string;
 
-  region_name: string;
+  website: string;
 
-  region_code: string;
+  address: {
+    main: string;
+    extra: string;
+    zip: string;
+    city: string;
+    gpsLat: number;
+    gpsLng: number;
+    department: string;
+    departmentCode: string;
+    region: string;
+    regionCode: string;
+  };
 
-  catalog_id: string;
+  organization: {
+    id: string;
+    name: string;
+    type: string;
+    legalStatus: string;
+  };
 
-  service_count: number;
+  catalogs: string[];
 
-  created: string;
+  services: ApticCatalog[];
 
-  updated: string;
+  serviceCount: number;
 }
diff --git a/src/structures/services/aptic-structures.service.ts b/src/structures/services/aptic-structures.service.ts
index fff5c3326547acc5f68bebbc93d0e81ef050b198..87ab19d74d06d59d56c7adfe46afe198c044c1ab 100644
--- a/src/structures/services/aptic-structures.service.ts
+++ b/src/structures/services/aptic-structures.service.ts
@@ -11,47 +11,63 @@ import { Structure, StructureDocument } from '../schemas/structure.schema';
 import { ApticStructure } from '../schemas/aptic-structure.schema';
 import { Address } from '../schemas/address.schema';
 import { UsersService } from '../../users/users.service';
+import { ApticCatalog } from '../schemas/aptic-catalog.schema';
+import { CategoriesFormationsService } from '../../categories/services/categories-formations.service';
 
 @Injectable()
 export class ApticStructuresService {
   constructor(
     private readonly httpService: HttpService,
     private readonly userService: UsersService,
+    private readonly categoriesFormationsService: CategoriesFormationsService,
     @InjectModel(Structure.name) private structureModel: Model<StructureDocument>
   ) {}
 
-  public formatApticStructures(postalCodeData: any[]): any {
+  /**
+   * Get all aptic structures in the given postal codes.
+   * @param postalCodeData
+   */
+  public formatApticStructures(postalCodeData: any[]): void {
     // Get all postal code in one array
-    const postalCodeArray = _.flatten(
-      postalCodeData.map((data) => {
-        return data.codesPostaux;
-      })
-    );
+    const postalCodeArray = _.flatten(postalCodeData.map((data) => data.codesPostaux));
 
     // Call APTIC Api's
-    const postalCodePromises = postalCodeArray.map((postalCode) => {
-      return this.getApticStructures(postalCode).toPromise();
-    });
-
-    Promise.all(postalCodePromises).then((data) => {
-      const structuresData = _.flatten(
-        data.map((tmp: { data }) => {
-          return tmp.data.data;
-        })
+    postalCodeArray.map((postalCode) => {
+      return this.getApticStructures(postalCode).subscribe(
+        (res) => {
+          res.data.presencePoints.forEach((structure) => {
+            // Call aptic api for offer
+            this.getApticStructureOffer(structure.catalogs[0]).subscribe(
+              (serviceData) => {
+                structure.services = serviceData.data.services;
+                // Create structure
+                this.createApticStructures(structure);
+              },
+              (err) => {
+                Logger.log(err);
+              }
+            );
+          });
+        },
+        (err) => {
+          Logger.log(`getApticStructures error on postal code: ${postalCode}. Code: ${err}`);
+        }
       );
-      // Create structures if possible
-      structuresData.forEach((structure) => this.createApticStructures(structure));
     });
   }
 
+  /**
+   * Create a structure for app database given an aptic structure
+   * @param structure ApticStructure
+   */
   private async createApticStructures(structure: ApticStructure): Promise<any> {
-    this.structureAlreadyExist(structure).then((exist) => {
+    this.structureAlreadyExist(structure).then(async (exist) => {
       if (!exist) {
-        Logger.log(`Create structure : ${structure.presence_name}`, 'ApticStructuresService - createApticStructures');
+        Logger.log(`Create structure : ${structure.name}`, 'ApticStructuresService - createApticStructures');
         const createdStructure = new this.structureModel();
         // Known fields
-        createdStructure.structureName = structure.presence_name;
-        createdStructure.contactPhone = structure.presence_phone;
+        createdStructure.structureName = structure.name;
+        createdStructure.contactPhone = structure.phone;
         // Unkown fields (but mandatory)
         createdStructure.contactMail = 'unknown@unknown.com';
         createdStructure.labelsQualifications = ['passNumerique'];
@@ -67,8 +83,14 @@ export class ApticStructuresService {
         createdStructure.nbTablets = null;
         createdStructure.nbNumericTerminal = null;
         // Address
-        createdStructure.coord = [structure.gps_lng, structure.gps_lat];
+        createdStructure.coord = [structure.address.gpsLng, structure.address.gpsLat];
         createdStructure.address = this.formatAddress(structure);
+        // Set structure offer
+        createdStructure.parentingHelp = await this.setModules(structure, 'parentingHelp');
+        createdStructure.baseSkills = await this.setModules(structure, 'baseSkills');
+        createdStructure.accessRight = await this.setModules(structure, 'accessRight');
+        createdStructure.socialAndProfessional = await this.setModules(structure, 'socialAndProfessional');
+        createdStructure.digitalCultureSecurity = await this.setModules(structure, 'digitalCultureSecurity');
         createdStructure.save();
         // Send admin weird structure mail
         this.verifyDuplication(createdStructure);
@@ -76,23 +98,74 @@ export class ApticStructuresService {
     });
   }
 
+  /**
+   * Given an aptic structure, this method return the corresponding services id by category.
+   * @param structure ApticStructure
+   * @param moduleCategory string
+   * @returns Promise<string[]>
+   */
+  private async setModules(structure: ApticStructure, moduleCategory: string): Promise<string[]> {
+    const referentialIds = await this.categoriesFormationsService
+      .findOne(moduleCategory)
+      .then((referential) => referential.modules.map((element) => element.id));
+
+    return structure.services
+      .filter((service) => referentialIds.includes(service.code))
+      .map((service) => {
+        return service.code;
+      });
+  }
+
+  /**
+   * Verifiy if an aptic structure already exist in database.
+   *  - If it's true return true and update fields if needed
+   *  - If it's false return false
+   * @param structure ApticStructure
+   * @returns boolean
+   */
   private async structureAlreadyExist(structure: ApticStructure): Promise<boolean> {
     let existingStructure = await this.structureModel
       .findOne({
-        structureName: { $regex: structure.presence_name, $options: 'i' },
+        structureName: { $regex: structure.name, $options: 'i' },
       })
       .exec();
     // Check without regex for case like 'TINEBRA*DANIEL/DANIEL/'
     if (!existingStructure) {
-      existingStructure = await this.structureModel.findOne({ structureName: structure.presence_name }).exec();
+      existingStructure = await this.structureModel.findOne({ structureName: structure.name }).exec();
     }
 
     if (existingStructure) {
       // Add aptic label if it's not the case
       if (!existingStructure.labelsQualifications.includes('passNumerique')) {
         existingStructure.labelsQualifications.push('passNumerique');
-        existingStructure.save();
       }
+      // Update service offer
+      existingStructure.parentingHelp = _.unionWith(
+        existingStructure.parentingHelp,
+        await this.setModules(structure, 'parentingHelp'),
+        _.isEqual
+      );
+      existingStructure.baseSkills = _.unionWith(
+        existingStructure.baseSkills,
+        await this.setModules(structure, 'baseSkills'),
+        _.isEqual
+      );
+      existingStructure.accessRight = _.unionWith(
+        existingStructure.accessRight,
+        await this.setModules(structure, 'accessRight'),
+        _.isEqual
+      );
+      existingStructure.socialAndProfessional = _.unionWith(
+        existingStructure.socialAndProfessional,
+        await this.setModules(structure, 'socialAndProfessional'),
+        _.isEqual
+      );
+      existingStructure.digitalCultureSecurity = _.unionWith(
+        existingStructure.digitalCultureSecurity,
+        await this.setModules(structure, 'digitalCultureSecurity'),
+        _.isEqual
+      );
+      existingStructure.save();
       return true;
     }
     return false;
@@ -128,11 +201,21 @@ export class ApticStructuresService {
     return this.httpService.get(encodeURI(req));
   }
 
-  public getApticStructures(postalCodeData: string): Observable<AxiosResponse<any>> {
+  public getApticStructures(postalCodeData: string): Observable<AxiosResponse<{ presencePoints: ApticStructure[] }>> {
+    const req = `https://aptisearch-api.aptic.fr/v1/postal-code/${postalCodeData}`;
+    Logger.log(`Request : ${req}`, 'ApticStructuresService');
+    return this.httpService.get(req, {
+      headers: {
+        api_key: process.env.APTIC_TOKEN,
+      },
+    });
+  }
+
+  public getApticStructureOffer(catalogId: string): Observable<AxiosResponse<{ services: ApticCatalog[] }>> {
     const agent = new https.Agent({
       rejectUnauthorized: false,
     });
-    const req = `https://presence.aptic.fr/postal_code/${postalCodeData}`;
+    const req = `https://aptisearch-api.aptic.fr/v1/catalog/${catalogId}/services`;
     Logger.log(`Request : ${req}`, 'ApticStructuresService');
     return this.httpService.get(req, {
       httpsAgent: agent,
@@ -161,13 +244,13 @@ export class ApticStructuresService {
     const address = new Address();
     const regexWithSpace = /\d+\s/g; // NOSONAR
     const regex = /\d+/g; // NOSONAR
-    if (structure.presence_address.match(regex)) {
-      address.numero = structure.presence_address.match(regex)[0];
-      address.street = structure.presence_address.replace(regexWithSpace, '');
+    if (structure.address.main.match(regex)) {
+      address.numero = structure.address.main.match(regex)[0];
+      address.street = structure.address.main.replace(regexWithSpace, '');
     } else {
-      address.street = structure.presence_address;
+      address.street = structure.address.main;
     }
-    address.commune = structure.city;
+    address.commune = structure.address.city;
     return address;
   }
 }
diff --git a/src/structures/services/structures.service.ts b/src/structures/services/structures.service.ts
index 5f1f1fec7da52bd97fd2e3f4c0091186d2123c01..71b6bb189b6aef32dcf401e1bfcdcebe08febdb9 100644
--- a/src/structures/services/structures.service.ts
+++ b/src/structures/services/structures.service.ts
@@ -1,6 +1,6 @@
 import { HttpException, HttpService, Injectable, HttpStatus, Logger } from '@nestjs/common';
 import { InjectModel } from '@nestjs/mongoose';
-import { Types, Model } from 'mongoose';
+import { Types, Model, FilterQuery, DocumentDefinition } from 'mongoose';
 import { Observable } from 'rxjs';
 import { AxiosResponse } from 'axios';
 import { Structure, StructureDocument } from '../schemas/structure.schema';
@@ -41,7 +41,7 @@ export class StructuresService {
     user.save();
 
     // Senc admin notification mail
-    this.userService.sendAdminNewStructureMail(createdStructure.structureName);
+    this.userService.sendAdminNewStructureMail(createdStructure.structureName, createdStructure._id);
 
     return createdStructure;
   }
@@ -58,7 +58,7 @@ export class StructuresService {
         .exec();
     } else if (filters) {
       return this.structureModel
-        .find({ $and: [{ $or: this.parseFilter(filters), deletedAt: { $exists: false }, accountVerified: true }] })
+        .find({ $and: [{ $and: this.parseFilter(filters), deletedAt: { $exists: false }, accountVerified: true }] })
         .exec();
     } else {
       return this.structureModel
@@ -162,7 +162,7 @@ export class StructuresService {
     const req =
       'https://download.data.grandlyon.com/geocoding/photon-bal/api?q=' +
       data.searchQuery +
-      '&lat=45.75&lon=4.85&lang=fr&limit=5&osm_tag=:!construction&osm_tag=:!bus_stop';
+      '&lat=45.75&lon=4.85&lang=fr&limit=50&osm_tag=:!construction&osm_tag=:!bus_stop';
     return new Promise((resolve, reject) => {
       this.httpService
         .request({
@@ -187,14 +187,29 @@ export class StructuresService {
    * @param key structure key
    * @return [{id: 'key', count: 'value'}]
    */
-  public async countByStructureKey(key: string): Promise<any> {
+  public async countByStructureKey(key: string, selected: { id: string; text: string }[]): Promise<any> {
     const uniqueElements = await this.structureModel.distinct(key).exec();
     return await Promise.all(
       uniqueElements.map(async (value) => {
+        const keyList: FilterQuery<DocumentDefinition<StructureDocument>>[] = [];
+        keyList.push({
+          [key]: { $elemMatch: { $eq: value } },
+          deletedAt: { $exists: false },
+        });
+        if (selected && selected.length > 0) {
+          for (const val of selected) {
+            keyList.push({
+              [val.text]: { $elemMatch: { $eq: val.id } },
+              deletedAt: { $exists: false },
+            });
+          }
+        }
         return {
           id: value,
           count: await this.structureModel
-            .countDocuments({ $and: [{ [key]: { $elemMatch: { $eq: value } }, deletedAt: { $exists: false } }] })
+            .countDocuments({
+              $and: keyList,
+            })
             .exec(),
         };
       })
@@ -316,7 +331,7 @@ export class StructuresService {
     return this.userService.getStructureOwners(structureId);
   }
 
-  public async updateAccountVerified(idStructure: string, emailUser: string): Promise<Structure> {
+  public async updateAccountVerified(idStructure: string): Promise<Structure> {
     const structureLinked = await this.findOne(idStructure);
     const structure = new this.structureModel(structureLinked);
     if (!structure) {
@@ -338,4 +353,31 @@ export class StructuresService {
     const owners = await this.userService.getStructureOwnersMails(idStructure, emailUser);
     return { structure: structure, owners: owners };
   }
+
+  public async reportStructureError(structureId: string, content: string) {
+    const structure = await this.findOne(structureId);
+    if (!structure) {
+      throw new HttpException('Invalid structure id', HttpStatus.BAD_REQUEST);
+    }
+    const owners = await this.userService.getStructureOwnersMails(structure._id, '');
+    const admins = await this.userService.getAdmins();
+    const emails = owners.map((owner) => owner.email).concat(admins.map((admin) => admin.email));
+    const uniqueEmails = [...new Set(emails)];
+
+    const emailsObject: { email: string }[] = uniqueEmails.map((item) => {
+      return { email: item };
+    });
+
+    const config = this.mailerService.config;
+    const ejsPath = this.mailerService.getTemplateLocation(config.templates.structureErrorReport.ejs);
+    const jsonConfig = this.mailerService.loadJsonConfig(config.templates.structureErrorReport.json);
+
+    const html = await ejs.renderFile(ejsPath, {
+      config,
+      content: content,
+      id: structure._id,
+      structureName: structure.structureName,
+    });
+    this.mailerService.send(emailsObject, jsonConfig.subject, html);
+  }
 }
diff --git a/src/structures/structures.controller.ts b/src/structures/structures.controller.ts
index b4aeea9bc83c7d3a6198af2d44909e4300b60f89..67e12c9a15469d46919601363dbfbe8882344fce 100644
--- a/src/structures/structures.controller.ts
+++ b/src/structures/structures.controller.ts
@@ -4,6 +4,7 @@ import {
   Delete,
   Get,
   HttpException,
+  HttpService,
   HttpStatus,
   Param,
   Post,
@@ -29,11 +30,28 @@ import { StructuresService } from './services/structures.service';
 @Controller('structures')
 export class StructuresController {
   constructor(
+    private readonly httpService: HttpService,
     private readonly structureService: StructuresService,
     private readonly userService: UsersService,
     private readonly tempUserService: TempUserService
   ) {}
 
+  /**
+   * Return points of given town exist.
+   * @param zipcode
+   * @returns Array of points
+   */
+  @Get('coordinates/:zipcode')
+  @ApiParam({ name: 'zipcode', type: String, required: true })
+  public async getCoordinates(@Param('zipcode') city: string): Promise<any> {
+    return await this.httpService
+      .get(encodeURI('https://download.data.grandlyon.com/geocoding/photon-bal/api?q=' + city))
+      .toPromise()
+      .then(async (res) => res.data.features)
+      .then((data) => data.filter((cityPoint) => cityPoint.properties.city.toLowerCase().includes(city.toLowerCase())))
+      .then((data) => data.map((filteredCityPoint) => filteredCityPoint.geometry.coordinates));
+  }
+
   @Post()
   public async create(@Body() createStructureDto: CreateStructureDto): Promise<Structure> {
     return this.structureService.create(createStructureDto.idUser, createStructureDto.structure);
@@ -45,11 +63,8 @@ export class StructuresController {
   }
 
   @Put('updateAfterOwnerVerify/:id')
-  public async updateAfterOwnerVerify(
-    @Param('id') id: string,
-    @Body() body: { emailUser: string }
-  ): Promise<Structure> {
-    return this.structureService.updateAccountVerified(id, body.emailUser);
+  public async updateAfterOwnerVerify(@Param('id') id: string): Promise<Structure> {
+    return this.structureService.updateAccountVerified(id);
   }
 
   @Put(':id')
@@ -74,22 +89,25 @@ export class StructuresController {
     return this.userService.updateStructureLinkedClaim(user.email, idStructure);
   }
 
-  @Get('count')
-  public async countCategories(): Promise<Array<{ id: string; count: number }>> {
+  @Post('count')
+  public async countCategories(
+    @Body()
+    selectedFilter: { id: string; text: string }[]
+  ): Promise<Array<{ id: string; count: number }>> {
     const data = await Promise.all([
-      this.structureService.countByStructureKey('proceduresAccompaniment'),
-
-      this.structureService.countByStructureKey('accessRight'),
-      this.structureService.countByStructureKey('baseSkills'),
-      this.structureService.countByStructureKey('parentingHelp'),
-      this.structureService.countByStructureKey('digitalCultureSecurity'),
-      this.structureService.countByStructureKey('socialAndProfessional'),
-
-      this.structureService.countByStructureKey('publicsAccompaniment'),
-      this.structureService.countByStructureKey('labelsQualifications'),
-      this.structureService.countByStructureKey('publics'),
-      this.structureService.countByStructureKey('accessModality'),
-      this.structureService.countByStructureKey('equipmentsAndServices'),
+      this.structureService.countByStructureKey('proceduresAccompaniment', selectedFilter),
+
+      this.structureService.countByStructureKey('accessRight', selectedFilter),
+      this.structureService.countByStructureKey('baseSkills', selectedFilter),
+      this.structureService.countByStructureKey('parentingHelp', selectedFilter),
+      this.structureService.countByStructureKey('digitalCultureSecurity', selectedFilter),
+      this.structureService.countByStructureKey('socialAndProfessional', selectedFilter),
+
+      this.structureService.countByStructureKey('publicsAccompaniment', selectedFilter),
+      this.structureService.countByStructureKey('labelsQualifications', selectedFilter),
+      this.structureService.countByStructureKey('publics', selectedFilter),
+      this.structureService.countByStructureKey('accessModality', selectedFilter),
+      this.structureService.countByStructureKey('equipmentsAndServices', selectedFilter),
     ]);
     // Return a concat of all arrays
     return data.reduce((a, b) => [...a, ...b]);
@@ -102,7 +120,12 @@ export class StructuresController {
 
   @Get(':id')
   public async find(@Param('id') id: string) {
-    return this.structureService.findOne(id);
+    const result = await this.structureService.findOne(id);
+    if (!result || result.deletedAt) {
+      throw new HttpException('Structure does not exist', HttpStatus.NOT_FOUND);
+    } else {
+      return result;
+    }
   }
 
   @Post(':id/withOwners')
@@ -217,4 +240,9 @@ export class StructuresController {
     }
     this.userService.removeFromStructureLinked(userFromDb.email, id);
   }
+
+  @Post('reportStructureError')
+  public async reportStructureError(@Body() data: { structureId: string; content: string }): Promise<void> {
+    return await this.structureService.reportStructureError(data.structureId, data.content);
+  }
 }
diff --git a/src/structures/structures.module.ts b/src/structures/structures.module.ts
index 51ec4d11ca7c6d9640944f69b156cfaac87f1aa3..1445999d289ff826e1504e3754d814f10ed06322 100644
--- a/src/structures/structures.module.ts
+++ b/src/structures/structures.module.ts
@@ -10,6 +10,7 @@ import { ApticStructuresService } from './services/aptic-structures.service';
 import { StructureTypeController } from './structure-type/structure-type.controller';
 import { StructureTypeService } from './structure-type/structure-type.service';
 import { StructureType, StructureTypeSchema } from './structure-type/structure-type.schema';
+import { CategoriesModule } from '../categories/categories.module';
 
 @Module({
   imports: [
@@ -20,6 +21,7 @@ import { StructureType, StructureTypeSchema } from './structure-type/structure-t
     HttpModule,
     MailerModule,
     forwardRef(() => UsersModule),
+    CategoriesModule,
     TempUserModule,
   ],
   controllers: [StructuresController, StructureTypeController],
diff --git a/src/users/users.service.ts b/src/users/users.service.ts
index e4a081cb3fb15b0d1f88bb68abb706069729be9e..00cb1129c062731347632cc2c3b64359a63e9839 100644
--- a/src/users/users.service.ts
+++ b/src/users/users.service.ts
@@ -159,7 +159,7 @@ export class UsersService {
   /**
    * Send to all admins notification email for new structures
    */
-  public async sendAdminNewStructureMail(structureName: string): Promise<any> {
+  public async sendAdminNewStructureMail(structureName: string, structureId: string): Promise<any> {
     const config = this.mailerService.config;
     const ejsPath = this.mailerService.getTemplateLocation(config.templates.adminStructureCreate.ejs);
     const jsonConfig = this.mailerService.loadJsonConfig(config.templates.adminStructureCreate.json);
@@ -167,6 +167,7 @@ export class UsersService {
     const html = await ejs.renderFile(ejsPath, {
       config,
       name: structureName,
+      id: structureId,
     });
     const admins = await this.getAdmins();
     admins.forEach((admin) => {