diff --git a/ee/app/assets/javascripts/escalation_policies/components/escalation_policies_wrapper.vue b/ee/app/assets/javascripts/escalation_policies/components/escalation_policies_wrapper.vue
index 529c9f5bb9ddab9d5d4396e715882f0790ce4f60..ff971037a6382f1d3022eddce28e3459c65adb46 100644
--- a/ee/app/assets/javascripts/escalation_policies/components/escalation_policies_wrapper.vue
+++ b/ee/app/assets/javascripts/escalation_policies/components/escalation_policies_wrapper.vue
@@ -1,5 +1,13 @@
 <script>
-import { GlEmptyState, GlButton, GlModalDirective, GlLoadingIcon, GlAlert } from '@gitlab/ui';
+import {
+  GlEmptyState,
+  GlButton,
+  GlModalDirective,
+  GlLoadingIcon,
+  GlAlert,
+  GlSprintf,
+  GlLink,
+} from '@gitlab/ui';
 import * as Sentry from '@sentry/browser';
 import { s__ } from '~/locale';
 import { addEscalationPolicyModalId } from '../constants';
@@ -16,6 +24,9 @@ export const i18n = {
     description: s__(
       "EscalationPolicies|Choose who to email if those contacted first about an alert don't respond.",
     ),
+    unauthorizedDescription: s__(
+      "EscalationPolicies|Choose who to email if those contacted first about an alert don't respond. To access this feature, ask %{linkStart}a project Owner%{linkEnd} to grant you at least the Maintainer role.",
+    ),
     button: s__('EscalationPolicies|Add an escalation policy'),
   },
   policyCreatedAlert: {
@@ -34,13 +45,20 @@ export default {
     GlButton,
     GlLoadingIcon,
     GlAlert,
+    GlSprintf,
+    GlLink,
     AddEscalationPolicyModal,
     EscalationPolicy,
   },
   directives: {
     GlModal: GlModalDirective,
   },
-  inject: ['projectPath', 'emptyEscalationPoliciesSvgPath'],
+  inject: [
+    'projectPath',
+    'emptyEscalationPoliciesSvgPath',
+    'userCanCreateEscalationPolicy',
+    'accessLevelDescriptionPath',
+  ],
   data() {
     return {
       escalationPolicies: [],
@@ -108,10 +126,19 @@ export default {
     <gl-empty-state
       v-else
       :title="$options.i18n.emptyState.title"
-      :description="$options.i18n.emptyState.description"
       :svg-path="emptyEscalationPoliciesSvgPath"
     >
-      <template #actions>
+      <template #description>
+        <p v-if="userCanCreateEscalationPolicy">
+          {{ $options.i18n.emptyState.description }}
+        </p>
+        <gl-sprintf v-else :message="$options.i18n.emptyState.unauthorizedDescription">
+          <template #link="{ content }">
+            <gl-link :href="accessLevelDescriptionPath">{{ content }}</gl-link>
+          </template>
+        </gl-sprintf>
+      </template>
+      <template v-if="userCanCreateEscalationPolicy" #actions>
         <gl-button v-gl-modal="$options.addEscalationPolicyModalId" variant="confirm">
           {{ $options.i18n.emptyState.button }}
         </gl-button>
diff --git a/ee/app/assets/javascripts/escalation_policies/index.js b/ee/app/assets/javascripts/escalation_policies/index.js
index 090f375e7ec757b758960b0a7287353dcadf1c8d..632c12d9503f75c438fb5ebe71e89db331555b25 100644
--- a/ee/app/assets/javascripts/escalation_policies/index.js
+++ b/ee/app/assets/javascripts/escalation_policies/index.js
@@ -1,6 +1,7 @@
 import { defaultDataIdFromObject } from '@apollo/client/core';
 import Vue from 'vue';
 import VueApollo from 'vue-apollo';
+import { parseBoolean } from '~/lib/utils/common_utils';
 import createDefaultClient from '~/lib/graphql';
 import EscalationPoliciesWrapper from './components/escalation_policies_wrapper.vue';
 
@@ -28,7 +29,12 @@ export default () => {
 
   if (!el) return null;
 
-  const { emptyEscalationPoliciesSvgPath, projectPath = '' } = el.dataset;
+  const {
+    emptyEscalationPoliciesSvgPath,
+    projectPath = '',
+    userCanCreateEscalationPolicy,
+    accessLevelDescriptionPath,
+  } = el.dataset;
 
   return new Vue({
     el,
@@ -36,6 +42,8 @@ export default () => {
     provide: {
       projectPath,
       emptyEscalationPoliciesSvgPath,
+      userCanCreateEscalationPolicy: parseBoolean(userCanCreateEscalationPolicy),
+      accessLevelDescriptionPath,
     },
     render(createElement) {
       return createElement(EscalationPoliciesWrapper);
diff --git a/ee/app/assets/javascripts/oncall_schedules/components/oncall_schedules_wrapper.vue b/ee/app/assets/javascripts/oncall_schedules/components/oncall_schedules_wrapper.vue
index a52cca85527757f0ea63e672c39a9f4b99ffb44b..4713702ef6257e99a4d81f0c4a430d73b8351e75 100644
--- a/ee/app/assets/javascripts/oncall_schedules/components/oncall_schedules_wrapper.vue
+++ b/ee/app/assets/javascripts/oncall_schedules/components/oncall_schedules_wrapper.vue
@@ -60,6 +60,7 @@ export default {
     'projectPath',
     'escalationPoliciesPath',
     'userCanCreateSchedule',
+    'accessLevelDescriptionPath',
   ],
   data() {
     return {
@@ -170,7 +171,7 @@ export default {
         </p>
         <gl-sprintf v-else :message="$options.i18n.emptyState.unauthorizedDescription">
           <template #link="{ content }">
-            <gl-link href="project_members?sort=access_level_desc">{{ content }}</gl-link>
+            <gl-link :href="accessLevelDescriptionPath">{{ content }}</gl-link>
           </template>
         </gl-sprintf>
       </template>
diff --git a/ee/app/assets/javascripts/oncall_schedules/index.js b/ee/app/assets/javascripts/oncall_schedules/index.js
index 22000bd28cc0f8882878dca0d319fffac92023be..72f19027f247ff8b57ef99a3b58b6923748bee29 100644
--- a/ee/app/assets/javascripts/oncall_schedules/index.js
+++ b/ee/app/assets/javascripts/oncall_schedules/index.js
@@ -18,6 +18,7 @@ export default () => {
     timezones,
     escalationPoliciesPath,
     userCanCreateSchedule,
+    accessLevelDescriptionPath,
   } = el.dataset;
 
   apolloProvider.clients.defaultClient.cache.writeQuery({
@@ -36,6 +37,7 @@ export default () => {
       timezones: JSON.parse(timezones),
       escalationPoliciesPath,
       userCanCreateSchedule: parseBoolean(userCanCreateSchedule),
+      accessLevelDescriptionPath,
     },
     render(createElement) {
       return createElement(OnCallSchedulesWrapper);
diff --git a/ee/app/helpers/incident_management/escalation_policy_helper.rb b/ee/app/helpers/incident_management/escalation_policy_helper.rb
index f020c36849eb702d8bc2fa55c2d8018d66c21e8c..265f26809af453f340c6915f10abe4ef859e85f2 100644
--- a/ee/app/helpers/incident_management/escalation_policy_helper.rb
+++ b/ee/app/helpers/incident_management/escalation_policy_helper.rb
@@ -4,8 +4,16 @@ module IncidentManagement
   module EscalationPolicyHelper
     def escalation_policy_data(project)
       {
-          'project-path' => project.full_path,
-          'empty_escalation_policies_svg_path' => image_path('illustrations/empty-state/empty-escalation.svg')
+        'project-path' => project.full_path,
+        'empty_escalation_policies_svg_path' => image_path('illustrations/empty-state/empty-escalation.svg'),
+        'user_can_create_escalation_policy' => can?(
+          current_user,
+          :admin_incident_management_escalation_policy, project
+        ).to_s,
+        'access_level_description_path' => Gitlab::Routing.url_helpers.project_project_members_url(
+          project,
+          sort: 'access_level_desc'
+        )
       }
     end
   end
diff --git a/ee/app/helpers/incident_management/oncall_schedule_helper.rb b/ee/app/helpers/incident_management/oncall_schedule_helper.rb
index 9cb26a0ca1922b5ac96a15a59f1fc18f7cbf1d09..8f30c23fb6bf3273c0171f0f71826c87cbafff1f 100644
--- a/ee/app/helpers/incident_management/oncall_schedule_helper.rb
+++ b/ee/app/helpers/incident_management/oncall_schedule_helper.rb
@@ -8,7 +8,11 @@ def oncall_schedule_data(project)
         'empty-oncall-schedules-svg-path' => image_path('illustrations/empty-state/empty-on-call.svg'),
         'timezones' => timezone_data(format: :full).to_json,
         'escalation-policies-path' => project_incident_management_escalation_policies_path(project),
-        'user_can_create_schedule' => can?(current_user, :admin_incident_management_oncall_schedule, project).to_s
+        'user_can_create_schedule' => can?(current_user, :admin_incident_management_oncall_schedule, project).to_s,
+        'access_level_description_path' => Gitlab::Routing.url_helpers.project_project_members_url(
+          project,
+          sort: 'access_level_desc'
+        )
       }
     end
   end
diff --git a/ee/spec/frontend/escalation_policies/escalation_policy_wrapper_spec.js b/ee/spec/frontend/escalation_policies/escalation_policy_wrapper_spec.js
index 438d41cdc63dc0d35f6f29d0477df3b51a1e07b7..b1929e54fc8748a8487e8305dd0d4c649ebb0857 100644
--- a/ee/spec/frontend/escalation_policies/escalation_policy_wrapper_spec.js
+++ b/ee/spec/frontend/escalation_policies/escalation_policy_wrapper_spec.js
@@ -1,18 +1,26 @@
-import { GlEmptyState, GlLoadingIcon, GlAlert } from '@gitlab/ui';
+import { GlEmptyState, GlLoadingIcon, GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
 import { nextTick } from 'vue';
-import EscalationPoliciesWrapper from 'ee/escalation_policies/components/escalation_policies_wrapper.vue';
+import EscalationPoliciesWrapper, {
+  i18n,
+} from 'ee/escalation_policies/components/escalation_policies_wrapper.vue';
 import EscalationPolicy from 'ee/escalation_policies/components/escalation_policy.vue';
 import AddEscalationPolicyModal from 'ee/escalation_policies/components/add_edit_escalation_policy_modal.vue';
 import { parsePolicy } from 'ee/escalation_policies/utils';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
 import mockEscalationPolicies from './mocks/mockPolicies.json';
 
 describe('Escalation Policies Wrapper', () => {
   let wrapper;
   const emptyEscalationPoliciesSvgPath = 'illustration/path.svg';
   const projectPath = 'group/project';
+  const accessLevelDescriptionPath = 'group/project/-/project_members?sort=access_level_desc';
 
-  function mountComponent({ loading = false, escalationPolicies = [] } = {}) {
+  function mountComponent({
+    loading = false,
+    escalationPolicies = [],
+    userCanCreateEscalationPolicy = true,
+    isShallowExtendedMount = true,
+  } = {}) {
     const $apollo = {
       queries: {
         escalationPolicies: {
@@ -20,20 +28,30 @@ describe('Escalation Policies Wrapper', () => {
         },
       },
     };
-    wrapper = shallowMountExtended(EscalationPoliciesWrapper, {
+
+    const mountProps = {
       provide: {
         emptyEscalationPoliciesSvgPath,
         projectPath,
+        userCanCreateEscalationPolicy,
+        accessLevelDescriptionPath,
       },
       mocks: {
         $apollo,
       },
+      stubs: {
+        GlSprintf,
+      },
       data() {
         return {
           escalationPolicies,
         };
       },
-    });
+    };
+
+    wrapper = isShallowExtendedMount
+      ? shallowMountExtended(EscalationPoliciesWrapper, mountProps)
+      : mountExtended(EscalationPoliciesWrapper, mountProps);
   }
 
   beforeEach(() => {
@@ -76,6 +94,24 @@ describe('Escalation Policies Wrapper', () => {
     });
   });
 
+  describe('Escalation policy empty state', () => {
+    it('should allow to create policy when user is at least a maintainer', () => {
+      mountComponent({ isShallowExtendedMount: false });
+
+      expect(findEmptyState().props('title')).toBe(i18n.emptyState.title);
+      expect(wrapper.findByText(i18n.emptyState.description).exists()).toBe(true);
+      expect(wrapper.findByRole('button', { name: i18n.emptyState.button }).exists()).toBe(true);
+    });
+
+    it('should show message about role restrictions when user is below maintainer level', () => {
+      mountComponent({ userCanCreateEscalationPolicy: false, isShallowExtendedMount: false });
+
+      expect(findEmptyState().props('title')).toBe(i18n.emptyState.title);
+      expect(wrapper.findComponent(GlLink).exists()).toBe(true);
+      expect(wrapper.findByRole('button', { name: i18n.emptyState.button }).exists()).toBe(false);
+    });
+  });
+
   describe('Escalation policy created alert', () => {
     it('should display alert when when policy created', async () => {
       mountComponent({
diff --git a/ee/spec/frontend/oncall_schedule/oncall_schedule_wrapper_spec.js b/ee/spec/frontend/oncall_schedule/oncall_schedule_wrapper_spec.js
index 11d6515b7fad8759555384055f5ee33c7a742388..a14b75075e016fc46be281224b78954df26e54a9 100644
--- a/ee/spec/frontend/oncall_schedule/oncall_schedule_wrapper_spec.js
+++ b/ee/spec/frontend/oncall_schedule/oncall_schedule_wrapper_spec.js
@@ -21,6 +21,7 @@ describe('On-call schedule wrapper', () => {
   const emptyOncallSchedulesSvgPath = 'illustration/path.svg';
   const projectPath = 'group/project';
   const escalationPoliciesPath = 'group/project/-/escalation_policies';
+  const accessLevelDescriptionPath = 'group/project/-/project_members?sort=access_level_desc';
 
   function mountComponent({
     loading = false,
@@ -48,6 +49,7 @@ describe('On-call schedule wrapper', () => {
         escalationPoliciesPath,
         userCanCreateSchedule,
         timezones: mockTimezones,
+        accessLevelDescriptionPath,
       },
       directives: {
         GlTooltip: createMockDirective(),
@@ -85,6 +87,7 @@ describe('On-call schedule wrapper', () => {
         projectPath,
         escalationPoliciesPath,
         userCanCreateSchedule: true,
+        accessLevelDescriptionPath,
       },
     });
   }
diff --git a/ee/spec/helpers/incident_management/escalation_policy_helper_spec.rb b/ee/spec/helpers/incident_management/escalation_policy_helper_spec.rb
index be22a16cf0c1398c12414f2d282f4dc20f46eebc..71fea6ab7dc79650e857df6e0d4f0614648fed88 100644
--- a/ee/spec/helpers/incident_management/escalation_policy_helper_spec.rb
+++ b/ee/spec/helpers/incident_management/escalation_policy_helper_spec.rb
@@ -2,16 +2,30 @@
 
 require 'spec_helper'
 
-RSpec.describe IncidentManagement::EscalationPolicyHelper do
+RSpec.describe IncidentManagement::EscalationPolicyHelper, feature_category: :incident_management do
+  let_it_be(:user) { create(:user) }
   let_it_be(:project) { create(:project) }
 
   describe '#escalation_policy_data' do
     subject(:data) { helper.escalation_policy_data(project) }
 
-    it 'returns scalation policies data' do
+    before do
+      allow(helper).to receive(:current_user) { user }
+      allow(helper).to receive(:can?).with(
+        user,
+        :admin_incident_management_escalation_policy, project
+      ).and_return(false)
+    end
+
+    it 'returns escalation policies data' do
       is_expected.to eq(
         'project-path' => project.full_path,
-        'empty_escalation_policies_svg_path' => helper.image_path('illustrations/empty-state/empty-escalation.svg')
+        'empty_escalation_policies_svg_path' => helper.image_path('illustrations/empty-state/empty-escalation.svg'),
+        'user_can_create_escalation_policy' => 'false',
+        'access_level_description_path' => Gitlab::Routing.url_helpers.project_project_members_url(
+          project,
+          sort: 'access_level_desc'
+        )
       )
     end
   end
diff --git a/ee/spec/helpers/incident_management/oncall_schedule_helper_spec.rb b/ee/spec/helpers/incident_management/oncall_schedule_helper_spec.rb
index 06ffbab2bc9a3a6f230a6609ef6a1e5d719339dc..2341de26c8a5d785e66c5d61883489910d155f23 100644
--- a/ee/spec/helpers/incident_management/oncall_schedule_helper_spec.rb
+++ b/ee/spec/helpers/incident_management/oncall_schedule_helper_spec.rb
@@ -20,7 +20,11 @@
         'empty-oncall-schedules-svg-path' => helper.image_path('illustrations/empty-state/empty-on-call.svg'),
         'timezones' => helper.timezone_data(format: :full).to_json,
         'escalation-policies-path' => project_incident_management_escalation_policies_path(project),
-        'user_can_create_schedule' => 'false'
+        'user_can_create_schedule' => 'false',
+        'access_level_description_path' => Gitlab::Routing.url_helpers.project_project_members_url(
+          project,
+          sort: 'access_level_desc'
+        )
       )
     end
   end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 95d2015d78cfc907f602c57667654401fb996685..75c2c38d1e1836865127e31954e7f8b5fdb86feb 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -16436,6 +16436,9 @@ msgstr ""
 msgid "EscalationPolicies|Choose who to email if those contacted first about an alert don't respond."
 msgstr ""
 
+msgid "EscalationPolicies|Choose who to email if those contacted first about an alert don't respond. To access this feature, ask %{linkStart}a project Owner%{linkEnd} to grant you at least the Maintainer role."
+msgstr ""
+
 msgid "EscalationPolicies|Create an escalation policy in GitLab"
 msgstr ""