diff --git a/deploy/api_gateway.tf b/deploy/api_gateway.tf
index 7b31edb8c70cebd700d1a002dd897e0e23365bf6..599967028453ead1a93a96938790ef563e0468b1 100644
--- a/deploy/api_gateway.tf
+++ b/deploy/api_gateway.tf
@@ -83,15 +83,37 @@ resource "aws_api_gateway_deployment" "main" {
     create_before_destroy = true
   }
 }
+locals {
+  stage_name = terraform.workspace
+}
+resource "aws_cloudwatch_log_group" "apigw_logs" {
+  name              = "apigw_${aws_api_gateway_rest_api.main.id}/${local.stage_name}"
+  retention_in_days = 7
+  tags              = local.common_tags
+}
 
 resource "aws_api_gateway_stage" "main" {
   deployment_id        = aws_api_gateway_deployment.main.id
   rest_api_id          = aws_api_gateway_rest_api.main.id
-  stage_name           = terraform.workspace == "production" ? "api" : terraform.workspace
+  stage_name           = local.stage_name
   xray_tracing_enabled = true
   tags                 = local.common_tags
+  access_log_settings {
+    format = jsonencode({
+      "stage"          = "$context.stage",
+      "apiId"          = "$context.apiId",
+      "requestId"      = "$context.requestId",
+      "ip"             = "$context.identity.sourceIp",
+      "requestTime"    = "$context.requestTime",
+      "httpMethod"     = "$context.httpMethod",
+      "resourcePath"   = "$context.resourcePath",
+      "status"         = "$context.status",
+      "protocol"       = "$context.protocol",
+      "responseLength" = "$context.responseLength"
+    })
+    destination_arn = aws_cloudwatch_log_group.apigw_logs.arn
+  }
 }
-
 output "api_endpoint" {
   value = aws_api_gateway_stage.main.invoke_url
 }
diff --git a/deploy/cloudwatch_api_logs.tf b/deploy/cloudwatch_api_logs.tf
index a93dc1e985fd65ce41f63fc330dd013358de8d9b..64b01a7363ed1cde66ff5b22870dfd4686fe5076 100644
--- a/deploy/cloudwatch_api_logs.tf
+++ b/deploy/cloudwatch_api_logs.tf
@@ -1,76 +1,81 @@
-locals {
-  API_GATEWAY_LOG_GROUP = "API-Gateway-Execution-Logs_${split("-", aws_api_gateway_stage.main.id)[1]}/${aws_api_gateway_stage.main.stage_name}"
-}
-
-output "log_groupe_name" {
-  value = local.API_GATEWAY_LOG_GROUP
-}
-
-resource "aws_lambda_function" "logs" {
-  filename                       = data.archive_file.lambda_logs_file.output_path
-  function_name                  = "${local.prefix}-logs-apigw"
+resource "aws_lambda_function" "apigw_logs" {
+  filename                       = data.archive_file.lambda_logs.output_path
+  function_name                  = "${local.prefix}-analyse-logs-apigw"
   role                           = aws_iam_role.iam_for_lambda_logs.arn
   handler                        = "apigw_metrics.handler"
   timeout                        = 10
-  source_code_hash               = data.archive_file.lambda_logs_file.output_base64sha256
+  source_code_hash               = data.archive_file.lambda_logs.output_base64sha256
   runtime                        = "nodejs12.x"
-  reserved_concurrent_executions = 2
+  reserved_concurrent_executions = 5
   layers = [
     "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:14"
   ]
   environment {
     variables = {
-      API_GATEWAY_LOG_GROUP = local.API_GATEWAY_LOG_GROUP
+      API_GATEWAY_LOG_GROUP = aws_cloudwatch_log_group.apigw_logs.name
     }
   }
 
   tags = local.common_tags
 }
 
-resource "aws_lambda_function" "logs_events" {
-  filename                       = data.archive_file.lambda_logs_event_file.output_path
-  function_name                  = "${local.prefix}-logs-events-apigw"
-  role                           = aws_iam_role.iam_for_lambda_logs.arn
-  handler                        = "apigw_metrics_live.handler"
-  timeout                        = 10
-  source_code_hash               = data.archive_file.lambda_logs_event_file.output_base64sha256
-  runtime                        = "nodejs12.x"
-  reserved_concurrent_executions = 200
-  layers = [
-    "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:14"
-  ]
-  environment {
-    variables = {
-      API_GATEWAY_LOG_GROUP = local.API_GATEWAY_LOG_GROUP
-    }
+resource "aws_cloudwatch_log_subscription_filter" "lambdafunction_logfilter" {
+  name            = "${local.prefix}-lambdafunction-subscription"
+  log_group_name  = aws_cloudwatch_log_group.apigw_logs.name
+  filter_pattern  = " "
+  destination_arn = aws_lambda_function.apigw_logs.arn
+  lifecycle {
+    ignore_changes = [
+      filter_pattern
+    ]
   }
+}
 
-  tags = local.common_tags
+resource "aws_lambda_permission" "log_subscription" {
+  statement_id  = "AllowClouWatchInvoke"
+  action        = "lambda:InvokeFunction"
+  function_name = aws_lambda_function.apigw_logs.function_name
+  principal     = "logs.${var.aws_region}.amazonaws.com"
+  source_arn    = "${aws_cloudwatch_log_group.apigw_logs.arn}:*"
 }
 
-data "archive_file" "lambda_logs_file" {
+data "archive_file" "lambda_logs" {
   type        = "zip"
   output_path = "${local.lambda_loc}/zip/apigw_metrics.zip"
   source_file = "${local.lambda_loc}/apigw_metrics/apigw_metrics.js"
 }
 
-data "archive_file" "lambda_logs_event_file" {
-  type        = "zip"
-  output_path = "${local.lambda_loc}/zip/apigw_metrics_events.zip"
-  source_file = "${local.lambda_loc}/apigw_metrics/apigw_metrics_live.js"
-}
-
 resource "aws_iam_role" "iam_for_lambda_logs" {
   name               = "${local.prefix}-lambda-admin-cloudwatch-logs"
   assume_role_policy = file("./templates/lambda/assume-role-policy.json")
 }
 
-resource "aws_iam_role_policy_attachment" "lambda_logs_FA" {
+resource "aws_iam_role_policy_attachment" "lambda_cloudwatch_FA" {
   role       = aws_iam_role.iam_for_lambda_logs.name
-  policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
+  policy_arn = "arn:aws:iam::aws:policy/CloudWatchFullAccess"
 }
 
-resource "aws_iam_role_policy_attachment" "lambda_cloudwatch_FA" {
+resource "aws_iam_role_policy_attachment" "lambda_apigw" {
   role       = aws_iam_role.iam_for_lambda_logs.name
-  policy_arn = "arn:aws:iam::aws:policy/CloudWatchFullAccess"
+  policy_arn = aws_iam_policy.apigw_read_only.arn
+}
+
+resource "aws_iam_policy" "apigw_read_only" {
+  name        = "ApiGatewayReadOnly"
+  path        = "/"
+  description = "Read only for describing APIGateway services"
+  # Terraform's "jsonencode" function converts a
+  # Terraform expression result to valid JSON syntax.
+  policy = jsonencode({
+    Version = "2012-10-17"
+    Statement = [
+      {
+        Action = [
+          "apigateway:GET",
+        ]
+        Effect   = "Allow"
+        Resource = "*"
+      }
+    ]
+  })
 }
diff --git a/deploy/dynamodb.tf b/deploy/dynamodb.tf
index 66d4d049e66d14c35239cbc693ee5fcff90de329..f07f98d2bce4a6b5c3437e593cf5eed9e5678468 100644
--- a/deploy/dynamodb.tf
+++ b/deploy/dynamodb.tf
@@ -18,5 +18,57 @@ resource "aws_dynamodb_table" "main" {
 
   lifecycle {
     prevent_destroy = true
+    ignore_changes = [
+      write_capacity,
+      read_capacity
+    ]
   }
 }
+
+resource "aws_appautoscaling_target" "dynamodb_table_read_target" {
+  max_capacity       = 10
+  min_capacity       = 1
+  resource_id        = "table/${aws_dynamodb_table.main.name}"
+  scalable_dimension = "dynamodb:table:ReadCapacityUnits"
+  service_namespace  = "dynamodb"
+}
+
+resource "aws_appautoscaling_policy" "dynamodb_table_read_policy" {
+  name               = "DynamoDBReadCapacityUtilization:${aws_appautoscaling_target.dynamodb_table_read_target.resource_id}"
+  policy_type        = "TargetTrackingScaling"
+  resource_id        = aws_appautoscaling_target.dynamodb_table_read_target.resource_id
+  scalable_dimension = aws_appautoscaling_target.dynamodb_table_read_target.scalable_dimension
+  service_namespace  = aws_appautoscaling_target.dynamodb_table_read_target.service_namespace
+
+  target_tracking_scaling_policy_configuration {
+    predefined_metric_specification {
+      predefined_metric_type = "DynamoDBReadCapacityUtilization"
+    }
+
+    target_value = 50
+  }
+}
+
+resource "aws_appautoscaling_target" "dynamodb_table_write_target" {
+  max_capacity       = 10
+  min_capacity       = 1
+  resource_id        = "table/${aws_dynamodb_table.main.name}"
+  scalable_dimension = "dynamodb:table:WriteCapacityUnits"
+  service_namespace  = "dynamodb"
+}
+
+resource "aws_appautoscaling_policy" "dynamodb_table_write_policy" {
+  name               = "DynamoDBWriteCapacityUtilization:${aws_appautoscaling_target.dynamodb_table_write_target.resource_id}"
+  policy_type        = "TargetTrackingScaling"
+  resource_id        = aws_appautoscaling_target.dynamodb_table_write_target.resource_id
+  scalable_dimension = aws_appautoscaling_target.dynamodb_table_write_target.scalable_dimension
+  service_namespace  = aws_appautoscaling_target.dynamodb_table_write_target.service_namespace
+
+  target_tracking_scaling_policy_configuration {
+    predefined_metric_specification {
+      predefined_metric_type = "DynamoDBWriteCapacityUtilization"
+    }
+
+    target_value = 50
+  }
+}
\ No newline at end of file
diff --git a/deploy/lambda/apigw_metrics/apigw_metrics.js b/deploy/lambda/apigw_metrics/apigw_metrics.js
index 2e33479d72728594f4ff3f2b07a12c930b9f5cc3..4e6396025a50e64f04dd706eb65cc31b97abcfcf 100644
--- a/deploy/lambda/apigw_metrics/apigw_metrics.js
+++ b/deploy/lambda/apigw_metrics/apigw_metrics.js
@@ -1,44 +1,84 @@
 const AWS = require('aws-sdk')
+const zlib = require('zlib');
 const CW = new AWS.CloudWatch()
-const CWL = new AWS.CloudWatchLogs()
-const API_GATEWAY_LOG_GROUP = process.env.API_GATEWAY_LOG_GROUP
-const now = Date.now()/1000
-const period = 30 * 60 // seconds
-exports.handler = async function(event){
+const APIGW = new AWS.APIGateway()
 
-    const params = {
-        logGroupName: API_GATEWAY_LOG_GROUP,
-        startTime: now - period,
-        endTime: now,
-        queryString: `fields @timestamp, @message
-        | parse @message \"(*) *\" as reqId, lMessage
-        | filter lMessage like /Method completed with status:/
-        | parse lMessage \"Method completed with status: *\" as status
-        | display @timestamp, reqId, status`
-    };
-      
-    // 1. Start the query. When we start a query, this returns a queryId for us to use on our next step.
-    const data = await CWL.startQuery(params).promise();
-    const { queryId } = data;
-    console.debug('query id', queryId);
+exports.handler = async function(event,context){
+    return new Promise((resolve,reject)=>{
 
-    while (true) {
-    
-    // 2. Send Insight query to CloudwatchLogs
-    const insightData = await CWL.getQueryResults({ queryId }).promise();
-    console.log(JSON.stringify(insightData,null,4))
-    // 3. Check if it is available    
-    if (Array.isArray(insightData.results) && insightData.status === 'Complete') {
-        const insightResult = insightData.results;
-        
-        // Change this line to publish to SNS or send to Slack
-        console.log(JSON.stringify(insightResult, null, 4))
-        break;
-    }
-    
-    // 4. Otherwise, Wait for 100 ms for insight api result
-    await new Promise((resolve, reject) => setTimeout(resolve, 100));
-    } 
+        console.log("Event: "+JSON.stringify(event))
+        var payload = Buffer.from(event.awslogs.data, 'base64');
+        zlib.gunzip(payload, function(e, result) {
+            if (e) { 
+                console.error(JSON.stringify(e, null, 2))
+                reject(e)
+            } else {
+                //début du traitement du log stream fourni
+                result = JSON.parse(result.toString('ascii'));
+                console.log("Event Data after decoding: ", JSON.stringify(result, null, 2));
+                
+                var promises = []
 
-    return 'ok';
-}
\ No newline at end of file
+                result.logEvents.forEach(log => {
+                    //for each log, get api name and push a metric data object associated in the payload for PutMetricData
+                    var message = JSON.parse(log.message)
+                    var getRestApiPromise = APIGW.getRestApi({restApiId:message.apiId}).promise()
+                    promises.push(new Promise((res,rej)=>{
+                        getRestApiPromise.then(data=>{
+                            console.log("getRestApi result: "+JSON.stringify(data))
+                            res(
+                                {
+                                    MetricName: '400 Errors', 
+                                    Dimensions: [
+                                        {
+                                            Name: 'API/REST',
+                                            Value:  data.name
+                                        },
+                                        {
+                                            Name: 'Stage',
+                                            Value:  message.stage
+                                        },
+                                        {
+                                            Name: 'Method',
+                                            Value:  message.httpMethod
+                                        },
+                                        {
+                                            Name: 'Resource',
+                                            Value:  message.resourcePath
+                                        },
+                                    ],
+                                    Timestamp: Math.floor(log.timestamp/1000),
+                                    Unit: "Count",
+                                    Value: message.status === "400" ? "1":"0",
+                                }
+                            )
+                        }).catch(err=>{
+                            rej(err)
+                        })
+                    }))
+                });
+                
+                //when all logs are processed, send the PutMetricData request
+                Promise.all(promises).then(metrics=>{ //if all the promises (cf line 26) succed (equivalent to all the getRestApiPromise)
+                    const params = {
+                        MetricData:metrics,
+                        Namespace: "APIGW/custom"
+                    };
+                    console.log("PutMetricData parameters : "+JSON.stringify(params))
+                    CW.putMetricData(params).promise()
+                        .then(data=>{
+                            console.log("PutMetricData succeeded :"+JSON.stringify(data))
+                            resolve(data)
+                        })
+                        .catch(err=>{
+                            console.error(err)
+                            reject(err)
+                        });
+                }).catch( err => { //if one of the promise (cf line 26) fails
+                    console.error(err)
+                    reject(err)
+                });
+            }
+        });
+    })
+};
\ No newline at end of file
diff --git a/deploy/lambda/apigw_metrics/apigw_metrics_live.js b/deploy/lambda/apigw_metrics/apigw_metrics_live.js
deleted file mode 100644
index bc448ca8c614e0547247e79006131c0d2dbd5b3b..0000000000000000000000000000000000000000
--- a/deploy/lambda/apigw_metrics/apigw_metrics_live.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const AWS = require('aws-sdk')
-const CW = new AWS.CloudWatch()
-const CWL = new AWS.CloudWatchLogs()
-const API_GATEWAY_LOG_GROUP = process.env.API_GATEWAY_LOG_GROUP
-exports.handler = async function(event,context){
-
-    console.log(JSON.stringify(event))
-    console.log(JSON.stringify(context))
-}
\ No newline at end of file
diff --git a/deploy/lambda/apigw_metrics/package-lock.json b/deploy/lambda/apigw_metrics/package-lock.json
index 9c3a1b414583486aca73bdf38d6b15d911e122df..8b913e17125c5e264a1eb192f04948a4204f08a4 100644
--- a/deploy/lambda/apigw_metrics/package-lock.json
+++ b/deploy/lambda/apigw_metrics/package-lock.json
@@ -5,7 +5,8 @@
     "packages": {
         "": {
             "dependencies": {
-                "aws-sdk": "^2.864.0"
+                "aws-sdk": "^2.864.0",
+                "zlib": "^1.0.5"
             }
         },
         "node_modules/aws-sdk": {
@@ -133,6 +134,15 @@
             "engines": {
                 "node": ">=4.0"
             }
+        },
+        "node_modules/zlib": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/zlib/-/zlib-1.0.5.tgz",
+            "integrity": "sha1-bnyXL8NxxkWmr7A6sUdp3vEU/MA=",
+            "hasInstallScript": true,
+            "engines": {
+                "node": ">=0.2.0"
+            }
         }
     },
     "dependencies": {
@@ -229,6 +239,11 @@
             "version": "9.0.7",
             "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
             "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
+        },
+        "zlib": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/zlib/-/zlib-1.0.5.tgz",
+            "integrity": "sha1-bnyXL8NxxkWmr7A6sUdp3vEU/MA="
         }
     }
 }
diff --git a/deploy/lambda/apigw_metrics/package.json b/deploy/lambda/apigw_metrics/package.json
index 7e315ed09924196e440250f2c46eaf9b237e8c03..43875eae23bd34a8e7fdb50e69d99be0cb6d9a6c 100644
--- a/deploy/lambda/apigw_metrics/package.json
+++ b/deploy/lambda/apigw_metrics/package.json
@@ -1,5 +1,6 @@
 {
     "dependencies": {
-        "aws-sdk": "^2.864.0"
+        "aws-sdk": "^2.864.0",
+        "zlib": "^1.0.5"
     }
 }
diff --git a/deploy/lambda/src/create-one.js b/deploy/lambda/src/create-one.js
index 4f12e2847724c7eb5df55bc747aceea805b499a0..94950b45ae88624d20feeab6cc1d98bcc05d27cb 100644
--- a/deploy/lambda/src/create-one.js
+++ b/deploy/lambda/src/create-one.js
@@ -8,8 +8,9 @@ const RESERVED_RESPONSE = `Error: You're using AWS reserved keywords as attribut
 exports.handler = async (event = {}) => {
     console.log(event)
     if (!event.body) {
-        console.error("400 invalid request, you are missing the parameter body")
-        return { statusCode: 400, body: 'invalid request, you are missing the parameter body' };
+        var err = { statusCode: 400, body: JSON.stringify({status:32001,msg:'invalid request, you are missing the parameter body'})};
+        console.error(err)
+        return err;
     }
     const item = typeof event.body == 'object' ? event.body : JSON.parse(event.body);
     item[PRIMARY_KEY] = uuidv4();
diff --git a/deploy/lambda/src/delete-one.js b/deploy/lambda/src/delete-one.js
index 6ed81e10a0892318fbd3cfa5ded543f00d42b903..637264d37d9081ddb4cd06552a79dc528f1c5996 100644
--- a/deploy/lambda/src/delete-one.js
+++ b/deploy/lambda/src/delete-one.js
@@ -4,15 +4,12 @@ const db = new AWS.DynamoDB.DocumentClient();
 const TABLE_NAME = process.env.TABLE_NAME || '';
 const PRIMARY_KEY = process.env.TABLE_KEY || '';
 exports.handler = async (event = {}) => {
-    if (!event.queryStringParameters) {
-        console.error("400 invalid request, you are missing the path parameter id")
-        return { statusCode: 400, body: `Error: You are missing the path parameter id` };
+    if (!event.queryStringParameters || !event.queryStringParameters.id) {
+        var err = { statusCode: 400, body: JSON.stringify({status:32002,msg:'invalid request, you are missing the path parameter id'})};
+        console.error(err)
+        return err;
     }
     const requestedItemId = event.queryStringParameters.id;
-    if (!requestedItemId) {
-        console.error("400 invalid request, you are missing the path parameter id")
-        return { statusCode: 400, body: `Error: You are missing the path parameter id` };
-    }
     const params = {
         TableName: TABLE_NAME,
         Key: {
diff --git a/deploy/lambda/src/get-one.js b/deploy/lambda/src/get-one.js
index a3f22ae96aab5569e1694208cb3692f50da75f8b..f55214599c9a4e661559f0372e8295254870d129 100644
--- a/deploy/lambda/src/get-one.js
+++ b/deploy/lambda/src/get-one.js
@@ -4,15 +4,12 @@ const db = new AWS.DynamoDB.DocumentClient();
 const TABLE_NAME = process.env.TABLE_NAME || '';
 const PRIMARY_KEY = process.env.TABLE_KEY || '';
 exports.handler = async (event = {}) => {
-    if (!event.queryStringParameters) {
-        console.error("400 invalid request, you are missing the path parameter id")
-        return { statusCode: 400, body: `Error: You are missing the path parameter id` };
+    if (!event.queryStringParameters || !event.queryStringParameters.id) {
+        var err = { statusCode: 400, body: JSON.stringify({status:32002,msg:'invalid request, you are missing the path parameter id'})};
+        console.error(err)
+        return err;
     }
     const requestedItemId = event.queryStringParameters.id;
-    if (!requestedItemId) {
-        console.error("400 invalid request, you are missing the path parameter id")
-        return { statusCode: 400, body: `Error: You are missing the path parameter id` };
-    }
     const params = {
         TableName: TABLE_NAME,
         Key: {
diff --git a/deploy/lambda/src/update-one.js b/deploy/lambda/src/update-one.js
index 12a48747e464efa644f697aa464377fe00653a9a..21f2c55a83247d693e64cf7a83cee03e3d438622 100644
--- a/deploy/lambda/src/update-one.js
+++ b/deploy/lambda/src/update-one.js
@@ -6,18 +6,16 @@ const PRIMARY_KEY = process.env.TABLE_KEY || '';
 const RESERVED_RESPONSE = `Error: You're using AWS reserved keywords as attributes`, DYNAMODB_EXECUTION_ERROR = `Error: Execution update, caused a Dynamodb error, please take a look at your CloudWatch Logs.`;
 exports.handler = async (event = {}) => {
     if (!event.body) {
-        console.error("400 invalid request, you are missing the parameter body")
-        return { statusCode: 400, body: 'invalid request, you are missing the parameter body' };
+        var err = { statusCode: 400, body: JSON.stringify({status:32001,msg:'invalid request, you are missing the parameter body'})};
+        console.error(err)
+        return err;
     }
-    if (!event.queryStringParameters) {
-        console.error("400 invalid request, you are missing the path parameter id")
-        return { statusCode: 400, body: `Error: You are missing the path parameter id` };
+    if (!event.queryStringParameters || !event.queryStringParameters.id) {
+        var err = { statusCode: 400, body: JSON.stringify({status:32002,msg:'invalid request, you are missing the path parameter id'})};
+        console.error(err)
+        return err;
     }
     const editedItemId = event.queryStringParameters.id;
-    if (!editedItemId) {
-        console.error("400 invalid request, you are missing the path parameter id")
-        return { statusCode: 400, body: 'invalid request, you are missing the path parameter id' };
-    }
     const editedItem = typeof event.body == 'object' ? event.body : JSON.parse(event.body);
     const editedItemProperties = Object.keys(editedItem);
     if (!editedItem || editedItemProperties.length < 1) {
diff --git a/deploy/vars/dev.tfvars b/deploy/vars/dev.tfvars
index 35e13d2bd25c800b85a1e028c7e8d840e1fc9c87..4988439cf49a532170d92319edde3f91a11a8416 100644
--- a/deploy/vars/dev.tfvars
+++ b/deploy/vars/dev.tfvars
@@ -1,3 +1,3 @@
-dynamodb_read_capacity  = 1
-dynamodb_write_capacity = 1
+dynamodb_read_capacity  = 2
+dynamodb_write_capacity = 2
 dynamodb_enable_pitr    = false