diff --git a/doc/api/applications.md b/doc/api/applications.md
index b2ff061a54e3103833039aec9afd435563064262..1715131a717c22ce37739bfe2d4b36cdffbce693 100644
--- a/doc/api/applications.md
+++ b/doc/api/applications.md
@@ -32,7 +32,7 @@ Returns `200` if the request succeeds.
 POST /applications
 ```
 
-Parameters:
+Supported attributes:
 
 | Attribute      | Type    | Required | Description                      |
 |:---------------|:--------|:---------|:---------------------------------|
@@ -103,7 +103,7 @@ Returns `204` if the request succeeds.
 DELETE /applications/:id
 ```
 
-Parameters:
+Supported attributes:
 
 | Attribute | Type    | Required | Description                                         |
 |:----------|:--------|:---------|:----------------------------------------------------|
@@ -114,3 +114,38 @@ Example request:
 ```shell
 curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/applications/:id"
 ```
+
+## Renew an application secret
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/422420) in GitLab 16.11.
+
+Renews an application secret. Returns `200` if the request succeeds.
+
+```plaintext
+POST /applications/:id/renew-secret
+```
+
+Supported attributes:
+
+| Attribute | Type    | Required | Description                                         |
+|:----------|:--------|:---------|:----------------------------------------------------|
+| `id`      | integer | yes      | The ID of the application (not the `application_id`). |
+
+Example request:
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/applications/:id/renew-secret"
+```
+
+Example response:
+
+```json
+{
+    "id":1,
+    "application_id": "5832fc6e14300a0d962240a8144466eef4ee93ef0d218477e55f11cf12fc3737",
+    "application_name": "MyApplication",
+    "secret": "ee1dd64b6adc89cf7e2c23099301ccc2c61b441064e9324d963c46902a85ec34",
+    "callback_url": "http://redirect.uri",
+    "confidential": true
+}
+```
diff --git a/lib/api/applications.rb b/lib/api/applications.rb
index 39f1638301ba33202bb7c8e8c4ab6fba58a403af..3cf0d9bddce0bfeb19a96dff0355d144414abd2f 100644
--- a/lib/api/applications.rb
+++ b/lib/api/applications.rb
@@ -61,6 +61,26 @@ class Applications < ::API::Base
 
         no_content!
       end
+
+      desc 'Renew an application secret' do
+        detail 'Renew the secret of a specific application'
+        success code: 200, model: Entities::ApplicationWithSecret
+      end
+      params do
+        requires :id, type: Integer, desc: 'The ID of the application (not the application_id)'
+      end
+      post ':id/renew-secret' do
+        application = ApplicationsFinder.new(params).execute
+        break not_found!('Application') unless application
+
+        application.renew_secret
+
+        if application.save
+          present application, with: Entities::ApplicationWithSecret
+        else
+          render_validation_error!(application)
+        end
+      end
     end
   end
 end
diff --git a/spec/requests/api/applications_spec.rb b/spec/requests/api/applications_spec.rb
index 16e24807e6788b7fc8ab6feea7aa4a86623c8af4..5de9f47c6851d4ae32bf1b00b750d0ca7dd5ccdf 100644
--- a/spec/requests/api/applications_spec.rb
+++ b/spec/requests/api/applications_spec.rb
@@ -198,4 +198,53 @@
       end
     end
   end
+
+  describe "POST /application/:id/renew-secret" do
+    let(:path) { "/applications/#{application.id}/renew-secret" }
+
+    context 'user authorization' do
+      it_behaves_like 'POST request permissions for admin mode' do
+        let(:params) { {} }
+      end
+    end
+
+    context 'authenticated and authorized user' do
+      it 'can renew a secret token' do
+        application = Doorkeeper::Application.last
+        post api(path, admin, admin_mode: true), params: {}
+
+        expect(response).to have_gitlab_http_status(:created)
+        expect(json_response['secret']).not_to be nil
+        expect(application.secret_matches?(json_response['secret'])).not_to eq(true)
+      end
+
+      it 'return 404 when application_id not found' do
+        post api("/applications/#{non_existing_record_id}/renew-secret", admin, admin_mode: true)
+        expect(response).to have_gitlab_http_status(:not_found)
+      end
+
+      it 'return 400 when the operation is failed' do
+        allow_next_instance_of(ApplicationsFinder) do |finder|
+          allow(finder).to receive(:execute).and_return(application)
+        end
+        allow(application).to receive(:renew_secret).and_return(true)
+        allow(application).to receive(:valid?).and_return(false)
+        errors = ActiveModel::Errors.new(application)
+        errors.add(:name, 'Error 1')
+        allow(application).to receive(:errors).and_return(errors)
+
+        post api(path, admin, admin_mode: true)
+
+        expect(response).to have_gitlab_http_status(:bad_request)
+      end
+    end
+
+    context 'non-authenticated user' do
+      it 'cannot renew a secret token' do
+        post api(path)
+
+        expect(response).to have_gitlab_http_status(:unauthorized)
+      end
+    end
+  end
 end