diff --git a/pkg/services/container/ape.go b/pkg/services/container/ape.go
index fb1cc8121..3ea591c6a 100644
--- a/pkg/services/container/ape.go
+++ b/pkg/services/container/ape.go
@@ -164,11 +164,26 @@ func (ac *apeChecker) List(ctx context.Context, req *container.ListRequest) (*co
 		reqProps,
 	)
 
+	groups, err := aperequest.Groups(ac.frostFSIDClient, pk)
+	if err != nil {
+		return nil, fmt.Errorf("failed to get group ids: %w", err)
+	}
+
+	// Policy contract keeps group related chains as namespace-group pair.
+	for i := range groups {
+		groups[i] = fmt.Sprintf("%s:%s", namespace, groups[i])
+	}
+
 	rt := policyengine.NewRequestTargetWithNamespace(namespace)
 	rt.User = &policyengine.Target{
 		Type: policyengine.User,
 		Name: fmt.Sprintf("%s:%s", namespace, pk.Address()),
 	}
+	rt.Groups = make([]policyengine.Target, len(groups))
+	for i := range groups {
+		rt.Groups[i] = policyengine.GroupTarget(groups[i])
+	}
+
 	s, found, err := ac.router.IsAllowed(apechain.Ingress, rt, request)
 	if err != nil {
 		return nil, err
@@ -222,11 +237,26 @@ func (ac *apeChecker) Put(ctx context.Context, req *container.PutRequest) (*cont
 		reqProps,
 	)
 
+	groups, err := aperequest.Groups(ac.frostFSIDClient, pk)
+	if err != nil {
+		return nil, fmt.Errorf("failed to get group ids: %w", err)
+	}
+
+	// Policy contract keeps group related chains as namespace-group pair.
+	for i := range groups {
+		groups[i] = fmt.Sprintf("%s:%s", namespace, groups[i])
+	}
+
 	rt := policyengine.NewRequestTargetWithNamespace(namespace)
 	rt.User = &policyengine.Target{
 		Type: policyengine.User,
 		Name: fmt.Sprintf("%s:%s", namespace, pk.Address()),
 	}
+	rt.Groups = make([]policyengine.Target, len(groups))
+	for i := range groups {
+		rt.Groups[i] = policyengine.GroupTarget(groups[i])
+	}
+
 	s, found, err := ac.router.IsAllowed(apechain.Ingress, rt, request)
 	if err != nil {
 		return nil, err
@@ -311,6 +341,16 @@ func (ac *apeChecker) validateContainerBoundedOperation(ctx context.Context, con
 		namespace = cntNamespace
 	}
 
+	groups, err := aperequest.Groups(ac.frostFSIDClient, pk)
+	if err != nil {
+		return fmt.Errorf("failed to get group ids: %w", err)
+	}
+
+	// Policy contract keeps group related chains as namespace-group pair.
+	for i := range groups {
+		groups[i] = fmt.Sprintf("%s:%s", namespace, groups[i])
+	}
+
 	request := aperequest.NewRequest(
 		op,
 		aperequest.NewResource(
@@ -321,7 +361,7 @@ func (ac *apeChecker) validateContainerBoundedOperation(ctx context.Context, con
 	)
 
 	s, found, err := ac.router.IsAllowed(apechain.Ingress,
-		policyengine.NewRequestTargetExtended(namespace, id.EncodeToString(), fmt.Sprintf("%s:%s", namespace, pk.Address()), nil),
+		policyengine.NewRequestTargetExtended(namespace, id.EncodeToString(), fmt.Sprintf("%s:%s", namespace, pk.Address()), groups),
 		request)
 	if err != nil {
 		return err
diff --git a/pkg/services/container/ape_test.go b/pkg/services/container/ape_test.go
index 90c0225dd..a6f0fb222 100644
--- a/pkg/services/container/ape_test.go
+++ b/pkg/services/container/ape_test.go
@@ -44,6 +44,7 @@ const (
 func TestAPE(t *testing.T) {
 	t.Parallel()
 	t.Run("allow then deny get container", testAllowThenDenyGetContainerRuleDefined)
+	t.Run("allow by group id", TestAllowByGroupIDs)
 	t.Run("deny get container no rule found", testDenyGetContainerNoRuleFound)
 	t.Run("deny get container for others", testDenyGetContainerForOthers)
 	t.Run("deny get container by user claim tag", testDenyGetContainerByUserClaimTag)
@@ -145,6 +146,104 @@ func testAllowThenDenyGetContainerRuleDefined(t *testing.T) {
 	require.Contains(t, errAccessDenied.Reason(), chain.AccessDenied.String())
 }
 
+func TestAllowByGroupIDs(t *testing.T) {
+	t.Parallel()
+	srv := &srvStub{
+		calls: map[string]int{},
+	}
+	router := inmemory.NewInMemory()
+	contRdr := &containerStub{
+		c: map[cid.ID]*containercore.Container{},
+	}
+	ir := &irStub{
+		keys: [][]byte{},
+	}
+	nm := &netmapStub{}
+
+	pk, err := keys.NewPrivateKey()
+	require.NoError(t, err)
+
+	frostfsIDSubjectReader := &frostfsidStub{
+		subjects: map[util.Uint160]*client.Subject{
+			pk.PublicKey().GetScriptHash(): {
+				KV: map[string]string{
+					"tag-attr1": "value1",
+					"tag-attr2": "value2",
+				},
+			},
+		},
+		subjectsExt: map[util.Uint160]*client.SubjectExtended{
+			pk.PublicKey().GetScriptHash(): {
+				KV: map[string]string{
+					"tag-attr1": "value1",
+					"tag-attr2": "value2",
+				},
+				Groups: []*client.Group{
+					{
+						ID:   1,
+						Name: "Group#1",
+					},
+				},
+			},
+		},
+	}
+	apeSrv := NewAPEServer(router, contRdr, ir, nm, frostfsIDSubjectReader, srv)
+
+	contID := cidtest.ID()
+	testContainer := containertest.Container()
+	pp := netmap.PlacementPolicy{}
+	require.NoError(t, pp.DecodeString("REP 1"))
+	testContainer.SetPlacementPolicy(pp)
+	contRdr.c[contID] = &containercore.Container{Value: testContainer}
+
+	nm.currentEpoch = 100
+	nm.netmaps = map[uint64]*netmap.NetMap{}
+	var testNetmap netmap.NetMap
+	testNetmap.SetEpoch(nm.currentEpoch)
+	testNetmap.SetNodes([]netmap.NodeInfo{{}})
+	nm.netmaps[nm.currentEpoch] = &testNetmap
+	nm.netmaps[nm.currentEpoch-1] = &testNetmap
+
+	req := &container.GetRequest{}
+	req.SetBody(&container.GetRequestBody{})
+	var refContID refs.ContainerID
+	contID.WriteToV2(&refContID)
+	req.GetBody().SetContainerID(&refContID)
+
+	require.NoError(t, signature.SignServiceMessage(&pk.PrivateKey, req))
+
+	_, _, err = router.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.GroupTarget(":1"), &chain.Chain{
+		Rules: []chain.Rule{
+			{
+				Status: chain.Allow,
+				Actions: chain.Actions{
+					Names: []string{
+						nativeschema.MethodGetContainer,
+					},
+				},
+				Resources: chain.Resources{
+					Names: []string{
+						fmt.Sprintf(nativeschema.ResourceFormatRootContainer, contID.EncodeToString()),
+					},
+				},
+				Condition: []chain.Condition{
+					{
+						Kind:  chain.KindRequest,
+						Key:   commonschema.PropertyKeyFrostFSIDGroupID,
+						Value: "1",
+						Op:    chain.CondStringEquals,
+					},
+				},
+			},
+		},
+	})
+	require.NoError(t, err)
+
+	resp, err := apeSrv.Get(context.Background(), req)
+	require.NotNil(t, resp)
+	require.NoError(t, err)
+}
+
 func testDenyGetContainerNoRuleFound(t *testing.T) {
 	t.Parallel()
 	srv := &srvStub{