diff --git a/ee/lib/api/group_service_accounts.rb b/ee/lib/api/group_service_accounts.rb
index d3186918d17b74b6e246cf9fe8baa943ead89c42..09125897501101b3f7d888c3964c193ee4658dc6 100644
--- a/ee/lib/api/group_service_accounts.rb
+++ b/ee/lib/api/group_service_accounts.rb
@@ -12,6 +12,7 @@ class GroupServiceAccounts < ::API::Base
       set_current_organization
     end
 
+    helpers ::API::Helpers::PersonalAccessTokensHelpers
     helpers do
       def user
         user_group.provisioned_users.find_by_id(params[:user_id])
@@ -128,12 +129,10 @@ def validate_service_account_user
           end
 
           params do
-            requires :name, type: String, desc: 'The name of the personal access token'
+            use :create_personal_access_token_params
             requires :scopes, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
               values: ::Gitlab::Auth.all_available_scopes.map(&:to_s),
               desc: 'The array of scopes of the personal access token'
-            optional :expires_at, type: Date,
-              desc: 'The expiration date of the personal access token in ISO 8601 format'
           end
 
           post do
diff --git a/ee/spec/requests/api/group_service_accounts_spec.rb b/ee/spec/requests/api/group_service_accounts_spec.rb
index 67663a8041651c67e8892216d187cb52328f88b5..f51f7c31e99e68b03fd2ef171e101982b82b3197 100644
--- a/ee/spec/requests/api/group_service_accounts_spec.rb
+++ b/ee/spec/requests/api/group_service_accounts_spec.rb
@@ -501,9 +501,10 @@
 
   describe "POST /groups/:id/service_accounts/:user_id/personal_access_tokens" do
     let(:name) { 'new pat' }
+    let(:description) { 'description' }
     let(:expires_at) { 3.days.from_now.to_date.to_s }
     let(:scopes) { %w[api read_user] }
-    let(:params) { { name: name, scopes: scopes, expires_at: expires_at } }
+    let(:params) { { name: name, description: description, expires_at: expires_at, scopes: scopes } }
 
     subject(:perform_request) do
       post(
@@ -530,6 +531,7 @@
 
             expect(response).to have_gitlab_http_status(:created)
             expect(json_response['name']).to eq(name)
+            expect(json_response['description']).to eq(description)
             expect(json_response['scopes']).to eq(scopes)
             expect(json_response['expires_at']).to eq(expires_at)
             expect(json_response['id']).to be_present
diff --git a/ee/spec/requests/api/users_spec.rb b/ee/spec/requests/api/users_spec.rb
index 0a43ad565f899b7d6d054d1f0fb7140e57d49ebc..dd7413cc4e94d13172e5478a5fe812bb6d8907b8 100644
--- a/ee/spec/requests/api/users_spec.rb
+++ b/ee/spec/requests/api/users_spec.rb
@@ -511,9 +511,11 @@
 
   describe 'POST /user/personal_access_tokens', :with_current_organization do
     let(:name) { 'new pat' }
+    let(:description) { 'description' }
+    let(:expires_at) { 3.days.from_now.to_date.to_s }
     let(:scopes) { %w[k8s_proxy] }
     let(:path) { "/user/personal_access_tokens" }
-    let(:params) { { name: name, scopes: scopes } }
+    let(:params) { { name: name, description: description, expires_at: expires_at, scopes: scopes } }
 
     context 'when disable_personal_access_tokens feature is available' do
       before do
@@ -542,8 +544,9 @@
 
           expect(response).to have_gitlab_http_status(:created)
           expect(json_response['name']).to eq(name)
+          expect(json_response['description']).to eq(description)
+          expect(json_response['expires_at']).to eq(expires_at)
           expect(json_response['scopes']).to eq(scopes)
-          expect(json_response['expires_at']).to eq(1.day.from_now.to_date.to_s)
           expect(json_response['id']).to be_present
           expect(json_response['created_at']).to be_present
           expect(json_response['active']).to be_truthy
diff --git a/lib/api/helpers/personal_access_tokens_helpers.rb b/lib/api/helpers/personal_access_tokens_helpers.rb
index cab5ec02e1f507e7ac747c71fae6f56030e53bb6..94fa8e0a99130dd04084e27a8e72f55ccdcd0f81 100644
--- a/lib/api/helpers/personal_access_tokens_helpers.rb
+++ b/lib/api/helpers/personal_access_tokens_helpers.rb
@@ -26,6 +26,15 @@ module PersonalAccessTokensHelpers
         optional :sort, type: String, desc: 'Sort tokens', documentation: { example: 'created_at_desc' }
       end
 
+      params :create_personal_access_token_params do
+        requires :name, type: String, desc: 'The name of the access token', documentation: { example: 'My token' }
+        optional :description, type: String, desc: 'The description of the access token',
+          documentation: { example: 'A token used for k8s' }
+        optional :expires_at, type: Date, desc: "Expiration date of the access token in ISO format (YYYY-MM-DD). " \
+                                            "If undefined, the date is set to the maximum allowable lifetime limit.",
+          documentation: { example: '2021-01-31' }
+      end
+
       def finder_params(current_user)
         user_param =
           if current_user.can_admin_all_resources?
diff --git a/lib/api/resource_access_tokens.rb b/lib/api/resource_access_tokens.rb
index 161a68a1bcc0940c455dc0a5878bfd77f7c1ee62..68fa7079deabe12c4a62dfa9b9890e6e80085c49 100644
--- a/lib/api/resource_access_tokens.rb
+++ b/lib/api/resource_access_tokens.rb
@@ -95,14 +95,11 @@ class ResourceAccessTokens < ::API::Base
           success Entities::ResourceAccessTokenWithToken
         end
         params do
+          use :create_personal_access_token_params
           requires :id,
             type: String,
             desc: "The #{source_type} ID",
             documentation: { example: 2 }
-          requires :name,
-            type: String,
-            desc: "Resource access token name",
-            documentation: { example: 'test' }
           requires :scopes,
             type: Array[String],
             values: ::Gitlab::Auth.resource_bot_scopes.map(&:to_s),
@@ -113,10 +110,6 @@ class ResourceAccessTokens < ::API::Base
             desc: "The expiration date of the token",
             default: PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now,
             documentation: { example: '"2021-01-31' }
-          optional :description,
-            type: String,
-            desc: "Resource access token description",
-            documentation: { example: 'test description' }
           optional :access_level,
             type: Integer,
             values: ALLOWED_RESOURCE_ACCESS_LEVELS.values,
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 75bb425cf928a76e18b61fef5c5feaf20b0018f0..62495a46f5287dfb733bbf7fe4242fbde67383f9 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -31,6 +31,7 @@ class Users < ::API::Base
 
       helpers Helpers::UsersHelpers
       helpers Gitlab::Tracking::Helpers::WeakPasswordErrorEvent
+      helpers ::API::Helpers::PersonalAccessTokensHelpers
 
       helpers do
         def custom_order_by_or_sort?
@@ -1085,11 +1086,9 @@ def target_user
             success Entities::PersonalAccessTokenWithToken
           end
           params do
-            requires :name, type: String, desc: 'The name of the personal access token'
+            use :create_personal_access_token_params
             requires :scopes, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, values: ::Gitlab::Auth.all_available_scopes.map(&:to_s),
               desc: 'The array of scopes of the personal access token'
-            optional :description, type: String, desc: 'The description of the personal access token'
-            optional :expires_at, type: Date, desc: 'The expiration date in the format YEAR-MONTH-DAY of the personal access token'
           end
           post feature_category: :system_access do
             response = ::PersonalAccessTokens::CreateService.new(
@@ -1112,6 +1111,8 @@ def target_user
         set_current_organization
       end
 
+      helpers ::API::Helpers::PersonalAccessTokensHelpers
+
       # Enabling /user endpoint for the v3 version to allow oauth
       # authentication through this endpoint.
       version %w[v3 v4], using: :path do
@@ -1534,14 +1535,12 @@ def set_user_status(include_missing_params:)
           success Entities::PersonalAccessTokenWithToken
         end
         params do
-          requires :name, type: String, desc: 'The name of the personal access token'
+          use :create_personal_access_token_params
           # NOTE: for security reasons only the k8s_proxy scope is allowed at the moment.
           # See details in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131923#note_1571272897
           # and in https://gitlab.com/gitlab-org/gitlab/-/issues/425171
           requires :scopes, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, values: [::Gitlab::Auth::K8S_PROXY_SCOPE].map(&:to_s),
             desc: 'The array of scopes of the personal access token'
-          optional :description, type: String, desc: 'The description of the personal access token'
-          optional :expires_at, type: Date, default: -> { 1.day.from_now.to_date }, desc: 'The expiration date in the format YEAR-MONTH-DAY of the personal access token'
         end
         post feature_category: :system_access do
           response = ::PersonalAccessTokens::CreateService.new(
diff --git a/spec/requests/api/resource_access_tokens_spec.rb b/spec/requests/api/resource_access_tokens_spec.rb
index 01f2237c3f0f082bfbc97b99be48caad71f0811c..19f4d27cab393f34dcb25cdc71b2bd88e12dd631 100644
--- a/spec/requests/api/resource_access_tokens_spec.rb
+++ b/spec/requests/api/resource_access_tokens_spec.rb
@@ -446,7 +446,11 @@
     end
 
     context "POST #{source_type}s/:id/access_tokens" do
-      let(:params) { { name: "test", scopes: ["api"], expires_at: expires_at, access_level: access_level } }
+      let(:params) do
+        { name: "test", description: "description", scopes: ["api"], expires_at: expires_at,
+          access_level: access_level }
+      end
+
       let(:expires_at) { 1.month.from_now }
       let(:access_level) { 20 }
 
@@ -462,6 +466,7 @@
 
               expect(response).to have_gitlab_http_status(:created)
               expect(json_response["name"]).to eq("test")
+              expect(json_response["description"]).to eq("description")
               expect(json_response["scopes"]).to eq(["api"])
               expect(json_response["access_level"]).to eq(20)
               expect(json_response["expires_at"]).to eq(expires_at.to_date.iso8601)
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 8c8f1902b9bca0d53a576493f612819d3e86ecc9..73e9a80846753bc6c70e835ae8abb1485dd8356a 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -5084,9 +5084,10 @@ def request
 
     let(:name) { 'new pat' }
     let(:description) { 'new pat description' }
+    let(:expires_at) { 3.days.from_now.to_date.to_s }
     let(:scopes) { %w[k8s_proxy] }
     let(:path) { "/user/personal_access_tokens" }
-    let(:params) { { name: name, scopes: scopes, description: description } }
+    let(:params) { { name: name, expires_at: expires_at, description: description, scopes: scopes } }
 
     let(:all_scopes) do
       ::Gitlab::Auth::API_SCOPES + ::Gitlab::Auth::AI_FEATURES_SCOPES + ::Gitlab::Auth::OPENID_SCOPES +
@@ -5155,7 +5156,7 @@ def request
       expect(json_response['name']).to eq(name)
       expect(json_response['description']).to eq(description)
       expect(json_response['scopes']).to eq(scopes)
-      expect(json_response['expires_at']).to eq(1.day.from_now.to_date.to_s)
+      expect(json_response['expires_at']).to eq(expires_at)
       expect(json_response['id']).to be_present
       expect(json_response['created_at']).to be_present
       expect(json_response['active']).to be_truthy
@@ -5176,25 +5177,6 @@ def request
           expect(json_response['active']).to be_falsey
         end
       end
-
-      context 'when expires_at is in the future' do
-        let(:expires_at) { 1.month.from_now.to_date }
-
-        it 'creates a personal access token' do
-          post api(path, user), params: params
-
-          expect(response).to have_gitlab_http_status(:created)
-          expect(json_response['name']).to eq(name)
-          expect(json_response['description']).to eq(description)
-          expect(json_response['scopes']).to eq(scopes)
-          expect(json_response['expires_at']).to eq(1.month.from_now.to_date.to_s)
-          expect(json_response['id']).to be_present
-          expect(json_response['created_at']).to be_present
-          expect(json_response['active']).to be_truthy
-          expect(json_response['revoked']).to be_falsey
-          expect(json_response['token']).to be_present
-        end
-      end
     end
 
     context 'when an error is thrown by the model' do