The API doesn't currently make this very easy, although it probably should... Although depending on what you ImageJ macro does, it might be preferable to skip this step and do all the work in QuPath + Groovy.
Anyway, maybe the following helps:
import qupath.imagej.plugins.ImageJMacroRunner
import qupath.lib.plugins.parameters.ParameterList
// Create a macro runner so we can check what the parameter list contains
def params = new ImageJMacroRunner(getQuPath()).getParameterList()
print ParameterList.getParameterListJSON(params, ' ')
// Change the value of a parameter, using the JSON to identify the key
params.getParameters().get('downsampleFactor').setValue(4.0 as double)
print ParameterList.getParameterListJSON(params, ' ')
// Get the macro text and other required variables
def macro = 'print("Overlay size: " + Overlay.size)'
def imageData = getCurrentImageData()
def annotations = getAnnotationObjects()
// Loop through the annotations and run the macro
for (annotation in annotations) {
ImageJMacroRunner.runMacro(params, imageData, null, annotation, macro)
}
print 'Done!'
I don't know how well or robustly it works, and the manipulation of the ParameterList is a bit awkward. But hopefully it gives a starting point.