diff --git a/package-lock.json b/package-lock.json
index 3a9b022857765131e6a81f0c1973ef3a366285c7..e43f775d70e72e5ae2030c3079c539d465b56af1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5454,15 +5454,6 @@
         "@types/node": "*"
       }
     },
-    "@types/bson": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz",
-      "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==",
-      "dev": true,
-      "requires": {
-        "@types/node": "*"
-      }
-    },
     "@types/connect": {
       "version": "3.4.33",
       "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
@@ -5602,26 +5593,6 @@
       "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==",
       "dev": true
     },
-    "@types/mongodb": {
-      "version": "3.5.34",
-      "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.34.tgz",
-      "integrity": "sha512-73iy3+MiH+wxSM+hVA5jcW9ZTUaor2WKvM7hW+htOSgVb7E6/JBHOWaxj7rL1/vaxEBziKRr/VPecy3YAKqLuQ==",
-      "dev": true,
-      "requires": {
-        "@types/bson": "*",
-        "@types/node": "*"
-      }
-    },
-    "@types/mongoose": {
-      "version": "5.10.1",
-      "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.10.1.tgz",
-      "integrity": "sha512-5yqbLHOyCQhUb7GPGW0A2dauUbhwgBvUWMzYcaUQiHdLZ8slgRp2R6i8FETZ+t5xeXpfhylYp9U7dAng7WamqQ==",
-      "dev": true,
-      "requires": {
-        "@types/mongodb": "*",
-        "@types/node": "*"
-      }
-    },
     "@types/node": {
       "version": "14.14.6",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.6.tgz",
@@ -15103,6 +15074,11 @@
       "resolved": "https://registry.npmjs.org/slug/-/slug-5.3.0.tgz",
       "integrity": "sha512-h7yD2UDVyMcQRv/WLSjq7HDH6ToO/22MB381zfx6/ebtdWUlGcyxpJNVHl6WFvKjIMHf5ZxANFp/srsy4mfT/w=="
     },
+    "slugify": {
+      "version": "1.6.5",
+      "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.5.tgz",
+      "integrity": "sha512-8mo9bslnBO3tr5PEVFzMPIWwWnipGS0xVbYf65zxDqfNwmzYn1LpiKNrR6DlClusuvo+hDHd1zKpmfAe83NQSQ=="
+    },
     "smart-buffer": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
diff --git a/package.json b/package.json
index de7bd8d23b4c0ec75812c67592a7e0d346db95fa..7be2075c1ec1b18f00741d3d1484e6aa9cd419a6 100644
--- a/package.json
+++ b/package.json
@@ -60,6 +60,7 @@
     "rimraf": "^3.0.2",
     "rxjs": "^7.5.5",
     "sanitize-html": "^2.7.3",
+    "slugify": "^1.6.5",
     "swagger-ui-express": "^4.6.0"
   },
   "devDependencies": {
diff --git a/scripts/data/structures.js b/scripts/data/structures.js
index 72de0768adcd26c62562c8bd701b4135aac2a319..9de10021f046f404cc0eb4673f5b53e6022b8456 100644
--- a/scripts/data/structures.js
+++ b/scripts/data/structures.js
@@ -141,6 +141,7 @@ module.exports = {
         street: 'Rue de la Mairie',
         commune: 'Feyzin',
         postcode: '69320',
+        inseeCode: '69291',
       },
       accountVerified: true,
       coord: [4.8563106, 45.6693619],
@@ -257,7 +258,8 @@ module.exports = {
         numero: '27',
         street: 'Avenue de la République',
         commune: 'Vénissieux',
-        postcode: '69259',
+        postcode: '69200',
+        inseeCode: '69259',
       },
       accountVerified: true,
       coord: [4.8678548, 45.7075853],
@@ -305,6 +307,7 @@ module.exports = {
         street: 'Rue Saint Polycarpe',
         commune: 'Lyon 1er',
         postcode: '69001',
+        inseeCode: '69381',
       },
       hours: {
         monday: {
@@ -465,7 +468,8 @@ module.exports = {
         numero: '30 bis',
         street: 'Avenue Leclerc',
         commune: 'Rillieux-la-Pape',
-        postcode: '69286',
+        postcode: '69140',
+        inseeCode: '69286',
       },
       coord: [4.9036773, 45.8142196],
       accountVerified: true,
@@ -590,6 +594,7 @@ module.exports = {
         street: 'Avenue Franklin Roosevelt',
         commune: 'Bron',
         postcode: '69500',
+        inseeCode: '69029',
       },
       coord: [4.9149121, 45.7311648],
       accountVerified: true,
@@ -693,6 +698,7 @@ module.exports = {
         street: 'Rue Louis Normand',
         commune: 'Oullins',
         postcode: '69600',
+        inseeCode: '69149',
       },
       coord: [4.8188378, 45.7157896],
       accountVerified: true,
@@ -803,7 +809,8 @@ module.exports = {
         numero: '7',
         street: 'Rue Robert et Reynier ',
         commune: 'Saint-Fons',
-        postcode: '69199',
+        postcode: '69190',
+        inseeCode: '69199',
       },
       coord: [4.8608585, 45.7086482],
       accountVerified: true,
@@ -880,6 +887,7 @@ module.exports = {
         street: 'Avenue Général Frère',
         commune: 'Lyon 8e',
         postcode: '69008',
+        inseeCode: '69388',
       },
       createdAt: '2020-11-13T14:13:00.000Z',
       updatedAt: '2022-04-13T14:13:00.000Z',
@@ -948,6 +956,7 @@ module.exports = {
         street: 'Rue Neuve',
         commune: 'Fleurieu-sur-Saône',
         postcode: '69250',
+        inseeCode: '69085',
       },
       createdAt: '2022-01-13T14:13:00.000Z',
       updatedAt: '2022-02-13T14:13:00.000Z',
diff --git a/scripts/init-ctm-territories.js b/scripts/init-ctm-territories.js
new file mode 100644
index 0000000000000000000000000000000000000000..3f5fabf6194f15b9e71b5740602b03339574d22f
--- /dev/null
+++ b/scripts/init-ctm-territories.js
@@ -0,0 +1,30 @@
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const axios = require('axios');
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const path = require('path');
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
+
+axios
+  .post('http://localhost:3000/api/auth/login', {
+    email: 'admin@admin.com',
+    password: process.env.USER_PWD,
+  })
+  .then((res) => {
+    const config = {
+      headers: { Authorization: `Bearer ${res.data.accessToken}` },
+    };
+    axios
+      .get('http://localhost:3000/api/structures/ctm/update', config)
+      .then((res) => {
+        console.log(`CTM territories | statusCode: ${res.status}`);
+      })
+      .catch((error) => {
+        console.error('Error in fetching CTM territories');
+        console.error(error);
+      });
+  })
+  .catch((error) => {
+    console.error('Error in auth');
+    console.error(error);
+  });
diff --git a/src/categories/categories.module.ts b/src/categories/categories.module.ts
index 3a02d98d3d166462090080fd68034a4aa36e5d18..63bb708ba5ef0a0b8b831ef46c6133d2ce1e2e26 100644
--- a/src/categories/categories.module.ts
+++ b/src/categories/categories.module.ts
@@ -3,9 +3,10 @@ import { MongooseModule } from '@nestjs/mongoose';
 import { CategoriesService } from './services/categories.service';
 import { Categories, CategoriesSchema } from './schemas/categories.schema';
 import { CategoriesController } from './controllers/categories.controller';
+import { HttpModule } from '@nestjs/axios';
 
 @Module({
-  imports: [MongooseModule.forFeature([{ name: Categories.name, schema: CategoriesSchema }])],
+  imports: [MongooseModule.forFeature([{ name: Categories.name, schema: CategoriesSchema }]), HttpModule],
   controllers: [CategoriesController],
   exports: [CategoriesService],
   providers: [CategoriesService],
diff --git a/src/categories/schemas/module.class.ts b/src/categories/schemas/module.class.ts
index 15c6b2a4d3695edc8a0dff69346668a03532ff7b..66dc8264caebd2dea200891087f2251d64cbc947 100644
--- a/src/categories/schemas/module.class.ts
+++ b/src/categories/schemas/module.class.ts
@@ -2,4 +2,5 @@ export class Module {
   id: string;
   name: string;
   apticIds?: string[];
+  communes?: string[];
 }
diff --git a/src/categories/services/categories.service.spec.ts b/src/categories/services/categories.service.spec.ts
index 227d0a000ae911b91054fc5cad65dff2203e13c3..ee2726837903a0a80738dc1120834022e032fa51 100644
--- a/src/categories/services/categories.service.spec.ts
+++ b/src/categories/services/categories.service.spec.ts
@@ -1,3 +1,4 @@
+import { HttpModule } from '@nestjs/axios';
 import { getModelToken } from '@nestjs/mongoose';
 import { Test, TestingModule } from '@nestjs/testing';
 import { CreateCategories } from '../dto/create-categories.dto';
@@ -16,7 +17,7 @@ describe('CategoriesService', () => {
 
   beforeEach(async () => {
     const module: TestingModule = await Test.createTestingModule({
-      imports: [],
+      imports: [HttpModule],
       providers: [
         CategoriesService,
         {
diff --git a/src/categories/services/categories.service.ts b/src/categories/services/categories.service.ts
index 6e331f8cb5a0188d3d9d773c0b8fbdd411445405..8b63e7cec2b42273139ae5c1b02e6dfa4df35c00 100644
--- a/src/categories/services/categories.service.ts
+++ b/src/categories/services/categories.service.ts
@@ -1,12 +1,19 @@
+import { HttpService } from '@nestjs/axios';
 import { Injectable, Logger } from '@nestjs/common';
 import { InjectModel } from '@nestjs/mongoose';
 import { Model } from 'mongoose';
+import { lastValueFrom } from 'rxjs';
+import slugify from 'slugify';
 import { Categories, CategoriesDocument } from '../schemas/categories.schema';
+import { Module } from '../schemas/module.class';
 
 @Injectable()
 export class CategoriesService {
   private readonly logger = new Logger(CategoriesService.name);
-  constructor(@InjectModel(Categories.name) private categoriesModel: Model<CategoriesDocument>) {}
+  constructor(
+    @InjectModel(Categories.name) private categoriesModel: Model<CategoriesDocument>,
+    private httpService: HttpService
+  ) {}
 
   public async findAll(): Promise<Categories[]> {
     this.logger.debug(`findAll`);
@@ -22,4 +29,40 @@ export class CategoriesService {
     this.logger.debug(`findOneComplete`);
     return this.categoriesModel.findOne({ id: categoryId }).exec();
   }
+
+  public async findCtmByInseeCode(inseeCode: string): Promise<Module> {
+    const ctm = await this.findOneComplete('ctm');
+    return ctm.modules.find((ctm) => ctm.communes.includes(inseeCode));
+  }
+
+  /**
+   * Clear 'categories.ctm' and fill it with data from Data Grand Lyon
+   */
+  public async updateCTM(): Promise<Categories> {
+    this.logger.debug('updateCTM');
+    const dataCTMs = await lastValueFrom(
+      this.httpService.get(
+        `https://download.data.grandlyon.com/ws/grandlyon/ter_territoire.direction_territoriale/all.json?maxfeatures=-1`
+      )
+    ).then(async (res) => res.data.values);
+    dataCTMs.sort((a, b) => a.nom.localeCompare(b.nom));
+
+    const newDocCTM: Categories = {
+      modules: [],
+      name: 'Territoires métropolitains',
+      theme: 'Territoires métropolitains',
+      id: 'ctm',
+    };
+
+    for (const ctm of dataCTMs) {
+      newDocCTM.modules.push({
+        id: slugify(ctm.nom, { lower: true, strict: true, locale: 'fr' }),
+        name: ctm.nom,
+        communes: ctm.communes,
+      });
+    }
+
+    await this.categoriesModel.updateOne({ id: 'ctm' }, [{ $set: newDocCTM }], { upsert: true });
+    return newDocCTM;
+  }
 }
diff --git a/src/structures/schemas/address.schema.ts b/src/structures/schemas/address.schema.ts
index 7e822db4c5eb894d595701ec1ab6afb998bcce29..633146267703638c4b51d6dee77d13fcf24938ae 100644
--- a/src/structures/schemas/address.schema.ts
+++ b/src/structures/schemas/address.schema.ts
@@ -13,6 +13,17 @@ export class Address {
   commune: string;
 
   postcode: string;
+  inseeCode: string;
+
+  static equals = function (a: Address, b: Address) {
+    // ignore inseeCode if not both set
+    return (
+      a.numero === b.numero &&
+      a.street === b.street &&
+      a.commune === b.commune &&
+      (a.inseeCode && b.inseeCode ? a.inseeCode === b.inseeCode : true)
+    );
+  };
 }
 
 export const AddressSchema = SchemaFactory.createForClass(Address);
diff --git a/src/structures/services/structures.service.spec.ts b/src/structures/services/structures.service.spec.ts
index e638eca20b335edd8cf9e36189c27d1e8d527065..45e18e3a84f2d00b6b7ec9b62a440f4e990937f6 100644
--- a/src/structures/services/structures.service.spec.ts
+++ b/src/structures/services/structures.service.spec.ts
@@ -159,7 +159,8 @@ const structuresSearchServiceMock = {
         numero: '30 bis',
         street: 'Avenue Leclerc',
         commune: 'Rillieux-la-Pape',
-        postcode: '69250',
+        postcode: '69140',
+        inseeCode: '69286',
       },
       coord: [4.9036773, 45.8142196],
       accountVerified: true,
@@ -350,7 +351,8 @@ describe('StructuresService', () => {
           numero: '30 bis',
           street: 'Avenue Leclerc',
           commune: 'Rillieux-la-Pape',
-          postcode: '69250',
+          postcode: '69140',
+          inseeCode: '69286',
         },
         coord: [4.9036773, 45.8142196],
         accountVerified: true,
diff --git a/src/structures/services/structures.service.ts b/src/structures/services/structures.service.ts
index 28b80fd52d6bdd261a48166fa6b53d7e6e8ee82f..ae063bcabc9127bf5f2dfd433ccac148b133e7fc 100644
--- a/src/structures/services/structures.service.ts
+++ b/src/structures/services/structures.service.ts
@@ -7,7 +7,7 @@ import * as ejs from 'ejs';
 import * as _ from 'lodash';
 import { DateTime } from 'luxon';
 import { DocumentDefinition, FilterQuery, Model, Types } from 'mongoose';
-import { map, Observable, tap } from 'rxjs';
+import { lastValueFrom, map, Observable, tap } from 'rxjs';
 import { PendingStructureDto } from '../../admin/dto/pending-structure.dto';
 import { UnclaimedStructureDto } from '../../admin/dto/unclaimed-structure-dto';
 import { Categories } from '../../categories/schemas/categories.schema';
@@ -24,6 +24,7 @@ import { StructureDto } from '../dto/structure.dto';
 import { UpdateStructureDto } from '../dto/update-structure.dto';
 import { EquipmentsServicesEnum } from '../enum/equipmentsServices';
 import { CNFSStructure } from '../interfaces/cnfs-structure.interface';
+import { Address } from '../schemas/address.schema';
 import { Structure, StructureDocument } from '../schemas/structure.schema';
 import { StructuresSearchService } from './structures-search.service';
 
@@ -46,17 +47,6 @@ export class StructuresService {
     return this.populateES();
   }
 
-  //TODO: refactor
-  public fillFilters(filters: Array<any>): Promise<any[]>[] {
-    return filters?.map(async (elem) => {
-      const key = Object.keys(elem)[0];
-      const modules = (await this.categoriesService.findOneComplete(elem[key])).modules;
-      return modules.map((module) => {
-        return { [elem[key]]: module.id };
-      });
-    });
-  }
-
   async searchForStructures(
     text: string,
     filters?: Array<any>,
@@ -69,47 +59,41 @@ export class StructuresService {
     const results = await this.structuresSearchService.search(text, fields);
     const ids = results.map((result) => result.structureId);
     let structures;
-    let multipleFilters = filters ? filters.filter((elem) => Object.keys(elem)[0]?.length == 0) : null;
-    filters = filters?.filter((elem) => {
-      return Object.keys(elem)[0]?.length != 0;
-    });
+    const andFilters = filters ? filters[0] : null;
+    const orFilters = filters ? filters[1] : null;
 
-    //TODO: remove useless code ?
-    if (multipleFilters) {
-      const filtersArrays = await Promise.all(this.fillFilters(multipleFilters));
-      multipleFilters = [...filtersArrays];
-    }
     if (!ids.length) {
       return [];
     }
 
     // we match ids from Elasticsearch with ids from mongoDB (and filters) and sort the result according to ElasticSearch order.
-    if (filters?.length > 0 && multipleFilters?.length == 0) {
+    if (andFilters?.length > 0 && orFilters?.length == 0) {
       structures = await this.structureModel
         .find({
           _id: { $in: ids },
-          $and: [...this.parseFilter(filters), { deletedAt: { $exists: false }, accountVerified: true }],
+          $and: [...this.parseFilter(andFilters), { deletedAt: { $exists: false }, accountVerified: true }],
         })
         .populate('personalOffers')
         .populate('structureType')
         .limit(limit)
         .exec();
-    } else if (filters?.length > 0 && multipleFilters?.length > 0) {
+    } else if (andFilters?.length > 0 && orFilters?.length > 0) {
       structures = await this.structureModel
         .find({
           _id: { $in: ids },
-          $or: [...this.parseFilter(multipleFilters)],
-          $and: [...this.parseFilter(filters), { deletedAt: { $exists: false }, accountVerified: true }],
+          $or: [...this.parseFilter(orFilters)],
+          $and: [...this.parseFilter(andFilters), { deletedAt: { $exists: false }, accountVerified: true }],
         })
         .populate('personalOffers')
         .populate('structureType')
         .limit(limit)
         .exec();
-    } else if (filters?.length == 0 && multipleFilters?.length > 0) {
+    } else if (andFilters?.length == 0 && orFilters?.length > 0) {
       structures = await this.structureModel
         .find({
           _id: { $in: ids },
-          $or: [...this.parseFilter(multipleFilters), { deletedAt: { $exists: false }, accountVerified: true }],
+          $or: [...this.parseFilter(orFilters)],
+          $and: [{ deletedAt: { $exists: false }, accountVerified: true }],
         })
         .populate('personalOffers')
         .populate('structureType')
@@ -140,19 +124,13 @@ export class StructuresService {
     structure.accountVerified = true;
     const createdStructure = new this.structureModel(structure);
     createdStructure._id = new Types.ObjectId();
+    createdStructure.structureName = createdStructure.structureName.trim();
     createdStructure.categories.selfServiceMaterial = this.getSelfServiceMaterial(createdStructure);
     await createdStructure.save();
-    await this.getStructurePosition(createdStructure).then(async (position: StructureDocument) => {
+    await this.getStructurePosition(createdStructure).then(async (structureWithPosition: StructureDocument) => {
       return this.structuresSearchService.indexStructure(
         await this.structureModel
-          .findByIdAndUpdate(
-            new Types.ObjectId(createdStructure._id),
-            {
-              address: position.address,
-              coord: position.coord,
-            },
-            { new: true }
-          )
+          .findByIdAndUpdate(new Types.ObjectId(createdStructure._id), structureWithPosition, { new: true })
           .populate('personalOffers')
           .populate('structureType')
           .exec()
@@ -334,8 +312,15 @@ export class StructuresService {
   public async update(idStructure: string, updatedFields: UpdateStructureDto): Promise<Structure> {
     this.logger.debug(`Updating structure ${idStructure}`);
     const oldStructure = await this.findOne(idStructure);
-    const oldCategories = { ...oldStructure.categories };
+
+    // Update position only if address has changed (to not erase coord which may have been set manually in database)
+    if (updatedFields.address && !Address.equals(updatedFields.address, oldStructure.address)) {
+      oldStructure.address = updatedFields.address;
+      await this.getStructurePosition(oldStructure);
+    }
+
     // Store updated categories because it will be erased by Object.assign which is a shallow copy
+    const oldCategories = { ...oldStructure.categories };
     const updatedCategories = { ...updatedFields.categories };
     const deepClone: StructureDocument = Object.assign(oldStructure, updatedFields);
     // Update structure categories in order to not override it
@@ -346,8 +331,8 @@ export class StructuresService {
       });
     }
 
-    if (updatedFields.address) {
-      await this.getStructurePosition(deepClone);
+    if (updatedFields.structureName) {
+      deepClone.structureName = deepClone.structureName.trim();
     }
     const parsedStructure = deepClone;
     parsedStructure.categories.selfServiceMaterial = this.getSelfServiceMaterial(deepClone);
@@ -379,65 +364,101 @@ export class StructuresService {
   /**
    * Get structures positions and add marker corresponding to those positons on the map
    */
-  private getStructurePosition(structure: Structure): Promise<Structure> {
+  private async getStructurePosition(structure: Structure): Promise<Structure> {
     this.logger.debug('getStructurePosition');
-    return new Promise((resolve, reject) => {
-      this.getCoord(structure.address.numero, structure.address.street, structure.address.commune, 'photon').subscribe(
-        (res) => {
-          this.getCoord(
-            structure.address.numero,
-            structure.address.street,
-            structure.address.commune,
-            'photon-bal'
-          ).subscribe((resbal) => {
-            // check if photon-bal is more precise than photon ban
-            if (resbal.data.features.length > 0) {
-              resbal.data.features = resbal.data.features.sort((_a, b) => {
-                return b.properties.housenumber ? 1 : -1;
-              });
-              const address = resbal.data.features[0];
-              if (address && address.geometry) {
-                structure.coord = address.geometry.coordinates;
-                structure.address.postcode = address.properties.postcode;
-              } else {
-                this.logger.error(
-                  `No coord found for: ${structure.address.numero} ${structure.address.street} ${structure.address.commune}`,
-                  StructuresService.name
-                );
-                structure.coord = [];
-              }
-              resolve(structure);
-              // else pick structure from photonban if it exists or throw error
-            } else {
-              if (res.data.features.length > 0) {
-                const address = res.data.features[0];
-                if (address && address.geometry) {
-                  structure.coord = address.geometry.coordinates;
-                  structure.address.postcode = address.properties.postcode;
-                } else {
-                  this.logger.error(
-                    `No coord found for: ${structure.address.numero} ${structure.address.street} ${structure.address.commune}`,
-                    StructuresService.name
-                  );
-                  structure.coord = [];
-                }
-                resolve(structure);
-              } else {
-                this.logger.error(
-                  `No structure found for: ${structure.address.numero} ${structure.address.street} ${structure.address.commune}`,
-                  StructuresService.name
-                );
-              }
-            }
-          });
-        },
-        (err) => {
-          this.logger.error(`Request error: ${err.config.url}`, 'StructureService');
-          this.logger.error(err);
-          reject(err);
-        }
+    try {
+      let address: {
+        geometry: {
+          coordinates: [number, number];
+        };
+        properties: {
+          [key: string]: string;
+        };
+      };
+      // check if photon-bal is more precise than photon ban
+      const photonBalCoord = await this.getCoord(
+        structure.address.numero || '',
+        structure.address.street,
+        structure.address.commune,
+        'photon-bal'
       );
-    });
+      if (photonBalCoord.data.features.length > 0) {
+        address = photonBalCoord.data.features.find((feature) => feature.properties.hasOwnProperty('housenumber'));
+        if (!address) {
+          address = photonBalCoord.data.features[0];
+        }
+      } else {
+        // else pick structure from photonban if it exists or log error
+        const photonCoord = await this.getCoord(
+          structure.address.numero || '',
+          structure.address.street,
+          structure.address.commune,
+          'photon'
+        );
+        address = photonCoord.data.features.find((feature) => feature.properties.hasOwnProperty('housenumber'));
+      }
+
+      if (!address || !address.geometry) {
+        this.logger.error(
+          `No coord found for: ${structure.address.numero || ''} ${structure.address.street} ${
+            structure.address.commune
+          }`,
+          StructuresService.name
+        );
+        structure.coord = [];
+      } else {
+        structure.coord = address.geometry.coordinates;
+        structure.address.postcode = address.properties.postcode;
+      }
+
+      // set territory for CTM filter
+      await this.setCtmTerritory(structure);
+    } catch (err) {
+      this.logger.error(err);
+    }
+
+    return structure;
+  }
+
+  /**
+   * Get structure territory
+   */
+  private async setCtmTerritory(structure: Structure): Promise<Structure> {
+    structure.categories['ctm'] = [];
+    if (structure.address?.postcode) {
+      let inseeCode: string = null;
+      const req = `https://apicarto.ign.fr/api/codes-postaux/communes/${structure.address.postcode}`;
+      this.logger.debug(`Request : ${req}`);
+
+      const res = await lastValueFrom(this.httpService.get(encodeURI(req)));
+      if (res.data?.length) {
+        if (res.data.length == 1) {
+          this.logger.debug(`Insee code ${res.data[0].codeCommune} for ${structure.address.commune}`);
+          inseeCode = res.data[0].codeCommune;
+        } else {
+          // if several commune for this postcode, find which commune we are looking for the insee code
+          const communes = res.data.filter((commune) => commune.nomCommune == structure.address.commune);
+          if (communes.length) {
+            this.logger.debug(`Insee code ${communes[0].codeCommune} for ${structure.address.commune}`);
+            inseeCode = res.data[0].codeCommune;
+          } else {
+            this.logger.warn(
+              `Commune not found for ${structure.address.commune} in ${JSON.stringify(res.data)} (${req})`
+            );
+          }
+        }
+      }
+
+      if (inseeCode) {
+        structure.address.inseeCode = inseeCode;
+        const ctm: Module = await this.categoriesService.findCtmByInseeCode(inseeCode);
+        if (ctm) {
+          this.logger.debug(`${structure.structureName} : CTM ${ctm.id}`);
+          structure.categories['ctm'] = [ctm.id];
+        }
+      }
+    }
+    return structure;
   }
 
   public async isClaimed(structureId: string, user: User): Promise<boolean> {
@@ -564,12 +585,12 @@ export class StructuresService {
     );
   }
 
-  public getCoord(
+  public async getCoord(
     numero: string,
     address: string,
     commune: string,
     scope: string
-  ): Observable<AxiosResponse<PhotonResponse>> {
+  ): Promise<AxiosResponse<PhotonResponse>> {
     const req =
       `https://download.data.grandlyon.com/geocoding/${scope}/api?q=` +
       (numero == null ? '' : numero + ' ') +
@@ -578,7 +599,7 @@ export class StructuresService {
       commune;
     this.logger.debug('Print getCoord ' + req);
 
-    return this.httpService.get(encodeURI(req));
+    return await lastValueFrom(this.httpService.get(encodeURI(req)));
   }
 
   /**
@@ -1112,4 +1133,18 @@ export class StructuresService {
       .populate('personalOffers')
       .exec();
   }
+
+  /**
+   * Set structure CTM Territory with data from Data Grand Lyon
+   */
+  public async setCTMs(): Promise<void> {
+    this.logger.debug('setCTMs');
+    const structures: StructureDocument[] = await this.findAll();
+    for (const structure of structures) {
+      this.setCtmTerritory(structure).then(async (updatedStructure) => {
+        await this.structureModel.findByIdAndUpdate(new Types.ObjectId(structure._id), updatedStructure).exec();
+      });
+    }
+    return null;
+  }
 }
diff --git a/src/structures/structures.controller.ts b/src/structures/structures.controller.ts
index 19f13b6fb8deac03871d8bed25557588f544a56c..6b7e1304339022e7747bf5e557def4deccd487e3 100644
--- a/src/structures/structures.controller.ts
+++ b/src/structures/structures.controller.ts
@@ -15,7 +15,7 @@ import {
   Request,
   UseGuards,
 } from '@nestjs/common';
-import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
+import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
 import { Types } from 'mongoose';
 import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
 import { CategoriesService } from '../categories/services/categories.service';
@@ -177,6 +177,22 @@ export class StructuresController {
     return this.tempUserService.getStructureTempUsers(id);
   }
 
+  @ApiOperation({
+    description: `Mettre à jour les territoires CTM des structures à partir de Data Grand Lyon`,
+  })
+  @ApiResponse({
+    status: 204,
+    description: 'The CTM territories have been updated successfully.',
+  })
+  @Get('/ctm/update')
+  @UseGuards(JwtAuthGuard, RolesGuard)
+  @Roles('admin')
+  public async updateCTM(): Promise<void> {
+    this.logger.debug('updateCTM');
+    await this.categoriesService.updateCTM();
+    return this.structureService.setCTMs();
+  }
+
   @Delete(':id')
   @UseGuards(JwtAuthGuard, IsStructureOwnerGuard)
   @ApiParam({ name: 'id', type: String, required: true })
diff --git a/test/mock/data/structures.mock.data.ts b/test/mock/data/structures.mock.data.ts
index bbd943cacbdfbf9954367c9c4bb28b70f6ffc119..6c15cd6dec556e8ddec133e828946f82b91f6bd9 100644
--- a/test/mock/data/structures.mock.data.ts
+++ b/test/mock/data/structures.mock.data.ts
@@ -19,6 +19,7 @@ export const structuresDocumentDataMock: StructureDocument[] = [
       street: 'Rue Alphonse Daudet',
       commune: 'Lyon 7ème Arrondissement',
       postcode: '69007',
+      inseeCode: '69387',
     },
     contactMail: '',
     contactPhone: '',
@@ -95,6 +96,7 @@ export const structuresDocumentDataMock: StructureDocument[] = [
       street: 'Rue Alphonse Daudet',
       commune: 'Lyon 7ème Arrondissement',
       postcode: '69007',
+      inseeCode: '69387',
     },
     contactMail: '',
     contactPhone: '',
@@ -179,6 +181,7 @@ export const structureMockDto: StructureDto = {
     street: 'Rue Alphonse Daudet',
     commune: 'Lyon 7ème Arrondissement',
     postcode: '69007',
+    inseeCode: '69387',
   },
   contactMail: '',
   contactPhone: '',
diff --git a/test/mock/services/structures-for-search.mock.service.ts b/test/mock/services/structures-for-search.mock.service.ts
index 816f18da0fa1e046995b76ee610ed364ed5f99af..6b0b5de417f25a08b92fc195e58ba9113287627b 100644
--- a/test/mock/services/structures-for-search.mock.service.ts
+++ b/test/mock/services/structures-for-search.mock.service.ts
@@ -10,6 +10,7 @@ export class StructuresForSearchServiceMock {
           street: 'Avenue Edouard Aynard',
           commune: 'Écully',
           postcode: '69130',
+          inseeCode: '69081',
         },
         nbPrinters: 1,
         description:
@@ -24,6 +25,7 @@ export class StructuresForSearchServiceMock {
           street: " Place de l'Abbe Launay",
           commune: 'Grézieu-la-Varenne',
           postcode: '69290',
+          inseeCode: '69094',
         },
         nbPrinters: 1,
         description: null,
@@ -37,6 +39,7 @@ export class StructuresForSearchServiceMock {
           street: 'Place de la Mairie',
           commune: 'La Tour-de-Salvagny',
           postcode: '69890',
+          inseeCode: '69250',
         },
         nbPrinters: 1,
         description: null,
@@ -50,6 +53,7 @@ export class StructuresForSearchServiceMock {
           street: 'Chemin Jean-Marie Vianney',
           commune: 'Écully',
           postcode: '69130',
+          inseeCode: '69081',
         },
         nbPrinters: 1,
         description: null,
@@ -63,6 +67,7 @@ export class StructuresForSearchServiceMock {
           street: 'Rue Tupin',
           commune: 'Oullins',
           postcode: '69600',
+          inseeCode: '69149',
         },
         description: null,
         nbPrinters: 1,
@@ -76,7 +81,8 @@ export class StructuresForSearchServiceMock {
           numero: '7',
           street: 'Rue Robert et Reynier ',
           commune: 'Saint-Fons',
-          postcode: '69199',
+          postcode: '69190',
+          inseeCode: '69199',
         },
         nbScanners: 1,
         description:
@@ -91,6 +97,7 @@ export class StructuresForSearchServiceMock {
           street: 'a Rue du Mai 1945',
           commune: 'Villeurbanne',
           postcode: '69100',
+          inseeCode: '69266',
         },
         nbScanners: 1,
         description: "Notre rôle est de faciliter l'accès des personnes aux services nécessaires à la vie quotidienne",
diff --git a/test/mock/services/structures.mock.service.ts b/test/mock/services/structures.mock.service.ts
index 9dd4e40a76a99096d4aa89ea11725e4fd1066a08..421f379d32e37b4362343ed3bbd59a906d2d2c30 100644
--- a/test/mock/services/structures.mock.service.ts
+++ b/test/mock/services/structures.mock.service.ts
@@ -58,6 +58,7 @@ export class StructuresServiceMock {
           street: 'Rue Alphonse Daudet',
           commune: 'Lyon 7ème Arrondissement',
           postcode: '69007',
+          inseeCode: '69387',
         },
         contactMail: '',
         contactPhone: '',
@@ -249,7 +250,8 @@ export class StructuresServiceMock {
           numero: '30 bis',
           street: 'Avenue Leclerc',
           commune: 'Rillieux-la-Pape',
-          postcode: '69286',
+          postcode: '69140',
+          inseeCode: '69286',
         },
         coord: [4.9036773, 45.8142196],
         accountVerified: true,
@@ -403,7 +405,8 @@ export class StructuresServiceMock {
           numero: '30 bis',
           street: 'Avenue Leclerc',
           commune: 'Rillieux-la-Pape',
-          postcode: '69286',
+          postcode: '69140',
+          inseeCode: '69286',
         },
         coord: [4.9036773, 45.8142196],
         accountVerified: true,
@@ -470,6 +473,7 @@ export class StructuresServiceMock {
           street: 'Rue Alphonse Daudet',
           commune: 'Lyon 7ème Arrondissement',
           postcode: '69007',
+          inseeCode: '69387',
         },
         contactMail: '',
         contactPhone: '',
@@ -571,6 +575,7 @@ export class StructuresServiceMock {
           street: 'Rue Alphonse Daudet',
           commune: 'Lyon 7ème Arrondissement',
           postcode: '69007',
+          inseeCode: '69387',
         },
         contactMail: '',
         contactPhone: '',
@@ -677,6 +682,7 @@ export class StructuresServiceMock {
           street: 'Rue Alphonse Daudet',
           commune: 'Lyon 7ème Arrondissement',
           postcode: '69007',
+          inseeCode: '69387',
         },
         contactMail: '',
         contactPhone: '',
@@ -778,6 +784,7 @@ export class StructuresServiceMock {
           street: 'Rue Alphonse Daudet',
           commune: 'Lyon 7ème Arrondissement',
           postcode: '69007',
+          inseeCode: '69387',
         },
         contactMail: '',
         contactPhone: '',
@@ -884,6 +891,7 @@ export class StructuresServiceMock {
           street: 'Rue Alphonse Daudet',
           commune: 'Lyon 7ème Arrondissement',
           postcode: '69007',
+          inseeCode: '69387',
         },
         contactMail: '',
         contactPhone: '',
@@ -986,8 +994,9 @@ export class StructuresServiceMock {
           numero: null,
           street: 'Rue Alphonse Daudet',
           commune: 'Lyon 7ème Arrondissement',
+          postcode: '69007',
+          inseeCode: '69387',
         },
-        postcode: '69007',
         contactMail: '',
         contactPhone: '',
         website: '',
@@ -1065,6 +1074,7 @@ export class StructuresServiceMock {
         street: 'Rue Alphonse Daudet',
         commune: 'Lyon 7ème Arrondissement',
         postcode: '69007',
+        inseeCode: '69387',
       },
       contactMail: '',
       contactPhone: '',
@@ -1178,6 +1188,7 @@ export class StructuresServiceMock {
           street: 'Rue Alphonse Daudet',
           commune: 'Lyon 7ème Arrondissement',
           postcode: '69007',
+          inseeCode: '69387',
         },
         contactMail: '',
         contactPhone: '',
@@ -1386,6 +1397,7 @@ export const mockResinStructures: Array<Structure> = [
       street: 'Rue Alphonse Daudet',
       commune: 'Lyon 7ème Arrondissement',
       postcode: '69007',
+      inseeCode: '69387',
     },
     website: '',
     facebook: null,
@@ -1490,6 +1502,7 @@ export const mockResinStructures: Array<Structure> = [
       street: 'Rue Alphonse Daudet',
       commune: 'Lyon 7ème Arrondissement',
       postcode: '69007',
+      inseeCode: '69387',
     },
     website: '',
     facebook: null,
@@ -1594,6 +1607,7 @@ export const mockResinStructures: Array<Structure> = [
       street: 'Rue Alphonse Daudet',
       commune: 'Lyon 7ème Arrondissement',
       postcode: '69007',
+      inseeCode: '69387',
     },
     website: '',
     facebook: null,