diff --git a/models/CopilotPayment.js b/models/CopilotPayment.js
index 46c8674..92b127f 100644
--- a/models/CopilotPayment.js
+++ b/models/CopilotPayment.js
@@ -14,6 +14,19 @@ const dynamoose = require('dynamoose');
 
 const Schema = dynamoose.Schema;
 
+/**
+ * @typedef {Object} CopilotPayment
+ * @property {String} id The unique identifier for the CopilotPayment entity.
+ * @property {String} project The project associated with the payment.
+ * @property {Number} amount The payment amount.
+ * @property {String} description The description of the payment.
+ * @property {Number} challengeId The ID of the associated challenge (if applicable).
+ * @property {String} challengeUUID The UUID of the associated challenge (if applicable).
+ * @property {String} closed Indicates whether the payment is closed or not (default is 'false').
+ * @property {String} username The username of the Copilot receiving the payment.
+ * @property {String} status The status of the payment.
+ */
+
 const schema = new Schema({
   id: {
     type: String,
diff --git a/models/GithubUserMapping.js b/models/GithubUserMapping.js
index 6cd5d23..56b0cb5 100644
--- a/models/GithubUserMapping.js
+++ b/models/GithubUserMapping.js
@@ -7,6 +7,14 @@ const dynamoose = require('dynamoose');
 
 const Schema = dynamoose.Schema;
 
+/**
+ * @typedef {Object} GithubUserMapping
+ * @property {String} id The unique identifier for the GithubUserMapping entity.
+ * @property {String} topcoderUsername The Topcoder username associated with the GitHub user.
+ * @property {String} githubUsername The GitHub username.
+ * @property {Number} githubUserId The GitHub user's numeric identifier.
+ */
+
 const schema = new Schema({
   id: {
     type: String,
diff --git a/models/GitlabUserMapping.js b/models/GitlabUserMapping.js
index f87f1d9..6377122 100644
--- a/models/GitlabUserMapping.js
+++ b/models/GitlabUserMapping.js
@@ -7,6 +7,14 @@ const dynamoose = require('dynamoose');
 
 const Schema = dynamoose.Schema;
 
+/**
+ * @typedef {Object} GitlabUserMapping
+ * @property {String} id The unique identifier for the GitlabUserMapping entity.
+ * @property {String} topcoderUsername The Topcoder username associated with the GitLab user.
+ * @property {String} gitlabUsername The GitLab username.
+ * @property {Number} gitlabUserId The GitLab user's numeric identifier.
+ */
+
 const schema = new Schema({
   id: {
     type: String,
diff --git a/models/Issue.js b/models/Issue.js
index 79fb016..61ba943 100644
--- a/models/Issue.js
+++ b/models/Issue.js
@@ -12,6 +12,27 @@ const dynamoose = require('dynamoose');
 
 const Schema = dynamoose.Schema;
 
+/**
+ * @typedef {Object} Issue
+ * @property {String} id The id.
+ * @property {Number} number From the receiver service.
+ * @property {String} title The title.
+ * @property {String} body The body.
+ * @property {Number[]} prizes Prizes extracted from title.
+ * @property {String} provider Provider (github or gitlab).
+ * @property {Number} repositoryId Repository ID.
+ * @property {String} repoUrl Repository URL.
+ * @property {String} repositoryIdStr Repository ID as a String.
+ * @property {Array} labels Labels associated with the issue.
+ * @property {String} assignee Assignee for the issue.
+ * @property {Date} updatedAt Date when the issue was last updated.
+ * @property {Number} challengeId Challenge ID from topcoder API.
+ * @property {String} challengeUUID Challenge UUID.
+ * @property {String} projectId Project ID.
+ * @property {String} status Status of the issue.
+ * @property {Date} assignedAt Date when the issue was assigned (if applicable).
+ */
+
 const schema = new Schema({
   id: {type: String, hashKey: true, required: true},
   // From the receiver service
diff --git a/models/Project.js b/models/Project.js
index df53b8d..d2d1499 100755
--- a/models/Project.js
+++ b/models/Project.js
@@ -12,6 +12,23 @@ const dynamoose = require('dynamoose');
 
 const Schema = dynamoose.Schema;
 
+/**
+ * @typedef {Object} ProjectChallengeMapping
+ * @property {String} id The id.
+ * @property {String} title The title.
+ * @property {Number} tcDirectId The tc direct id.
+ * @property {String} tags The tags.
+ * @property {String} rocketChatWebhook The rocket chat webhook.
+ * @property {String} rocketChatChannelName The rocket chat channel name.
+ * @property {String} archived The archived.
+ * @property {String} owner The owner.
+ * @property {String} secretWebhookKey The secret webhook key.
+ * @property {String} copilot The copilot.
+ * @property {Date} updatedAt The updated at.
+ * @property {String} createCopilotPayments The create copilot payments.
+ * @property {Boolean} isConnect Is Topcoder connect.
+ */
+
 const schema = new Schema({
   id: {
     type: String,
diff --git a/models/ProjectChallengeMapping.js b/models/ProjectChallengeMapping.js
index 5b4c585..53a25be 100644
--- a/models/ProjectChallengeMapping.js
+++ b/models/ProjectChallengeMapping.js
@@ -10,6 +10,13 @@ const dynamoose = require('dynamoose');
 
 const Schema = dynamoose.Schema;
 
+/**
+ * @typedef {Object} ProjectChallengeMapping
+ * @property {String} id the id
+ * @property {String} projectId the project id
+ * @property {String} challengeId the challenge id
+ */
+
 const schema = new Schema({
   id: {
     type: String,
diff --git a/models/Repository.js b/models/Repository.js
index e9a8f1a..ae63fb2 100644
--- a/models/Repository.js
+++ b/models/Repository.js
@@ -12,6 +12,16 @@ const dynamoose = require('dynamoose');
 
 const Schema = dynamoose.Schema;
 
+/**
+ * @typedef {Object} Repository
+ * @property {String} id The unique identifier for the Repository entity.
+ * @property {String} projectId The project ID associated with the repository.
+ * @property {String} url The URL of the repository.
+ * @property {String} archived Indicates whether the repository is archived or not.
+ * @property {String} repoId The repository ID (if applicable).
+ * @property {String} registeredWebhookId The ID of the registered webhook (if applicable).
+ */
+
 const schema = new Schema({
   id: {
     type: String,
diff --git a/models/User.js b/models/User.js
index 7237dfe..96cef0a 100755
--- a/models/User.js
+++ b/models/User.js
@@ -12,6 +12,21 @@ const constants = require('../constants');
 
 const Schema = dynamoose.Schema;
 
+/**
+ * @typedef {Object} User
+ * @property {String} id The user's unique identifier.
+ * @property {Number} userProviderId The user provider's numeric identifier.
+ * @property {String} userProviderIdStr The user provider's identifier as a string.
+ * @property {String} username The user's username.
+ * @property {String} role The user's role, one of the allowed constants.USER_ROLES.
+ * @property {String} type The user's type, one of the allowed constants.USER_TYPES.
+ * @property {String} accessToken GitLab token data (if applicable).
+ * @property {Date} accessTokenExpiration Expiration date of the access token (if applicable).
+ * @property {String} refreshToken GitLab token refresh token (if applicable).
+ * @property {String} lockId Lock identifier (if applicable).
+ * @property {Date} lockExpiration Expiration date of the lock (if applicable).
+ */
+
 const schema = new Schema({
   id: {
     type: String,
diff --git a/models/index.js b/models/index.js
index 4d8dbc1..07f58e2 100644
--- a/models/index.js
+++ b/models/index.js
@@ -35,16 +35,23 @@ if (process.env.CREATE_DB) {
 
 /* eslint-disable global-require */
 const models = {
+  /** @type {import('dynamoose').ModelConstructor<import('./Issue').Issue>} */
   Issue: dynamoose.model('Topcoder_X.Issue', require('./Issue')),
+  /** @type {import('dynamoose').ModelConstructor<import('./Project').Project>} */
   Project: dynamoose.model('Topcoder_X.Project', require('./Project')),
+  /** @type {import('dynamoose').ModelConstructor<import('./ProjectChallengeMapping').ProjectChallengeMapping>} */
   ProjectChallengeMapping: dynamoose.model('Topcoder_X.ProjectChallengeMapping', require('./ProjectChallengeMapping')),
+  /** @type {import('dynamoose').ModelConstructor<import('./User').User>} */
   User: dynamoose.model('Topcoder_X.User', require('./User')),
+  /** @type {import('dynamoose').ModelConstructor<import('./CopilotPayment').CopilotPayment>} */
   CopilotPayment: dynamoose.model('Topcoder_X.CopilotPayment', require('./CopilotPayment')),
+  /** @type {import('dynamoose').ModelConstructor<import('./GithubUserMapping').GithubUserMapping>} */
   GithubUserMapping: dynamoose.model('Topcoder_X.GithubUserMapping', require('./GithubUserMapping')),
+  /** @type {import('dynamoose').ModelConstructor<import('./GitlabUserMapping').GitlabUserMapping>} */
   GitlabUserMapping: dynamoose.model('Topcoder_X.GitlabUserMapping', require('./GitlabUserMapping')),
+  /** @type {import('dynamoose').ModelConstructor<import('./Repository').Repository>} */
   Repository: dynamoose.model('Topcoder_X.Repository', require('./Repository'))
 };
 /* eslint-enable global-require */
 
-
 module.exports = models;
diff --git a/package-lock.json b/package-lock.json
index 929ac63..e5420b9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,10 +11,12 @@
       "dependencies": {
         "@gitbeaker/rest": "^39.13.0",
         "@octokit/rest": "^18.9.0",
+        "archiver": "^6.0.1",
         "axios": "^0.19.0",
         "circular-json": "^0.5.7",
         "config": "^1.30.0",
         "dynamoose": "^1.11.1",
+        "form-data": "^4.0.0",
         "fs-extra": "^7.0.0",
         "get-parameter-names": "^0.3.0",
         "global-request-logger": "^0.1.1",
@@ -831,6 +833,102 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
+    "node_modules/archiver": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz",
+      "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==",
+      "dependencies": {
+        "archiver-utils": "^4.0.1",
+        "async": "^3.2.4",
+        "buffer-crc32": "^0.2.1",
+        "readable-stream": "^3.6.0",
+        "readdir-glob": "^1.1.2",
+        "tar-stream": "^3.0.0",
+        "zip-stream": "^5.0.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/archiver-utils": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz",
+      "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==",
+      "dependencies": {
+        "glob": "^8.0.0",
+        "graceful-fs": "^4.2.0",
+        "lazystream": "^1.0.0",
+        "lodash": "^4.17.15",
+        "normalize-path": "^3.0.0",
+        "readable-stream": "^3.6.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/archiver-utils/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/archiver-utils/node_modules/glob": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+      "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^5.0.1",
+        "once": "^1.3.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/archiver-utils/node_modules/minimatch": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/archiver-utils/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/archiver/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/are-docs-informative": {
       "version": "0.0.2",
       "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz",
@@ -893,6 +991,11 @@
         "node": "*"
       }
     },
+    "node_modules/async": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
+      "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ=="
+    },
     "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -932,17 +1035,6 @@
         }
       ]
     },
-    "node_modules/auth0-js/node_modules/combined-stream": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
-      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dependencies": {
-        "delayed-stream": "~1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
     "node_modules/auth0-js/node_modules/component-emitter": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
@@ -969,19 +1061,6 @@
         }
       }
     },
-    "node_modules/auth0-js/node_modules/form-data": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
-      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
-      "dependencies": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.8",
-        "mime-types": "^2.1.12"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
     "node_modules/auth0-js/node_modules/formidable": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz",
@@ -1157,6 +1236,11 @@
         "is-buffer": "^2.0.2"
       }
     },
+    "node_modules/b4a": {
+      "version": "1.6.4",
+      "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz",
+      "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw=="
+    },
     "node_modules/babel-code-frame": {
       "version": "6.26.0",
       "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@@ -1245,8 +1329,7 @@
     "node_modules/balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-      "devOptional": true
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
     },
     "node_modules/base64-js": {
       "version": "1.3.1",
@@ -1591,9 +1674,9 @@
       }
     },
     "node_modules/combined-stream": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
-      "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
       "dependencies": {
         "delayed-stream": "~1.0.0"
       },
@@ -1621,6 +1704,33 @@
       "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
       "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
     },
+    "node_modules/compress-commons": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz",
+      "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==",
+      "dependencies": {
+        "crc-32": "^1.2.0",
+        "crc32-stream": "^5.0.0",
+        "normalize-path": "^3.0.0",
+        "readable-stream": "^3.6.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/compress-commons/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -1721,6 +1831,42 @@
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
     },
+    "node_modules/crc-32": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+      "bin": {
+        "crc32": "bin/crc32.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/crc32-stream": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz",
+      "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==",
+      "dependencies": {
+        "crc-32": "^1.2.0",
+        "readable-stream": "^3.4.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/crc32-stream/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/cross-spawn": {
       "version": "6.0.5",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -3310,6 +3456,11 @@
       "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
       "dev": true
     },
+    "node_modules/fast-fifo": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+      "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="
+    },
     "node_modules/fast-json-stable-stringify": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
@@ -3460,16 +3611,16 @@
       }
     },
     "node_modules/form-data": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz",
-      "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
       "dependencies": {
         "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.5",
+        "combined-stream": "^1.0.8",
         "mime-types": "^2.1.12"
       },
       "engines": {
-        "node": ">= 0.12"
+        "node": ">= 6"
       }
     },
     "node_modules/formidable": {
@@ -3513,8 +3664,7 @@
     "node_modules/fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-      "dev": true
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
     },
     "node_modules/function-bind": {
       "version": "1.1.1",
@@ -3722,12 +3872,9 @@
       }
     },
     "node_modules/graceful-fs": {
-      "version": "4.1.11",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
-      "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
-      "engines": {
-        "node": ">=0.4.0"
-      }
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
     },
     "node_modules/graphemer": {
       "version": "1.4.0",
@@ -4086,7 +4233,6 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "devOptional": true,
       "dependencies": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -4683,6 +4829,17 @@
       "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz",
       "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk="
     },
+    "node_modules/lazystream": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
+      "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+      "dependencies": {
+        "readable-stream": "^2.0.5"
+      },
+      "engines": {
+        "node": ">= 0.6.3"
+      }
+    },
     "node_modules/le_node": {
       "version": "1.8.0",
       "resolved": "https://registry.npmjs.org/le_node/-/le_node-1.8.0.tgz",
@@ -5222,6 +5379,14 @@
         "validate-npm-package-license": "^3.0.1"
       }
     },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/normalize-url": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.2.0.tgz",
@@ -5592,8 +5757,7 @@
     "node_modules/process-nextick-args": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
-      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
-      "dev": true
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
     },
     "node_modules/progress": {
       "version": "1.1.8",
@@ -5700,6 +5864,11 @@
         }
       ]
     },
+    "node_modules/queue-tick": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
+      "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag=="
+    },
     "node_modules/range-parser": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -5786,7 +5955,6 @@
       "version": "2.3.8",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
       "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
-      "dev": true,
       "dependencies": {
         "core-util-is": "~1.0.0",
         "inherits": "~2.0.3",
@@ -5797,6 +5965,33 @@
         "util-deprecate": "~1.0.1"
       }
     },
+    "node_modules/readdir-glob": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
+      "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
+      "dependencies": {
+        "minimatch": "^5.1.0"
+      }
+    },
+    "node_modules/readdir-glob/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/readdir-glob/node_modules/minimatch": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/readline2": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz",
@@ -5904,6 +6099,20 @@
         "request": "^2.34"
       }
     },
+    "node_modules/request/node_modules/form-data": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+      "dev": true,
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.6",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 0.12"
+      }
+    },
     "node_modules/require-uncached": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
@@ -6308,11 +6517,19 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/streamx": {
+      "version": "2.15.1",
+      "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.1.tgz",
+      "integrity": "sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==",
+      "dependencies": {
+        "fast-fifo": "^1.1.0",
+        "queue-tick": "^1.0.1"
+      }
+    },
     "node_modules/string_decoder": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
       "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-      "dev": true,
       "dependencies": {
         "safe-buffer": "~5.1.0"
       }
@@ -6417,6 +6634,19 @@
       "resolved": "https://registry.npmjs.org/superagent-promise/-/superagent-promise-1.1.0.tgz",
       "integrity": "sha1-uvIti73UOamwfdEPjAj1T+JQNTM="
     },
+    "node_modules/superagent/node_modules/form-data": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
+      "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.6",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 0.12"
+      }
+    },
     "node_modules/superagent/node_modules/process-nextick-args": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -6587,6 +6817,16 @@
         "node": ">=0.8.0"
       }
     },
+    "node_modules/tar-stream": {
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz",
+      "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==",
+      "dependencies": {
+        "b4a": "^1.6.4",
+        "fast-fifo": "^1.2.0",
+        "streamx": "^2.15.0"
+      }
+    },
     "node_modules/tc-core-library-js": {
       "version": "2.4.1",
       "resolved": "git+ssh://git@github.com/appirio-tech/tc-core-library-js.git#f45352974dafe5a10c86fc50bdd59ef399b50c65",
@@ -6627,17 +6867,6 @@
       "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz",
       "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg=="
     },
-    "node_modules/tc-core-library-js/node_modules/combined-stream": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
-      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dependencies": {
-        "delayed-stream": "~1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
     "node_modules/tc-core-library-js/node_modules/fast-deep-equal": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -7229,6 +7458,32 @@
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
+    },
+    "node_modules/zip-stream": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz",
+      "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==",
+      "dependencies": {
+        "archiver-utils": "^4.0.1",
+        "compress-commons": "^5.0.1",
+        "readable-stream": "^3.6.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/zip-stream/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
     }
   }
 }
diff --git a/package.json b/package.json
index 5d74f99..eda15c3 100644
--- a/package.json
+++ b/package.json
@@ -29,10 +29,12 @@
   "dependencies": {
     "@gitbeaker/rest": "^39.13.0",
     "@octokit/rest": "^18.9.0",
+    "archiver": "^6.0.1",
     "axios": "^0.19.0",
     "circular-json": "^0.5.7",
     "config": "^1.30.0",
     "dynamoose": "^1.11.1",
+    "form-data": "^4.0.0",
     "fs-extra": "^7.0.0",
     "get-parameter-names": "^0.3.0",
     "global-request-logger": "^0.1.1",
diff --git a/services/GitlabService.js b/services/GitlabService.js
index 8dd94db..9932da7 100644
--- a/services/GitlabService.js
+++ b/services/GitlabService.js
@@ -73,7 +73,7 @@ class GitlabService {
   /** @type {User} */
   #user = null;
 
-  /** @type {Gitlab} */
+  /** @type {import('@gitbeaker/rest').Gitlab} */
   #gitlab = null;
 
   constructor(user) {
@@ -84,6 +84,15 @@ class GitlabService {
     this.#user = user;
   }
 
+  /**
+   * Get the full URL for a Gitlab repository from its full name.
+   * @param {String} repoFullName Repo full name
+   * @returns {String}
+   */
+  static getRepoUrl(repoFullName) {
+    return `${config.GITLAB_API_BASE_URL}/${repoFullName}`;
+  }
+
   /**
    * Helper method for initializing a GitlabService instance with an active
    * access token.
@@ -478,8 +487,8 @@ class GitlabService {
         acc += `deleted file mode ${file.a_mode}\n`;
       }
       if (file.diff && file.diff !== '') {
-        acc += `--- a/${file.new_file ? '/dev/null' : file.old_path}\n`;
-        acc += `+++ b/${file.deleted_file ? '/dev/null' : file.new_path}\n`;
+        acc += `--- ${file.new_file ? '/dev/null' : `a/${file.old_path}`}\n`;
+        acc += `+++ ${file.deleted_file ? '/dev/null' : `b/${file.new_path}`}\n`;
         acc += file.diff;
       } else if (file.renamed_file) {
         acc += 'similarity index 100%\n';
@@ -488,7 +497,9 @@ class GitlabService {
       }
       return acc;
     }, '');
+    console.log(`PATCH FILE STARTS::${mergeRequest.web_url}`);
     console.log(patchFile);
+    console.log(`PATCH FILE ENDS::${mergeRequest.web_url}`);
     return patchFile;
   }
 
diff --git a/services/PrivateForkService.js b/services/PrivateForkService.js
index 4d24411..8c25ff1 100644
--- a/services/PrivateForkService.js
+++ b/services/PrivateForkService.js
@@ -10,7 +10,6 @@ const errors = require('../utils/errors');
 const GitlabService = require('../services/GitlabService');
 const {GITLAB_ACCESS_LEVELS, USER_ROLES, USER_TYPES} = require('../constants');
 
-const ProjectChallengeMapping = models.ProjectChallengeMapping;
 const Project = models.Project;
 const User = models.User;
 const GitlabUserMapping = models.GitlabUserMapping;
@@ -30,24 +29,13 @@ async function process(payload) {
   logger.debug(`${logPrefix}: Member ID: ${memberId}`);
   logger.debug(`${logPrefix}: Member Handle: ${memberHandle}`);
   // Check if there are projects mapped to the challenge
-  const filterValues = {};
-  const filter = {
-    FilterExpression: '#challengeId = :challengeId',
-    ExpressionAttributeNames: {
-      '#challengeId': 'challengeId'
-    },
-    ExpressionAttributeValues: {
-      ':challengeId': challengeId
-    }
-  };
-  const projectChallengeMapping = await dbHelper.scan(ProjectChallengeMapping, filter, filterValues);
-  if (projectChallengeMapping.length === 0) {
+  const projectId = await dbHelper.queryProjectIdByChallengeId(challengeId);
+  if (!projectId) {
     logger.info(`${logPrefix} ProjectChallengeMapping not found for challengeId: ${challengeId}`);
     return;
   }
-  logger.debug(`${logPrefix} ProjectChallengeMapping: ${JSON.stringify(projectChallengeMapping)}`);
+  logger.debug(`${logPrefix} TCX ProjectId: ${projectId}`);
   // Get Project
-  const projectId = projectChallengeMapping[0].projectId;
   const project = await dbHelper.getById(Project, projectId);
   if (!project) {
     logger.info(`${logPrefix} Project not found for projectId: ${projectId}`);
diff --git a/services/PullRequestService.js b/services/PullRequestService.js
index 5cc37c9..28c1816 100644
--- a/services/PullRequestService.js
+++ b/services/PullRequestService.js
@@ -3,13 +3,24 @@
 const _ = require('lodash');
 const Joi = require('joi');
 const uuid = require('uuid').v4;
+const archiver = require('archiver');
 const models = require('../models');
 const dbHelper = require('../utils/db-helper');
 const logger = require('../utils/logger');
 const GitlabService = require('../services/GitlabService');
+const TopcoderApiHelper = require('../utils/topcoder-api-helper');
 
 const GitlabUserMapping = models.GitlabUserMapping;
 
+/**
+ * Normalizes a string to be used as a file name.
+ * @param {String} fileName File name to normalize
+ * @returns {String} Normalized file name
+ */
+function normalizeFileName(fileName) {
+  return fileName.replace(/[^a-zA-Z0-9_\-.]/g, '_');
+}
+
 /**
  * Handles a pull request creation event.
  * @param {Object} payload The event payload.
@@ -59,44 +70,53 @@ async function process(payload) {
     return;
   }
   logger.debug(`${logPrefix} Project: ${JSON.stringify(project)}`);
-  // 4. Find all repositories corresponding to the TCX project
+  // 4. Find the challenge ID for the TCX project
+  const challengeId = await dbHelper.queryChallengeIdByProjectId(project.id);
+  if (!challengeId) {
+    logger.info(`${logPrefix} ProjectChallengeMapping not found for projectId: ${project.id}`);
+    return;
+  }
+  logger.debug(`${logPrefix} Challenge ID: ${challengeId}`);
+  // 5. Find all repositories corresponding to the TCX project
   const repositories = await dbHelper.queryAllRepositoriesByProjectId(project.id);
   if (!repositories || repositories.length === 0) {
     logger.info(`${logPrefix} Repositories not found for projectId: ${project.id}`);
     return;
   }
   logger.debug(`${logPrefix} Repositories: ${JSON.stringify(repositories)}`);
-  // 5. Get co-pilot's GitlabUserMapping
+  // 6. Get co-pilot's GitlabUserMapping
   const copilot = await dbHelper.queryOneUserMappingByTCUsername(GitlabUserMapping, project.copilot);
   if (!copilot) {
     logger.info(`${logPrefix} GitlabUserMapping not found for copilot: ${project.copilot}`);
     return;
   }
   logger.debug(`${logPrefix} GitlabUserMapping[Copilot]: ${JSON.stringify(copilot)}`);
-  // 6. Get co-pilot's Gitlab user
+  // 7. Get co-pilot's Gitlab user
   const copilotGitlabUser = await dbHelper.queryOneUserByType(models.User, copilot.gitlabUsername, 'gitlab');
   if (!copilotGitlabUser) {
     logger.info(`${logPrefix} GitlabUser not found for copilot: ${project.copilot}`);
     return;
   }
   logger.debug(`${logPrefix} GitlabUser[Copilot]: ${JSON.stringify(copilotGitlabUser)}`);
-  // 7. For each project, get the repositories
-  const gitRepositories = await Promise.all(repositories.map((repo) => GitlabService.getRepository(copilotGitlabUser, repo.url)));
+  // 8. Init the Gitlab service for co-pilot
+  const copilotGitlabService = await GitlabService.create(copilotGitlabUser);
+  // 9. For each project, get the repositories
+  const gitRepositories = await Promise.all(repositories.map((repo) => copilotGitlabService.getRepository(repo.url)));
   if (!gitRepositories || gitRepositories.length === 0) {
     logger.info(`${logPrefix} Git repositories not found for repositories: ${JSON.stringify(repositories)}`);
     return;
   }
   logger.debug(`${logPrefix} Git repositories: ${JSON.stringify(gitRepositories)}`);
-  // 8. For each repository, get the merge requests
+  // 10. For each repository, get the merge requests
   const mergeRequests = await Promise.all(
-    gitRepositories.map((repo) => GitlabService.getOpenMergeRequestsByUser(copilotGitlabUser, repo, submitter.gitlabUserId))
+    gitRepositories.map((repo) => copilotGitlabService.getOpenMergeRequestsByUser(repo, submitter.gitlabUserId))
   );
   if (!mergeRequests || mergeRequests.length === 0) {
     logger.info(`${logPrefix} Merge requests not found for repositories: ${JSON.stringify(gitRepositories)}`);
     return;
   }
   logger.debug(`${logPrefix} Merge requests: ${JSON.stringify(mergeRequests)}`);
-  // 9. Ensure that there exists a merge request by the same member as the pull request for each project (if not, return)
+  // 11. Ensure that there exists a merge request by the same member as the pull request for each project (if not, return)
   // eslint-disable-next-line no-restricted-syntax
   for (let i = 0; i < mergeRequests.length; i += 1) {
     const mr = mergeRequests[i];
@@ -105,21 +125,54 @@ async function process(payload) {
       return;
     }
   }
-  // 10. Get the latest merge request for each project
+  // 12. Get the latest merge request for each project
   const latestMergeRequests = mergeRequests.map((mrs) => _.maxBy(mrs, (mr) => new Date(mr.created_at).getTime()));
   logger.debug(`${logPrefix} Latest merge requests: ${JSON.stringify(latestMergeRequests)}`);
-  // 11. Create patch files for each merge request
+  // 13. Create patch files for each merge request
   const patches = await Promise.all(
-    latestMergeRequests.map((mr) => GitlabService.getMergeRequestDiffPatches(copilotGitlabUser, mr))
+    latestMergeRequests.map((mr) => copilotGitlabService.getMergeRequestDiffPatches(mr))
   );
   if (!patches || patches.length !== latestMergeRequests.length) {
     logger.info(`${logPrefix} Patches not found for merge requests.`);
     return;
   }
   logger.debug(`${logPrefix} Patches: ${JSON.stringify(patches)}`);
-  // 11. Get the topcoder M2M token
-  // 10. Create a zip file containing all patch files
-  // 11. Use the submission API to submit the zip file
+  // 14. Create a zip file containing all patch files
+  logger.debug(`${logPrefix} Creating zip file...`);
+  const zipStream = archiver('zip');
+  const zipBufferPromise = new Promise((resolve, reject) => {
+    const buffers = [];
+    zipStream.on('data', (data) => {
+      buffers.push(data);
+    });
+    zipStream.on('end', () => {
+      resolve(Buffer.concat(buffers));
+    });
+    zipStream.on('error', reject);
+  });
+  patches.forEach((patch) => {
+    const buffer = Buffer.from(patch);
+    zipStream.append(buffer, {name: `${normalizeFileName(repoFullName)}.patch`});
+  });
+  await zipStream.finalize();
+  const zipBuffer = await zipBufferPromise;
+  logger.debug(`${logPrefix} Zip file size: ${zipBuffer.length}`);
+  // 15. Get the Topcoder user's member ID
+  const memberId = await TopcoderApiHelper.getTopcoderMemberId(submitter.topcoderUsername);
+  if (!memberId) {
+    logger.info(`${logPrefix} Member ID not found for topcoderUsername: ${submitter.topcoderUsername}`);
+    return;
+  }
+  logger.debug(`${logPrefix} Member ID: ${memberId}`);
+  // 16. Use the submission API to submit the zip file
+  logger.debug(`${logPrefix} Submitting the zip file...`);
+  const submission = await TopcoderApiHelper.createSubmission(
+    challengeId,
+    memberId,
+    zipBuffer,
+    `${correlationId}.zip`,
+  );
+  logger.debug(`${logPrefix} Submission: ${JSON.stringify(submission.data)}`);
 }
 
 process.schema = Joi.object().keys({
diff --git a/utils/db-helper.js b/utils/db-helper.js
index 873fca6..2c707b0 100644
--- a/utils/db-helper.js
+++ b/utils/db-helper.js
@@ -11,6 +11,24 @@ const logger = require('./logger');
  * @version 1.0
  */
 
+/**
+ * @typedef {Object} GitlabUserMapping
+ * @property {String} id the id
+ * @property {Number} gitlabUserId the gitlab user id
+ * @property {String} topcoderUsername the topcoder username
+ * @property {String} gitlabUsername the gitlab username
+ */
+
+/**
+ * @typedef {Object} Repository
+ * @property {String} id the id
+ * @property {String} projectId the project id
+ * @property {String} url the repository url
+ * @property {String} archived the archived flag
+ * @property {String} repoId the repository id
+ * @property {String} registeredWebhookId the registered webhook id
+ */
+
 /**
  * Get Data by model id
  * @param {Object} model The dynamoose model to query
@@ -150,7 +168,7 @@ async function queryOneUserByTypeAndRole(model, username, type, role) {
 /**
  * Query project by repository url
  * @param {String} repoUrl the repo url
- * @returns {Promise<Object>}
+ * @returns {Promise<Repository>}
  */
 async function queryOneProjectByRepositoryLink(repoUrl) {
   const projectId = await new Promise((resolve, reject) => {
@@ -187,7 +205,7 @@ async function queryOneProjectByRepositoryLink(repoUrl) {
  * Get single data by query parameters
  * @param {Object} model The dynamoose model to query
  * @param {String} tcusername The tc username
- * @returns {Promise<void>}
+ * @returns {Promise<GitlabUserMapping>}
  */
 async function queryOneUserMappingByTCUsername(model, tcusername) {
   return await new Promise((resolve, reject) => {
@@ -267,7 +285,7 @@ async function queryOneUserMappingByGithubUserId(model, userId) {
  * Get single data by query parameters
  * @param {Object} model The dynamoose model to query
  * @param {Number} userId The  The user id
- * @returns {Promise<void>}
+ * @returns {Promise<GitlabUserMapping>}
  */
 async function queryOneUserMappingByGitlabUserId(model, userId) {
   return await new Promise((resolve, reject) => {
@@ -490,6 +508,56 @@ async function releaseLockOnUser(id, lockId) {
   return user;
 }
 
+/**
+ * Find the TC Challenge ID for a given TCX project ID
+ * @param {String} projectId Project ID
+ * @returns {Promise<String | null>} Challenge ID
+ */
+async function queryChallengeIdByProjectId(projectId) {
+  const filter = {
+    FilterExpression: '#projectId = :projectId',
+    ExpressionAttributeNames: {
+      '#projectId': 'projectId'
+    },
+    ExpressionAttributeValues: {
+      ':projectId': projectId
+    }
+  };
+  return new Promise((resolve, reject) => {
+    models.ProjectChallengeMapping.scan(filter, (err, result) => {
+      if (err) {
+        return reject(err);
+      }
+      return resolve(result.count === 0 ? null : result[0].challengeId);
+    });
+  });
+}
+
+/**
+ * Find the TCX Project ID for a given TC Challenge ID
+ * @param {String} challengeId Challenge ID
+ * @returns {Promise<String | null>} Project ID
+ */
+async function queryProjectIdByChallengeId(challengeId) {
+  const filter = {
+    FilterExpression: '#challengeId = :challengeId',
+    ExpressionAttributeNames: {
+      '#challengeId': 'challengeId'
+    },
+    ExpressionAttributeValues: {
+      ':challengeId': challengeId
+    }
+  };
+  return new Promise((resolve, reject) => {
+    models.ProjectChallengeMapping.scan(filter, (err, result) => {
+      if (err) {
+        return reject(err);
+      }
+      return resolve(result.count === 0 ? null : result[0].projectId);
+    });
+  });
+}
+
 module.exports = {
   getById,
   scan,
@@ -512,5 +580,7 @@ module.exports = {
   removeCopilotPayment,
   removeIssue,
   acquireLockOnUser,
-  releaseLockOnUser
+  releaseLockOnUser,
+  queryChallengeIdByProjectId,
+  queryProjectIdByChallengeId
 };
diff --git a/utils/topcoder-api-helper.js b/utils/topcoder-api-helper.js
index de63381..1ba3092 100644
--- a/utils/topcoder-api-helper.js
+++ b/utils/topcoder-api-helper.js
@@ -13,9 +13,10 @@
 'use strict';
 
 const config = require('config');
-const axios = require('axios');
+const axios = require('axios').default;
 const _ = require('lodash');
 const circularJSON = require('circular-json');
+const FormData = require('form-data');
 
 const m2mAuth = require('tc-core-library-js').auth.m2m;
 
@@ -465,8 +466,43 @@ async function getProjectByDirectId(id, directId) {
   });
 }
 
-async function createSubmission(challengeId, submissionFileStream, submissionFileName, submissionType) {
-  // TODO: Implement submission creation
+/**
+ * Create a new submission.
+ * @param {String} challengeId Challenge ID
+ * @param {Number} memberId Member ID
+ * @param {Buffer} submissionFile Submission file
+ * @param {String} submissionFileName Submission file name
+ */
+async function createSubmission(challengeId, memberId, submissionFile, submissionFileName) {
+  try {
+    const formData = new FormData();
+    formData.append('submission', submissionFile, {
+      filename: submissionFileName,
+      contentType: 'application/zip',
+      knownLength: submissionFile.length
+    });
+    formData.append('type', 'Contest Submission');
+    formData.append('memberId', memberId);
+    formData.append('challengeId', challengeId);
+    const apiKey = await getM2Mtoken();
+    const res = await axios.post(`${config.TC_API_URL}/submissions`, formData, {
+      headers: {
+        authorization: `Bearer ${apiKey}`,
+        accept: 'application/json',
+        ...formData.getHeaders()
+      }
+    });
+    return res;
+  } catch (error) {
+    logger.error('createSubmission ERROR.');
+    if (error.isAxiosError) {
+      logger.error(`Request: ${JSON.stringify(error.config)}`);
+      logger.error(`Response Data: ${JSON.stringify(error.response.data)}`);
+    } else {
+      logger.error(`${error.message}`, error);
+    }
+    throw errors.convertTopcoderApiError(error, 'Failed to create submission.');
+  }
 }
 
 module.exports = {
@@ -485,5 +521,6 @@ module.exports = {
   cancelPrivateContent,
   assignUserAsRegistrant,
   removeResourceToChallenge,
-  getProjectByDirectId
+  getProjectByDirectId,
+  createSubmission
 };