I solved it. As lassoan already mentioned in this answer, you need MRML and module logic classes to interact with any other module (find more in these slides).
After looking at ImageStack code, I ended up with the following script:
# filename is the first image path of the stack, eg "/opt/data/image-0001.tif"
filename = "/opt/data/image-0001.tif"
# Set "ImageStacks" as currently active module.
slicer.util.selectModule("ImageStacks")
# Python scripted modules
moduleWidget = slicer.modules.imagestacks.widgetRepresentation().self()
# load image stack
# User selects a file like "/opt/data/image-0001.tif"
moduleWidget.archetypeText.text = filename
# The populateFromArchetype method will populate the file list with all files
# that match the numbering pattern in that directory
moduleWidget.populateFromArchetype()
# to check correct file loading
# print(moduleWidget.logic.filePaths)
# Instantiate and add a VolumeNode to the scene.
masterVolumeNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLScalarVolumeNode", "microCT_scan")
# Load the files in paths to outputNode.
moduleWidget.logic.loadVolume(outputNode = masterVolumeNode)