How to make custom "create options" panel in bottom left
While mountainstorm's answer is functional, you actually don't need so much to get the redo UI to show up. (At least not in Blender 2.9 - I haven't tried this in earlier versions but the release notes don't indicate any changes around this.)
The requirements are simply:

Your operator's bl_options must include 'REGISTER' and 'UNDO'. (Register indicates to show the redo panel; undo is needed because redo is just an undo of the last operation, followed by re-execution of the operator.)
Your operator needs to have at least one property defined.

That means this is a minimal operator that will display the redo panel when executed:
import bpy

class EXAMPLE_OT_DefaultIntOperator(bpy.types.Operator):
    bl_idname = &quot;example.redo_operator&quot;
    bl_label = &quot;Example Redo Operator&quot;
    bl_options = {'REGISTER', 'UNDO'}

    my_prop: bpy.props.IntProperty(name = &quot;My Property&quot;)

    def execute(self, context):
        self.report({'INFO'}, f&quot;my_prop: {self.my_prop}&quot;)
        return {'FINISHED'}


This uses the default operator drawing logic, with one property per row. If you want, you can also control exactly what's drawn by defining the draw(self, context) method in your operator. The following code defines two operators, one matching the earlier example, and another using a custom draw method to enable/disable a property input based on another property's value.
import bpy

class EXAMPLE_OT_DefaultIntOperator(bpy.types.Operator):
    bl_idname = &quot;example.redo_operator&quot;
    bl_label = &quot;Example Redo Operator&quot;
    bl_options = {'REGISTER', 'UNDO'}

    my_prop: bpy.props.IntProperty(name = &quot;My Property&quot;)

    def execute(self, context):
        self.report({'INFO'}, f&quot;my_prop: {self.my_prop}&quot;)
        return {'FINISHED'}

class EXAMPLE_OT_CustomDrawOperator(bpy.types.Operator):
    bl_idname = &quot;example.redo_operator_with_draw&quot;
    bl_label = &quot;Example Redo With Custom Draw&quot;
    bl_options = {'REGISTER', 'UNDO'}

    dependent_prop: bpy.props.IntProperty(name = &quot;My Property&quot;)
    flag_prop: bpy.props.BoolProperty(name = &quot;Use Int&quot;)

    def execute(self, context):
        msg = f&quot;dependent_prop: {self.dependent_prop}&quot; if self.flag_prop else &quot;Nothing to report&quot;
        self.report({'INFO'}, msg)

        return {'FINISHED'}
    
    def draw(self, context):
        self.layout.use_property_split = True

        row = self.layout.row()
        row.prop(self, &quot;flag_prop&quot;)
        
        sub = row.row()
        sub.enabled = self.flag_prop
        sub.prop(self, &quot;dependent_prop&quot;, text=&quot;&quot;)

bpy.utils.register_class(EXAMPLE_OT_DefaultIntOperator)
bpy.utils.register_class(EXAMPLE_OT_CustomDrawOperator)

# If you're using Blender 2.9, operators only show in search if they're registered in a menu, 
# so you need everything below this point. You can find your ops in the Object menu within the 3D viewport.
# See https://wiki.blender.org/wiki/Reference/Release_Notes/2.90/Python_API#Compatibility
def basic_op_menu_func(self, context):
    self.layout.operator(&quot;example.redo_operator&quot;)

def custom_op_menu_func(self, context):
    self.layout.operator(&quot;example.redo_operator_with_draw&quot;)

bpy.types.VIEW3D_MT_object.append(basic_op_menu_func)
bpy.types.VIEW3D_MT_object.append(custom_op_menu_func)


As mountainstorm noted, you may need to override the execution context if you're calling the operator from a script and still want the redo panel to appear. If you're just creating a UI button via UILayout.operator, or executing your operator from a menu, then this should be unnecessary.