diff --git a/packages/tests/fixtures/peertube-plugin-test-five/main.js b/packages/tests/fixtures/peertube-plugin-test-five/main.js
index 07dd18654..14e9ccd3d 100644
--- a/packages/tests/fixtures/peertube-plugin-test-five/main.js
+++ b/packages/tests/fixtures/peertube-plugin-test-five/main.js
@@ -9,6 +9,10 @@ async function register ({
   router.post('/form/post/mirror', (req, res) => {
     res.json(req.body)
   })
+
+  router.post('/form/post/mirror-raw-body', (req, res) => {
+    res.json(JSON.parse(req.rawBody))
+  })
 }
 
 async function unregister () {
diff --git a/packages/tests/src/plugins/plugin-router.ts b/packages/tests/src/plugins/plugin-router.ts
index 6f3571c05..82cf0321e 100644
--- a/packages/tests/src/plugins/plugin-router.ts
+++ b/packages/tests/src/plugins/plugin-router.ts
@@ -80,6 +80,24 @@ describe('Test plugin helpers', function () {
     }
   })
 
+  it('Should mirror the raw post body', async function () {
+    const body = {
+      torso: 'arms',
+      legs: 'feet'
+    }
+
+    for (const path of basePaths) {
+      const res = await makePostBodyRequest({
+        url: server.url,
+        path: path + 'form/post/mirror-raw-body',
+        fields: body,
+        expectedStatus: HttpStatusCode.OK_200
+      })
+
+      expect(res.body).to.deep.equal(body)
+    }
+  })
+
   it('Should remove the plugin and remove the routes', async function () {
     await server.plugins.uninstall({ npmName: 'peertube-plugin-test-five' })
 
diff --git a/server/core/types/express.d.ts b/server/core/types/express.d.ts
index 4f669ffee..2cabacc04 100644
--- a/server/core/types/express.d.ts
+++ b/server/core/types/express.d.ts
@@ -54,6 +54,7 @@ declare module 'express' {
   export interface Request {
     query: any
     method: HttpMethodType
+    rawBody: Buffer // Allow plugin routes to access the raw body
   }
 
   // ---------------------------------------------------------------------------
diff --git a/server/server.ts b/server/server.ts
index edb942ce8..23e6cb1e6 100644
--- a/server/server.ts
+++ b/server/server.ts
@@ -211,6 +211,10 @@ app.use(express.json({
         message: 'Invalid digest'
       })
     }
+
+    if (req.originalUrl.startsWith('/plugins/')) {
+      req.rawBody = buf
+    }
   }
 }))
 
diff --git a/support/doc/plugins/guide.md b/support/doc/plugins/guide.md
index 2ad212a15..3f85c5791 100644
--- a/support/doc/plugins/guide.md
+++ b/support/doc/plugins/guide.md
@@ -247,7 +247,7 @@ function register ({
   router.get('/ping', (req, res) => res.json({ message: 'pong' }))
 
   // Users are automatically authenticated
-  router.get('/auth', async (res, res) => {
+  router.get('/auth', async (req, res) => {
     const user = await peertubeHelpers.user.getAuthUser(res)
 
     const isAdmin = user.role === 0
@@ -261,6 +261,15 @@ function register ({
       isUser
     })
   })
+
+  router.post('/webhook', async (req, res) => {
+    const rawBody = req.rawBody // Buffer containing the raw body
+
+    handleRawBody(rawBody)
+
+    res.status(204)
+  })
+
 }
 ```