diff --git a/rest/index.php b/rest/index.php
index 999ca710f0da09ca0417a9325a0e4753bbc4c6c5..1347054de6834b02c97a2f4898e68c913cd4a184 100755
--- a/rest/index.php
+++ b/rest/index.php
@@ -90,6 +90,11 @@ $app->get('/batchHistories', \History\controllers\BatchHistoryController::class
 //Contacts
 $app->post('/contacts', \Contact\controllers\ContactController::class . ':create');
 $app->get('/contacts/{contactId}/communication', \Contact\controllers\ContactController::class . ':getCommunicationByContactId');
+$app->get('/contactsGroups', \Contact\controllers\ContactGroupController::class . ':get');
+$app->post('/contactsGroups', \Contact\controllers\ContactGroupController::class . ':create');
+$app->get('/contactsGroups/{id}', \Contact\controllers\ContactGroupController::class . ':getById');
+$app->put('/contactsGroups/{id}', \Contact\controllers\ContactGroupController::class . ':update');
+$app->delete('/contactsGroups/{id}', \Contact\controllers\ContactGroupController::class . ':delete');
 
 //Docservers
 $app->get('/docservers', \Docserver\controllers\DocserverController::class . ':get');
diff --git a/src/app/contact/controllers/ContactGroupController.php b/src/app/contact/controllers/ContactGroupController.php
new file mode 100644
index 0000000000000000000000000000000000000000..3642e11206b4aa079344f892102a610dc320260a
--- /dev/null
+++ b/src/app/contact/controllers/ContactGroupController.php
@@ -0,0 +1,138 @@
+<?php
+
+/**
+ * Copyright Maarch since 2008 under licence GPLv3.
+ * See LICENCE.txt file at the root folder for more details.
+ * This file is part of Maarch software.
+ *
+ */
+
+/**
+ * @brief Contact Group Controller
+ * @author dev@maarch.org
+ */
+
+namespace Contact\controllers;
+
+use Contact\models\ContactGroupModel;
+use Group\models\ServiceModel;
+use History\controllers\HistoryController;
+use Respect\Validation\Validator;
+use Slim\Http\Request;
+use Slim\Http\Response;
+use User\models\UserModel;
+
+class ContactGroupController
+{
+    public function get(Request $request, Response $response)
+    {
+        $contactsGroups = ContactGroupModel::get();
+
+        foreach ($contactsGroups as $key => $contactsGroup) {
+            $contactsGroups[$key]['labelledOwner'] = UserModel::getLabelledUserById(['id' => $contactsGroup['id']]);
+        }
+
+        return $response->withJson(['contactsGroups' => $contactsGroups]);
+    }
+
+    public function getById(Request $request, Response $response, array $aArgs)
+    {
+        $contactsGroup = ContactGroupModel::getById(['id' => $aArgs['id']]);
+
+        if (empty($contactsGroup)) {
+            return $response->withStatus(400)->withJson(['errors' => 'Contacts group not found']);
+        }
+
+        $contactsGroup['labelledOwner'] = UserModel::getLabelledUserById(['id' => $contactsGroup['id']]);
+
+        return $response->withJson(['contactsGroup' => $contactsGroup]);
+    }
+
+    public function create(Request $request, Response $response)
+    {
+        if (!ServiceModel::hasService(['id' => 'admin_contacts', 'userId' => $GLOBALS['userId'], 'location' => 'apps', 'type' => 'admin'])) {
+            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
+        }
+
+        $data = $request->getParams();
+        $check = Validator::stringType()->notEmpty()->validate($data['label']);
+        $check = $check && Validator::stringType()->notEmpty()->validate($data['description']);
+        $check = $check && Validator::boolType()->validate($data['public']);
+        if (!$check) {
+            return $response->withStatus(400)->withJson(['errors' => 'Bad Request']);
+        }
+
+        $data['public'] = $data['public'] ? 'true' : 'false';
+        $data['owner'] = $GLOBALS['userId'];
+        $data['entity_owner'] = UserModel::getPrimaryEntityByUserId(['userId' => $GLOBALS['userId']])['entity_id'];
+
+        $id = ContactGroupModel::create($data);
+
+        HistoryController::add([
+            'tableName' => 'contacts_groups',
+            'recordId'  => $id,
+            'eventType' => 'ADD',
+            'info'      => _CONTACTS_GROUP_ADDED . " : {$data['label']}",
+            'moduleId'  => 'contact',
+            'eventId'   => 'contactsGroupCreation',
+        ]);
+
+        return $response->withJson(['contactsGroup' => $id]);
+    }
+
+    public function update(Request $request, Response $response, array $aArgs)
+    {
+        if (!ServiceModel::hasService(['id' => 'admin_contacts', 'userId' => $GLOBALS['userId'], 'location' => 'apps', 'type' => 'admin'])) {
+            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
+        }
+
+        $data = $request->getParams();
+        $check = Validator::stringType()->notEmpty()->validate($data['label']);
+        $check = $check && Validator::stringType()->notEmpty()->validate($data['description']);
+        $check = $check && Validator::boolType()->validate($data['public']);
+        if (!$check) {
+            return $response->withStatus(400)->withJson(['errors' => 'Bad Request']);
+        }
+
+        $data['id'] = $aArgs['id'];
+        $data['public'] = $data['public'] ? 'true' : 'false';
+
+        ContactGroupModel::update($data);
+
+        HistoryController::add([
+            'tableName' => 'contacts_groups',
+            'recordId'  => $aArgs['id'],
+            'eventType' => 'UP',
+            'info'      => _CONTACTS_GROUP_UPDATED . " : {$data['label']}",
+            'moduleId'  => 'contact',
+            'eventId'   => 'contactsGroupModification',
+        ]);
+
+        return $response->withJson(['success' => 'success']);
+    }
+
+    public function delete(Request $request, Response $response, array $aArgs)
+    {
+        if (!ServiceModel::hasService(['id' => 'admin_contacts', 'userId' => $GLOBALS['userId'], 'location' => 'apps', 'type' => 'admin'])) {
+            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
+        }
+
+        ContactGroupModel::delete(['id' => $aArgs['id']]);
+
+        HistoryController::add([
+            'tableName' => 'contacts_groups',
+            'recordId'  => $aArgs['id'],
+            'eventType' => 'DEL',
+            'info'      => _CONTACTS_GROUP_DELETED . " : {$aArgs['id']}",
+            'moduleId'  => 'contact',
+            'eventId'   => 'contactsGroupSuppression',
+        ]);
+
+        $contactsGroups = ContactGroupModel::get();
+        foreach ($contactsGroups as $key => $contactsGroup) {
+            $contactsGroups[$key]['labelledOwner'] = UserModel::getLabelledUserById(['id' => $contactsGroup['id']]);
+        }
+
+        return $response->withJson(['contactsGroups' => $contactsGroups]);
+    }
+}
diff --git a/src/app/contact/models/ContactGroupModel.php b/src/app/contact/models/ContactGroupModel.php
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ec0639fe5d9e6c5d63e0eec56a88001bd03c8
--- /dev/null
+++ b/src/app/contact/models/ContactGroupModel.php
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * Copyright Maarch since 2008 under licence GPLv3.
+ * See LICENCE.txt file at the root folder for more details.
+ * This file is part of Maarch software.
+ *
+ */
+
+/**
+ * @brief Contact Group Model
+ * @author dev@maarch.org
+ */
+
+namespace Contact\models;
+
+class ContactGroupModel extends ContactGroupModelAbstract
+{
+}
\ No newline at end of file
diff --git a/src/app/contact/models/ContactGroupModelAbstract.php b/src/app/contact/models/ContactGroupModelAbstract.php
new file mode 100644
index 0000000000000000000000000000000000000000..a4ede70ef6d5af73e9f6223ae097c7838931bb1c
--- /dev/null
+++ b/src/app/contact/models/ContactGroupModelAbstract.php
@@ -0,0 +1,111 @@
+<?php
+/**
+ * Copyright Maarch since 2008 under licence GPLv3.
+ * See LICENCE.txt file at the root folder for more details.
+ * This file is part of Maarch software.
+ *
+ */
+
+/**
+ * @brief Contact Group Abstract Model
+ * @author dev@maarch.org
+ */
+
+namespace Contact\models;
+
+use SrcCore\models\ValidatorModel;
+use SrcCore\models\DatabaseModel;
+
+abstract class ContactGroupModelAbstract
+{
+    public static function get(array $aArgs = [])
+    {
+        ValidatorModel::arrayType($aArgs, ['select', 'where', 'data', 'orderBy']);
+
+        $aReturn = DatabaseModel::select([
+            'select'    => empty($aArgs['select']) ? ['*'] : $aArgs['select'],
+            'table'     => ['contacts_groups'],
+            'where'     => $aArgs['where'],
+            'data'      => $aArgs['data'],
+            'order_by'  => $aArgs['orderBy']
+        ]);
+
+        return $aReturn;
+    }
+
+    public static function getById(array $aArgs)
+    {
+        ValidatorModel::notEmpty($aArgs, ['id']);
+        ValidatorModel::intVal($aArgs, ['id']);
+        ValidatorModel::arrayType($aArgs, ['select']);
+
+        $aContactGroup = DatabaseModel::select([
+            'select'    => empty($aArgs['select']) ? ['*'] : $aArgs['select'],
+            'table'     => ['contacts_groups'],
+            'where'     => ['id = ?'],
+            'data'      => [$aArgs['id']]
+        ]);
+
+        return $aContactGroup[0];
+    }
+
+    public static function create(array $aArgs)
+    {
+        ValidatorModel::notEmpty($aArgs, ['label', 'description', 'public', 'owner', 'entity_owner']);
+        ValidatorModel::stringType($aArgs, ['label', 'description', 'public', 'owner', 'entity_owner']);
+
+        $nextSequenceId = DatabaseModel::getNextSequenceValue(['sequenceId' => 'contacts_groups_id_seq']);
+        DatabaseModel::insert([
+            'table'         => 'contacts_groups',
+            'columnsValues' => [
+                'id'            => $nextSequenceId,
+                'label'         => $aArgs['label'],
+                'description'   => $aArgs['description'],
+                'public'        => $aArgs['public'],
+                'owner'         => $aArgs['owner'],
+                'entity_owner'  => $aArgs['entity_owner'],
+            ]
+        ]);
+
+        return $nextSequenceId;
+    }
+
+    public static function update(array $aArgs)
+    {
+        ValidatorModel::notEmpty($aArgs, ['id', 'label', 'description', 'public']);
+        ValidatorModel::stringType($aArgs, ['label', 'description', 'public']);
+        ValidatorModel::intVal($aArgs, ['id']);
+
+        DatabaseModel::update([
+            'table'     => 'contacts_groups',
+            'set'       => [
+                'label'         => $aArgs['label'],
+                'description'   => $aArgs['description'],
+                'public'        => $aArgs['public']
+            ],
+            'where'     => ['id = ?'],
+            'data'      => [$aArgs['id']]
+        ]);
+
+        return true;
+    }
+
+    public static function delete(array $aArgs)
+    {
+        ValidatorModel::notEmpty($aArgs, ['id']);
+        ValidatorModel::intVal($aArgs, ['id']);
+
+        DatabaseModel::delete([
+            'table' => 'contacts_groups',
+            'where' => ['id = ?'],
+            'data'  => [$aArgs['id']]
+        ]);
+        DatabaseModel::delete([
+            'table' => 'contacts_groups_lists',
+            'where' => ['contacts_groups_id = ?'],
+            'data'  => [$aArgs['id']]
+        ]);
+
+        return true;
+    }
+}
diff --git a/src/core/lang/lang-en.php b/src/core/lang/lang-en.php
index a80bcf42c603d29196520db8add8d601ec1d66aa..5d33a056ecd8d357e33fda52681f02d5c197234c 100644
--- a/src/core/lang/lang-en.php
+++ b/src/core/lang/lang-en.php
@@ -30,6 +30,9 @@ define('_BASKET_REDIRECTION', 'Basket redirection');
 define('_BASKET_REDIRECTION_SUPPRESSION', 'Basket redirection');
 define('_BASKETS_SORT_MODIFICATION', 'Baskets order modification');
 define('_BY_DEFAULT', 'by default');
+define('_CONTACTS_GROUP_ADDED', 'Contacts group added');
+define('_CONTACTS_GROUP_UPDATED', 'Contacts group updated');
+define('_CONTACTS_GROUP_DELETED', 'Contacts group deleted');
 define('_DELETE_NOTIFICATIONS', 'Notification deleted');
 define('_DEST_USER', 'Recipient');
 define('_DOCTYPE_FIRSTLEVEL_ADDED', 'Doctype first level added');
diff --git a/src/core/lang/lang-fr.php b/src/core/lang/lang-fr.php
index f72d08cc6364ea127f8daeda2aa04c3af788348f..9e49b478340a0530c63e5f0801a3f7f01a459732 100644
--- a/src/core/lang/lang-fr.php
+++ b/src/core/lang/lang-fr.php
@@ -30,6 +30,9 @@ define('_BASKET_REDIRECTION', 'Redirection bannette');
 define('_BASKET_REDIRECTION_SUPPRESSION', 'Suppression redirection bannette');
 define('_BASKETS_SORT_MODIFICATION', 'Modification ordre bannettes');
 define('_BY_DEFAULT', 'par défaut');
+define('_CONTACTS_GROUP_ADDED', 'Groupe de contacts ajouté');
+define('_CONTACTS_GROUP_UPDATED', 'Groupe de contacts modifié');
+define('_CONTACTS_GROUP_DELETED', 'Groupe de contacts supprimé');
 define('_DELETE_NOTIFICATIONS', 'Notification supprimée');
 define('_DEST_USER', 'Destinataire');
 define('_DOCTYPE_FIRSTLEVEL_ADDED', 'Chemise ajoutée');