[{"data":1,"prerenderedAt":3944},["ShallowReactive",2],{"navigation":3,"\u002Fautomate\u002Fguides\u002Fslack-notifications":814,"\u002Fautomate\u002Fguides\u002Fslack-notifications-surround":3939},[4,84,167,419,483,589,626,705],{"title":5,"path":6,"stem":7,"children":8,"page":42},"Get Started","\u002Fget-started\u002F","01.get-started",[9,13,17,43,47],{"title":10,"path":11,"stem":12},"Welcome to UniteLabs","\u002Fget-started\u002Fwelcome\u002F","01.get-started\u002F00.welcome",{"title":14,"path":15,"stem":16},"How it works","\u002Fget-started\u002Fhow-it-works\u002F","01.get-started\u002F01.how-it-works",{"title":18,"path":19,"stem":20,"children":21,"page":42},"Use Cases","\u002Fget-started\u002Fuse-cases\u002F","01.get-started\u002F02.use-cases",[22,26,30,34,38],{"title":23,"path":24,"stem":25},"Instrument Control","\u002Fget-started\u002Fuse-cases\u002Flow-level-instrument-control\u002F","01.get-started\u002F02.use-cases\u002F10.low-level-instrument-control",{"title":27,"path":28,"stem":29},"Multi-device Control","\u002Fget-started\u002Fuse-cases\u002Fmulti-device-control\u002F","01.get-started\u002F02.use-cases\u002F20.multi-device-control",{"title":31,"path":32,"stem":33},"Workflow Orchestration","\u002Fget-started\u002Fuse-cases\u002Fworkflow-orchestration\u002F","01.get-started\u002F02.use-cases\u002F30.workflow-orchestration",{"title":35,"path":36,"stem":37},"Custom Apps and LIMS Integration","\u002Fget-started\u002Fuse-cases\u002Fcustom-apps-lims\u002F","01.get-started\u002F02.use-cases\u002F40.custom-apps-lims",{"title":39,"path":40,"stem":41},"Scale and Fleet Management","\u002Fget-started\u002Fuse-cases\u002Fscale-and-fleet\u002F","01.get-started\u002F02.use-cases\u002F50.scale-and-fleet",false,{"title":44,"path":45,"stem":46},"Lab as Code","\u002Fget-started\u002Fwhy-code\u002F","01.get-started\u002F03.why-code",{"title":48,"path":49,"stem":50,"children":51,"page":42},"Setup","\u002Fget-started\u002Fsetup\u002F","01.get-started\u002F20.setup",[52,56,60,64,68,72,76,80],{"title":53,"path":54,"stem":55},"Get access","\u002Fget-started\u002Fsetup\u002Fget-access\u002F","01.get-started\u002F20.setup\u002F00.get-access",{"title":57,"path":58,"stem":59},"GroundControl","\u002Fget-started\u002Fsetup\u002Fgroundcontrol\u002F","01.get-started\u002F20.setup\u002F10.groundcontrol",{"title":61,"path":62,"stem":63},"Set up your development machine","\u002Fget-started\u002Fsetup\u002Fdeveloper-machine\u002F","01.get-started\u002F20.setup\u002F20.developer-machine",{"title":65,"path":66,"stem":67},"SDK installation","\u002Fget-started\u002Fsetup\u002Fsdk-installation\u002F","01.get-started\u002F20.setup\u002F30.sdk-installation",{"title":69,"path":70,"stem":71},"Connect to Platform","\u002Fget-started\u002Fsetup\u002Fconnect-to-platform\u002F","01.get-started\u002F20.setup\u002F40.connect-to-platform",{"title":73,"path":74,"stem":75},"Multi-device Networking","\u002Fget-started\u002Fsetup\u002Fmultiple-devices\u002F","01.get-started\u002F20.setup\u002F45.multiple-devices",{"title":77,"path":78,"stem":79},"UniteLabs Desktop","\u002Fget-started\u002Fsetup\u002Funitelabs-desktop\u002F","01.get-started\u002F20.setup\u002F70.unitelabs-desktop",{"title":81,"path":82,"stem":83},"Network requirements","\u002Fget-started\u002Fsetup\u002Fnetwork-requirements\u002F","01.get-started\u002F20.setup\u002F90.network-requirements",{"title":85,"path":86,"stem":87,"children":88,"page":42},"Integrate","\u002Fintegrate\u002F","02.integrate",[89,93,97,122,151],{"title":90,"path":91,"stem":92},"What is a connector?","\u002Fintegrate\u002Fwhat-is-a-connector\u002F","02.integrate\u002F00.what-is-a-connector",{"title":94,"path":95,"stem":96},"Calling a Connector","\u002Fintegrate\u002Fcontrol-with-code\u002F","02.integrate\u002F05.control-with-code",{"title":98,"path":99,"stem":100,"children":101,"page":42},"Concepts","\u002Fintegrate\u002Fconcepts\u002F","02.integrate\u002F10.concepts",[102,106,110,114,118],{"title":103,"path":104,"stem":105},"Connector","\u002Fintegrate\u002Fconcepts\u002Fconnector\u002F","02.integrate\u002F10.concepts\u002F10.connector",{"title":107,"path":108,"stem":109},"Module","\u002Fintegrate\u002Fconcepts\u002Fmodule\u002F","02.integrate\u002F10.concepts\u002F20.module",{"title":111,"path":112,"stem":113},"Action","\u002Fintegrate\u002Fconcepts\u002Faction\u002F","02.integrate\u002F10.concepts\u002F30.action",{"title":115,"path":116,"stem":117},"Subscription","\u002Fintegrate\u002Fconcepts\u002Fsubscription\u002F","02.integrate\u002F10.concepts\u002F40.subscription",{"title":119,"path":120,"stem":121},"Device","\u002Fintegrate\u002Fconcepts\u002Fdevice\u002F","02.integrate\u002F10.concepts\u002F50.device",{"title":123,"path":124,"stem":125,"children":126,"page":42},"Connect a device","\u002Fintegrate\u002Fconnect-a-device\u002F","02.integrate\u002F20.connect-a-device",[127,131,135,139,143,147],{"title":128,"path":129,"stem":130},"Connector Configuration","\u002Fintegrate\u002Fconnect-a-device\u002Fconnector-configuration\u002F","02.integrate\u002F20.connect-a-device\u002F10.connector-configuration",{"title":132,"path":133,"stem":134},"With an executable","\u002Fintegrate\u002Fconnect-a-device\u002Fwith-an-executable\u002F","02.integrate\u002F20.connect-a-device\u002F20.with-an-executable",{"title":136,"path":137,"stem":138},"From source","\u002Fintegrate\u002Fconnect-a-device\u002Ffrom-source\u002F","02.integrate\u002F20.connect-a-device\u002F30.from-source",{"title":140,"path":141,"stem":142},"Build your own","\u002Fintegrate\u002Fconnect-a-device\u002Fbuild-yourself\u002F","02.integrate\u002F20.connect-a-device\u002F40.build-yourself",{"title":144,"path":145,"stem":146},"Headless install","\u002Fintegrate\u002Fconnect-a-device\u002Fheadless-install\u002F","02.integrate\u002F20.connect-a-device\u002F50.headless-install",{"title":148,"path":149,"stem":150},"Raspberry Pi","\u002Fintegrate\u002Fconnect-a-device\u002Fraspberry-pi\u002F","02.integrate\u002F20.connect-a-device\u002F55.raspberry-pi",{"title":152,"path":153,"stem":154,"children":155,"page":42},"Use a connector","\u002Fintegrate\u002Fuse-a-connector\u002F","02.integrate\u002F30.use-a-connector",[156,159,163],{"title":57,"path":157,"stem":158},"\u002Fintegrate\u002Fuse-a-connector\u002Fgroundcontrol\u002F","02.integrate\u002F30.use-a-connector\u002F10.groundcontrol",{"title":160,"path":161,"stem":162},"Platform","\u002Fintegrate\u002Fuse-a-connector\u002Fplatform\u002F","02.integrate\u002F30.use-a-connector\u002F20.platform",{"title":164,"path":165,"stem":166},"UniteLabs SDK & REST API","\u002Fintegrate\u002Fuse-a-connector\u002Fpython\u002F","02.integrate\u002F30.use-a-connector\u002F30.python",{"title":168,"path":169,"stem":170,"children":171,"page":42},"Operate","\u002Foperate\u002F","03.operate",[172,175,179,219,292],{"title":168,"path":173,"stem":174},"\u002Foperate\u002Foverview\u002F","03.operate\u002F00.overview",{"title":176,"path":177,"stem":178},"Your First Protocol","\u002Foperate\u002Fyour-first-protocol\u002F","03.operate\u002F05.your-first-protocol",{"title":98,"path":180,"stem":181,"children":182,"page":42},"\u002Foperate\u002Fconcepts\u002F","03.operate\u002F10.concepts",[183,187,191,195,199,203,207,211,215],{"title":184,"path":185,"stem":186},"Liquid Handling","\u002Foperate\u002Fconcepts\u002Fliquid-handling\u002F","03.operate\u002F10.concepts\u002F10.liquid-handling",{"title":188,"path":189,"stem":190},"Modules","\u002Foperate\u002Fconcepts\u002Fmodules\u002F","03.operate\u002F10.concepts\u002F20.modules",{"title":192,"path":193,"stem":194},"Deck","\u002Foperate\u002Fconcepts\u002Fdeck\u002F","03.operate\u002F10.concepts\u002F30.deck",{"title":196,"path":197,"stem":198},"Labware","\u002Foperate\u002Fconcepts\u002Flabware\u002F","03.operate\u002F10.concepts\u002F40.labware",{"title":200,"path":201,"stem":202},"Liquids","\u002Foperate\u002Fconcepts\u002Fliquids\u002F","03.operate\u002F10.concepts\u002F50.liquids",{"title":204,"path":205,"stem":206},"Liquid Classes","\u002Foperate\u002Fconcepts\u002Fliquid-classes\u002F","03.operate\u002F10.concepts\u002F60.liquid-classes",{"title":208,"path":209,"stem":210},"Tips and Tip Tracking","\u002Foperate\u002Fconcepts\u002Ftips\u002F","03.operate\u002F10.concepts\u002F70.tips",{"title":212,"path":213,"stem":214},"Simulation","\u002Foperate\u002Fconcepts\u002Fsimulation\u002F","03.operate\u002F10.concepts\u002F80.simulation",{"title":216,"path":217,"stem":218},"Error Handling","\u002Foperate\u002Fconcepts\u002Ferror-handling\u002F","03.operate\u002F10.concepts\u002F90.error-handling",{"title":220,"path":221,"stem":222,"children":223,"page":42},"Guides","\u002Foperate\u002Fguides\u002F","03.operate\u002F20.guides",[224,253,265,289],{"title":196,"path":225,"stem":226,"children":227},"\u002Foperate\u002Fguides\u002Flabware\u002F","03.operate\u002F20.guides\u002F10.labware\u002Findex",[228,229,233,237,241,245,249],{"title":196,"path":225,"stem":226},{"title":230,"path":231,"stem":232},"Standard Labware","\u002Foperate\u002Fguides\u002Flabware\u002Fstandard-labware\u002F","03.operate\u002F20.guides\u002F10.labware\u002F05.standard-labware",{"title":234,"path":235,"stem":236},"Plates","\u002Foperate\u002Fguides\u002Flabware\u002Fplates\u002F","03.operate\u002F20.guides\u002F10.labware\u002F10.plates",{"title":238,"path":239,"stem":240},"Tips and Tip Racks","\u002Foperate\u002Fguides\u002Flabware\u002Ftips-and-racks\u002F","03.operate\u002F20.guides\u002F10.labware\u002F20.tips-and-racks",{"title":242,"path":243,"stem":244},"Tubes and Tube Racks","\u002Foperate\u002Fguides\u002Flabware\u002Ftubes-and-tube-racks\u002F","03.operate\u002F20.guides\u002F10.labware\u002F30.tubes-and-tube-racks",{"title":246,"path":247,"stem":248},"Troughs","\u002Foperate\u002Fguides\u002Flabware\u002Ftroughs\u002F","03.operate\u002F20.guides\u002F10.labware\u002F35.troughs",{"title":250,"path":251,"stem":252},"Carriers and Adapters","\u002Foperate\u002Fguides\u002Flabware\u002Fcarriers-and-adapters\u002F","03.operate\u002F20.guides\u002F10.labware\u002F40.carriers-and-adapters",{"title":192,"path":254,"stem":255,"children":256,"page":42},"\u002Foperate\u002Fguides\u002Fdeck\u002F","03.operate\u002F20.guides\u002F20.deck",[257,261],{"title":258,"path":259,"stem":260},"Building a Deck","\u002Foperate\u002Fguides\u002Fdeck\u002Fbuilding-a-deck\u002F","03.operate\u002F20.guides\u002F20.deck\u002F10.building-a-deck",{"title":262,"path":263,"stem":264},"Save\u002FLoad a Deck","\u002Foperate\u002Fguides\u002Fdeck\u002Fsave-load-deck\u002F","03.operate\u002F20.guides\u002F20.deck\u002F20.save-load-deck",{"title":266,"path":267,"stem":268,"children":269,"page":42},"Pipetting","\u002Foperate\u002Fguides\u002Fpipetting\u002F","03.operate\u002F20.guides\u002F30.pipetting",[270,274,278,282,285],{"title":271,"path":272,"stem":273},"Basic Pipetting","\u002Foperate\u002Fguides\u002Fpipetting\u002Fbasic-pipetting\u002F","03.operate\u002F20.guides\u002F30.pipetting\u002F10.basic-pipetting",{"title":275,"path":276,"stem":277},"Advanced Pipetting","\u002Foperate\u002Fguides\u002Fpipetting\u002Fadvanced-pipetting\u002F","03.operate\u002F20.guides\u002F30.pipetting\u002F20.advanced-pipetting",{"title":279,"path":280,"stem":281},"Tip Handling","\u002Foperate\u002Fguides\u002Fpipetting\u002Ftip-handling\u002F","03.operate\u002F20.guides\u002F30.pipetting\u002F30.tip-handling",{"title":204,"path":283,"stem":284},"\u002Foperate\u002Fguides\u002Fpipetting\u002Fliquid-classes\u002F","03.operate\u002F20.guides\u002F30.pipetting\u002F40.liquid-classes",{"title":286,"path":287,"stem":288},"Labware Transport","\u002Foperate\u002Fguides\u002Fpipetting\u002Flabware-transport\u002F","03.operate\u002F20.guides\u002F30.pipetting\u002F50.labware-transport",{"title":212,"path":290,"stem":291},"\u002Foperate\u002Fguides\u002Fsimulation\u002F","03.operate\u002F20.guides\u002F40.simulation",{"title":293,"path":294,"stem":295,"children":296,"page":42},"Liquid Handler Guides","\u002Foperate\u002Fdevices\u002F","03.operate\u002F30.devices",[297,338,363,380,397],{"title":298,"path":299,"stem":300,"children":301,"page":42},"Hamilton STAR","\u002Foperate\u002Fdevices\u002Fhamilton-star\u002F","03.operate\u002F30.devices\u002F10.hamilton-star",[302,306,310,314,318,322,326,330,334],{"title":303,"path":304,"stem":305},"Positioning & Movement","\u002Foperate\u002Fdevices\u002Fhamilton-star\u002Fpositioning\u002F","03.operate\u002F30.devices\u002F10.hamilton-star\u002F10.positioning",{"title":307,"path":308,"stem":309},"Gripper Module","\u002Foperate\u002Fdevices\u002Fhamilton-star\u002Fcore-gripper\u002F","03.operate\u002F30.devices\u002F10.hamilton-star\u002F20.core-gripper",{"title":311,"path":312,"stem":313},"iSWAP Module","\u002Foperate\u002Fdevices\u002Fhamilton-star\u002Fiswap\u002F","03.operate\u002F30.devices\u002F10.hamilton-star\u002F30.iswap",{"title":315,"path":316,"stem":317},"Using TADM","\u002Foperate\u002Fdevices\u002Fhamilton-star\u002Ftadm\u002F","03.operate\u002F30.devices\u002F10.hamilton-star\u002F40.tadm",{"title":319,"path":320,"stem":321},"Autoload Module","\u002Foperate\u002Fdevices\u002Fhamilton-star\u002Fautoload\u002F","03.operate\u002F30.devices\u002F10.hamilton-star\u002F50.autoload",{"title":323,"path":324,"stem":325},"Waste Block","\u002Foperate\u002Fdevices\u002Fhamilton-star\u002Fwaste-block\u002F","03.operate\u002F30.devices\u002F10.hamilton-star\u002F60.waste-block",{"title":327,"path":328,"stem":329},"Complex Mixing","\u002Foperate\u002Fdevices\u002Fhamilton-star\u002Fcomplex-mixing\u002F","03.operate\u002F30.devices\u002F10.hamilton-star\u002F66.complex-mixing",{"title":331,"path":332,"stem":333},"Teaching Gripper Paddle Positions","\u002Foperate\u002Fdevices\u002Fhamilton-star\u002Fgripper-positions\u002F","03.operate\u002F30.devices\u002F10.hamilton-star\u002F70.gripper-positions",{"title":335,"path":336,"stem":337},"Training a Custom Deck Position With the iSWAP","\u002Foperate\u002Fdevices\u002Fhamilton-star\u002Fcustom-deck-position\u002F","03.operate\u002F30.devices\u002F10.hamilton-star\u002F75.custom-deck-position",{"title":339,"path":340,"stem":341,"children":342,"page":42},"Hamilton Vantage","\u002Foperate\u002Fdevices\u002Fhamilton-vantage\u002F","03.operate\u002F30.devices\u002F20.hamilton-vantage",[343,347,351,355,359],{"title":344,"path":345,"stem":346},"Waste Configuration","\u002Foperate\u002Fdevices\u002Fhamilton-vantage\u002Fwaste-configuration\u002F","03.operate\u002F30.devices\u002F20.hamilton-vantage\u002F05.waste-configuration",{"title":348,"path":349,"stem":350},"Covers and Lights","\u002Foperate\u002Fdevices\u002Fhamilton-vantage\u002Fcovers-and-lights\u002F","03.operate\u002F30.devices\u002F20.hamilton-vantage\u002F10.covers-and-lights",{"title":352,"path":353,"stem":354},"IDL Module","\u002Foperate\u002Fdevices\u002Fhamilton-vantage\u002Fidl\u002F","03.operate\u002F30.devices\u002F20.hamilton-vantage\u002F20.idl",{"title":356,"path":357,"stem":358},"IPG Module","\u002Foperate\u002Fdevices\u002Fhamilton-vantage\u002Fipg\u002F","03.operate\u002F30.devices\u002F20.hamilton-vantage\u002F30.ipg",{"title":360,"path":361,"stem":362},"XL Pipettes","\u002Foperate\u002Fdevices\u002Fhamilton-vantage\u002Fxl-channels\u002F","03.operate\u002F30.devices\u002F20.hamilton-vantage\u002F40.xl-channels",{"title":364,"path":365,"stem":366,"children":367,"page":42},"Agilent Bravo","\u002Foperate\u002Fdevices\u002Fagilent-bravo\u002F","03.operate\u002F30.devices\u002F30.agilent-bravo",[368,371,374,377],{"title":279,"path":369,"stem":370},"\u002Foperate\u002Fdevices\u002Fagilent-bravo\u002Ftip-handling\u002F","03.operate\u002F30.devices\u002F30.agilent-bravo\u002F10.tip-handling",{"title":271,"path":372,"stem":373},"\u002Foperate\u002Fdevices\u002Fagilent-bravo\u002Fbasic-pipetting\u002F","03.operate\u002F30.devices\u002F30.agilent-bravo\u002F20.basic-pipetting",{"title":204,"path":375,"stem":376},"\u002Foperate\u002Fdevices\u002Fagilent-bravo\u002Fliquid-classes\u002F","03.operate\u002F30.devices\u002F30.agilent-bravo\u002F25.liquid-classes",{"title":307,"path":378,"stem":379},"\u002Foperate\u002Fdevices\u002Fagilent-bravo\u002Fusing-the-gripper\u002F","03.operate\u002F30.devices\u002F30.agilent-bravo\u002F30.using-the-gripper",{"title":381,"path":382,"stem":383,"children":384,"page":42},"Tecan FluentControl","\u002Foperate\u002Fdevices\u002Ftecan-fluentcontrol\u002F","03.operate\u002F30.devices\u002F40.tecan-fluentcontrol",[385,389,393],{"title":386,"path":387,"stem":388},"Connect & Run a Method","\u002Foperate\u002Fdevices\u002Ftecan-fluentcontrol\u002Fconnect-and-run\u002F","03.operate\u002F30.devices\u002F40.tecan-fluentcontrol\u002F10.connect-and-run",{"title":390,"path":391,"stem":392},"Sending XML Commands","\u002Foperate\u002Fdevices\u002Ftecan-fluentcontrol\u002Fsending-xml-commands\u002F","03.operate\u002F30.devices\u002F40.tecan-fluentcontrol\u002F20.sending-xml-commands",{"title":394,"path":395,"stem":396},"Full Workflow Example","\u002Foperate\u002Fdevices\u002Ftecan-fluentcontrol\u002Ffull-workflow-example\u002F","03.operate\u002F30.devices\u002F40.tecan-fluentcontrol\u002F30.full-workflow-example",{"title":398,"path":399,"stem":400,"children":401,"page":42},"Tecan Freedom EVO","\u002Foperate\u002Fdevices\u002Ftecan-freedom-evo\u002F","03.operate\u002F30.devices\u002F50.tecan-freedom-evo",[402,406,409,412,416],{"title":403,"path":404,"stem":405},"Deck & Carriers","\u002Foperate\u002Fdevices\u002Ftecan-freedom-evo\u002Fdeck-and-carriers\u002F","03.operate\u002F30.devices\u002F50.tecan-freedom-evo\u002F10.deck-and-carriers",{"title":279,"path":407,"stem":408},"\u002Foperate\u002Fdevices\u002Ftecan-freedom-evo\u002Ftip-handling\u002F","03.operate\u002F30.devices\u002F50.tecan-freedom-evo\u002F20.tip-handling",{"title":271,"path":410,"stem":411},"\u002Foperate\u002Fdevices\u002Ftecan-freedom-evo\u002Fbasic-pipetting\u002F","03.operate\u002F30.devices\u002F50.tecan-freedom-evo\u002F30.basic-pipetting",{"title":413,"path":414,"stem":415},"Using the Gripper (RoMa)","\u002Foperate\u002Fdevices\u002Ftecan-freedom-evo\u002Fusing-the-gripper\u002F","03.operate\u002F30.devices\u002F50.tecan-freedom-evo\u002F40.using-the-gripper",{"title":204,"path":417,"stem":418},"\u002Foperate\u002Fdevices\u002Ftecan-freedom-evo\u002Fliquid-classes\u002F","03.operate\u002F30.devices\u002F50.tecan-freedom-evo\u002F50.liquid-classes",{"title":420,"path":421,"stem":422,"children":423,"page":42},"Device Guides","\u002Fdevice-guides\u002F","06.device-guides",[424,427,440,457,470],{"title":420,"path":425,"stem":426},"\u002Fdevice-guides\u002Foverview\u002F","06.device-guides\u002F00.overview",{"title":428,"path":429,"stem":430,"children":431,"page":42},"Incubation","\u002Fdevice-guides\u002Fincubation\u002F","06.device-guides\u002F20.incubation",[432,436],{"title":433,"path":434,"stem":435},"Inheco ODTC","\u002Fdevice-guides\u002Fincubation\u002Finheco-odtc\u002F","06.device-guides\u002F20.incubation\u002F10.inheco-odtc",{"title":437,"path":438,"stem":439},"Inheco SCILA","\u002Fdevice-guides\u002Fincubation\u002Finheco-scila\u002F","06.device-guides\u002F20.incubation\u002F20.inheco-scila",{"title":441,"path":442,"stem":443,"children":444,"page":42},"Heater-Shakers","\u002Fdevice-guides\u002Fheater-shakers\u002F","06.device-guides\u002F30.heater-shakers",[445,449,453],{"title":446,"path":447,"stem":448},"Inheco Thermoshake","\u002Fdevice-guides\u002Fheater-shakers\u002Finheco-thermoshake\u002F","06.device-guides\u002F30.heater-shakers\u002F10.inheco-thermoshake",{"title":450,"path":451,"stem":452},"QInstruments Bioshake","\u002Fdevice-guides\u002Fheater-shakers\u002Fqinstruments-bioshake\u002F","06.device-guides\u002F30.heater-shakers\u002F20.qinstruments-bioshake",{"title":454,"path":455,"stem":456},"QInstruments ColdPlate","\u002Fdevice-guides\u002Fheater-shakers\u002Fqinstruments-coldplate\u002F","06.device-guides\u002F30.heater-shakers\u002F30.qinstruments-coldplate",{"title":458,"path":459,"stem":460,"children":461,"page":42},"Readers","\u002Fdevice-guides\u002Freaders\u002F","06.device-guides\u002F40.readers",[462,466],{"title":463,"path":464,"stem":465},"Agilent Biotek Gen5","\u002Fdevice-guides\u002Freaders\u002Fbiotek-gen5\u002F","06.device-guides\u002F40.readers\u002F10.biotek-gen5",{"title":467,"path":468,"stem":469},"Biotek Synergy H1","\u002Fdevice-guides\u002Freaders\u002Fbiotek-synergy-h1\u002F","06.device-guides\u002F40.readers\u002F20.biotek-synergy-H1",{"title":471,"path":472,"stem":473,"children":474,"page":42},"Tilt & Turn","\u002Fdevice-guides\u002Ftilt-and-turn\u002F","06.device-guides\u002F50.tilt-and-turn",[475,479],{"title":476,"path":477,"stem":478},"TurnStation","\u002Fdevice-guides\u002Ftilt-and-turn\u002Fqinstruments-turnstation\u002F","06.device-guides\u002F50.tilt-and-turn\u002F10.qinstruments-turnstation",{"title":480,"path":481,"stem":482},"TiltStation","\u002Fdevice-guides\u002Ftilt-and-turn\u002Fqinstruments-tiltstation\u002F","06.device-guides\u002F50.tilt-and-turn\u002F20.qinstruments-tiltstation",{"title":484,"path":485,"stem":486,"children":487,"page":42},"Automate","\u002Fautomate\u002F","07.automate",[488,492,496,500,549],{"title":489,"path":490,"stem":491},"What is a workflow?","\u002Fautomate\u002Fwhat-is-a-workflow\u002F","07.automate\u002F00.what-is-a-workflow",{"title":493,"path":494,"stem":495},"Workflow template","\u002Fautomate\u002Fworkflow-template\u002F","07.automate\u002F05.workflow-template",{"title":497,"path":498,"stem":499},"Your First Workflow","\u002Fautomate\u002Fyour-first-workflow\u002F","07.automate\u002F08.your-first-workflow",{"title":98,"pages":501,"path":511,"stem":512,"children":513,"page":42},[502,503,504,505,506,507,508,509,510],"workflow","phase","step","runs","input","artifacts","logs","human-in-the-loop","error-handling","\u002Fautomate\u002Fconcepts\u002F","07.automate\u002F10.concepts",[514,518,522,526,530,534,538,542,546],{"title":515,"path":516,"stem":517},"Workflow","\u002Fautomate\u002Fconcepts\u002Fworkflow\u002F","07.automate\u002F10.concepts\u002F10.workflow",{"title":519,"path":520,"stem":521},"Phase","\u002Fautomate\u002Fconcepts\u002Fphase\u002F","07.automate\u002F10.concepts\u002F20.phase",{"title":523,"path":524,"stem":525},"Step","\u002Fautomate\u002Fconcepts\u002Fstep\u002F","07.automate\u002F10.concepts\u002F30.step",{"title":527,"path":528,"stem":529},"Runs","\u002Fautomate\u002Fconcepts\u002Fruns\u002F","07.automate\u002F10.concepts\u002F40.runs",{"title":531,"path":532,"stem":533},"Input","\u002Fautomate\u002Fconcepts\u002Finput\u002F","07.automate\u002F10.concepts\u002F50.input",{"title":535,"path":536,"stem":537},"Artifacts","\u002Fautomate\u002Fconcepts\u002Fartifacts\u002F","07.automate\u002F10.concepts\u002F60.artifacts",{"title":539,"path":540,"stem":541},"Logs","\u002Fautomate\u002Fconcepts\u002Flogs\u002F","07.automate\u002F10.concepts\u002F70.logs",{"title":543,"path":544,"stem":545},"Human in the Loop","\u002Fautomate\u002Fconcepts\u002Fhuman-in-the-loop\u002F","07.automate\u002F10.concepts\u002F80.human-in-the-loop",{"title":216,"path":547,"stem":548},"\u002Fautomate\u002Fconcepts\u002Ferror-handling\u002F","07.automate\u002F10.concepts\u002F90.error-handling",{"title":220,"path":550,"stem":551,"children":552,"page":42},"\u002Fautomate\u002Fguides\u002F","07.automate\u002F25.guides",[553,557,561,565,569,573,577,581,585],{"title":554,"path":555,"stem":556},"Deploy a workflow","\u002Fautomate\u002Fguides\u002Fdeploy-a-workflow\u002F","07.automate\u002F25.guides\u002F30.deploy-a-workflow",{"title":558,"path":559,"stem":560},"Run a workflow","\u002Fautomate\u002Fguides\u002Frun-a-workflow\u002F","07.automate\u002F25.guides\u002F35.run-a-workflow",{"title":562,"path":563,"stem":564},"CI\u002FCD for workflows","\u002Fautomate\u002Fguides\u002Fcicd-for-workflows\u002F","07.automate\u002F25.guides\u002F40.cicd-for-workflows",{"title":566,"path":567,"stem":568},"HITL basics","\u002Fautomate\u002Fguides\u002Fbasic-hitl\u002F","07.automate\u002F25.guides\u002F50.basic-hitl",{"title":570,"path":571,"stem":572},"Slack notifications and operator confirmation","\u002Fautomate\u002Fguides\u002Fslack-notifications\u002F","07.automate\u002F25.guides\u002F52.slack-notifications",{"title":574,"path":575,"stem":576},"Typed operator inputs","\u002Fautomate\u002Fguides\u002Ftyped-operator-inputs\u002F","07.automate\u002F25.guides\u002F55.typed-operator-inputs",{"title":578,"path":579,"stem":580},"Error recovery","\u002Fautomate\u002Fguides\u002Fbasic-error-handling\u002F","07.automate\u002F25.guides\u002F60.basic-error-handling",{"title":582,"path":583,"stem":584},"Advanced error handling","\u002Fautomate\u002Fguides\u002Fadvanced-error-handling\u002F","07.automate\u002F25.guides\u002F70.advanced-error-handling",{"title":586,"path":587,"stem":588},"Workflows REST API","\u002Fautomate\u002Fguides\u002Fworkflows-api\u002F","07.automate\u002F25.guides\u002F80.workflows-api",{"title":590,"path":591,"stem":592,"children":593,"page":42},"Observe","\u002Fobserve\u002F","08.observe",[594,598,614],{"title":595,"path":596,"stem":597},"Overview","\u002Fobserve\u002Foverview\u002F","08.observe\u002F10.overview",{"title":98,"path":599,"stem":600,"children":601,"page":42},"\u002Fobserve\u002Fconcepts\u002F","08.observe\u002F20.concepts",[602,606,610],{"title":603,"path":604,"stem":605},"Object Storage","\u002Fobserve\u002Fconcepts\u002Fstorage\u002F","08.observe\u002F20.concepts\u002F10.storage",{"title":607,"path":608,"stem":609},"Secrets","\u002Fobserve\u002Fconcepts\u002Fsecrets\u002F","08.observe\u002F20.concepts\u002F20.secrets",{"title":611,"path":612,"stem":613},"Data sources","\u002Fobserve\u002Fconcepts\u002Fdata-sources\u002F","08.observe\u002F20.concepts\u002F30.data-sources",{"title":220,"path":615,"stem":616,"children":617,"page":42},"\u002Fobserve\u002Fguides\u002F","08.observe\u002F30.guides",[618,622],{"title":619,"path":620,"stem":621},"File System Connector","\u002Fobserve\u002Fguides\u002Ffilesystem-connector\u002F","08.observe\u002F30.guides\u002F10.filesystem-connector",{"title":623,"path":624,"stem":625},"Building an ETL","\u002Fobserve\u002Fguides\u002Fbuilding-an-etl\u002F","08.observe\u002F30.guides\u002F20.building-an-etl",{"title":627,"path":628,"stem":629,"children":630,"page":42},"Technical Reference","\u002Ftechnical-reference\u002F","10.technical-reference",[631,635,639,651,662,673,683,694],{"title":632,"path":633,"stem":634},"Introduction","\u002Ftechnical-reference\u002Fplatform-blueprint\u002F","10.technical-reference\u002F00.platform-blueprint",{"title":636,"path":637,"stem":638},"REST API","\u002Ftechnical-reference\u002Frest-api\u002F","10.technical-reference\u002F01.rest-api",{"title":57,"path":640,"stem":641,"children":642,"page":42},"\u002Ftechnical-reference\u002Fgroundcontrol\u002F","10.technical-reference\u002F10.groundcontrol",[643,647],{"title":644,"path":645,"stem":646},"Install & Setup","\u002Ftechnical-reference\u002Fgroundcontrol\u002Finstall\u002F","10.technical-reference\u002F10.groundcontrol\u002F10.install",{"title":648,"path":649,"stem":650},"Changelog","\u002Ftechnical-reference\u002Fgroundcontrol\u002Fchangelog\u002F","10.technical-reference\u002F10.groundcontrol\u002F20.changelog",{"title":652,"path":653,"stem":654,"children":655,"page":42},"UniteLabs SDK","\u002Ftechnical-reference\u002Fpython-sdk\u002F","10.technical-reference\u002F20.python-sdk",[656,659],{"title":644,"path":657,"stem":658},"\u002Ftechnical-reference\u002Fpython-sdk\u002Finstall\u002F","10.technical-reference\u002F20.python-sdk\u002F10.install",{"title":648,"path":660,"stem":661},"\u002Ftechnical-reference\u002Fpython-sdk\u002Fchangelog\u002F","10.technical-reference\u002F20.python-sdk\u002F20.changelog",{"title":663,"path":664,"stem":665,"children":666,"page":42},"Liquid Handling SDK","\u002Ftechnical-reference\u002Fpython-liquid-handling-sdk\u002F","10.technical-reference\u002F30.python-liquid-handling-sdk",[667,670],{"title":644,"path":668,"stem":669},"\u002Ftechnical-reference\u002Fpython-liquid-handling-sdk\u002Finstall\u002F","10.technical-reference\u002F30.python-liquid-handling-sdk\u002F10.install",{"title":648,"path":671,"stem":672},"\u002Ftechnical-reference\u002Fpython-liquid-handling-sdk\u002Fchangelog\u002F","10.technical-reference\u002F30.python-liquid-handling-sdk\u002F20.changelog",{"title":196,"path":674,"stem":675,"children":676,"page":42},"\u002Ftechnical-reference\u002Flabware\u002F","10.technical-reference\u002F40.labware",[677,680],{"title":644,"path":678,"stem":679},"\u002Ftechnical-reference\u002Flabware\u002Finstall\u002F","10.technical-reference\u002F40.labware\u002F10.install",{"title":648,"path":681,"stem":682},"\u002Ftechnical-reference\u002Flabware\u002Fchangelog\u002F","10.technical-reference\u002F40.labware\u002F20.changelog",{"title":684,"path":685,"stem":686,"children":687,"page":42},"Python Omnibus","\u002Ftechnical-reference\u002Fpython-omnibus\u002F","10.technical-reference\u002F50.python-omnibus",[688,691],{"title":644,"path":689,"stem":690},"\u002Ftechnical-reference\u002Fpython-omnibus\u002Finstall\u002F","10.technical-reference\u002F50.python-omnibus\u002F10.install",{"title":648,"path":692,"stem":693},"\u002Ftechnical-reference\u002Fpython-omnibus\u002Fchangelog\u002F","10.technical-reference\u002F50.python-omnibus\u002F20.changelog",{"title":695,"path":696,"stem":697,"children":698,"page":42},"Python CDK","\u002Ftechnical-reference\u002Fpython-cdk\u002F","10.technical-reference\u002F60.python-cdk",[699,702],{"title":644,"path":700,"stem":701},"\u002Ftechnical-reference\u002Fpython-cdk\u002Finstall\u002F","10.technical-reference\u002F60.python-cdk\u002F10.install",{"title":648,"path":703,"stem":704},"\u002Ftechnical-reference\u002Fpython-cdk\u002Fchangelog\u002F","10.technical-reference\u002F60.python-cdk\u002F20.changelog",{"title":706,"path":707,"stem":708,"children":709},"Connector Development (CDK)","\u002Fconnector-development\u002F","20.connector-development",[710,712,732,765,773,808],{"title":706,"path":707,"stem":711},"20.connector-development\u002Findex",{"title":713,"path":714,"stem":715,"children":716,"page":42},"Getting Started","\u002Fconnector-development\u002Fgetting-started\u002F","20.connector-development\u002F10.getting-started",[717,720,724,728],{"title":595,"path":718,"stem":719},"\u002Fconnector-development\u002Fgetting-started\u002Foverview\u002F","20.connector-development\u002F10.getting-started\u002F10.overview",{"title":721,"path":722,"stem":723},"What's new","\u002Fconnector-development\u002Fgetting-started\u002Fwhats-new\u002F","20.connector-development\u002F10.getting-started\u002F20.whats-new",{"title":725,"path":726,"stem":727},"Installation","\u002Fconnector-development\u002Fgetting-started\u002Finstallation\u002F","20.connector-development\u002F10.getting-started\u002F30.installation",{"title":729,"path":730,"stem":731},"Contributing","\u002Fconnector-development\u002Fgetting-started\u002Fcontributing\u002F","20.connector-development\u002F10.getting-started\u002F40.contributing",{"title":733,"path":734,"stem":735,"children":736,"page":42},"Tutorial","\u002Fconnector-development\u002Ftutorial\u002F","20.connector-development\u002F20.tutorial",[737,741,745,749,753,757,761],{"title":738,"path":739,"stem":740},"Walkthrough","\u002Fconnector-development\u002Ftutorial\u002Fwalkthrough\u002F","20.connector-development\u002F20.tutorial\u002F00.walkthrough",{"title":742,"path":743,"stem":744},"SiLA Endpoints","\u002Fconnector-development\u002Ftutorial\u002Fsila-endpoints\u002F","20.connector-development\u002F20.tutorial\u002F10.sila-endpoints",{"title":746,"path":747,"stem":748},"Data Endpoints","\u002Fconnector-development\u002Ftutorial\u002Fdata-endpoints\u002F","20.connector-development\u002F20.tutorial\u002F20.data-endpoints",{"title":750,"path":751,"stem":752},"Controls","\u002Fconnector-development\u002Ftutorial\u002Fcontrols\u002F","20.connector-development\u002F20.tutorial\u002F30.controls",{"title":754,"path":755,"stem":756},"SiLA Data Types","\u002Fconnector-development\u002Ftutorial\u002Fdata-types\u002F","20.connector-development\u002F20.tutorial\u002F40.data-types",{"title":758,"path":759,"stem":760},"Feature","\u002Fconnector-development\u002Ftutorial\u002Fconnector-structure\u002F","20.connector-development\u002F20.tutorial\u002F70.connector-structure",{"title":762,"path":763,"stem":764},"Hardware Communication","\u002Fconnector-development\u002Ftutorial\u002Fhardware-communication\u002F","20.connector-development\u002F20.tutorial\u002F90.hardware-communication",{"title":98,"path":766,"stem":767,"children":768,"page":42},"\u002Fconnector-development\u002Fconcepts\u002F","20.connector-development\u002F30.concepts",[769],{"title":770,"path":771,"stem":772},"SiLA","\u002Fconnector-development\u002Fconcepts\u002Fsila\u002F","20.connector-development\u002F30.concepts\u002Fsila",{"title":220,"path":774,"stem":775,"children":776,"page":42},"\u002Fconnector-development\u002Fguides\u002F","20.connector-development\u002F40.guides",[777,781,785,789,793,797,800,804],{"title":778,"path":779,"stem":780},"Configuration","\u002Fconnector-development\u002Fguides\u002Fconfiguration\u002F","20.connector-development\u002F40.guides\u002F20.configuration",{"title":782,"path":783,"stem":784},"Logging","\u002Fconnector-development\u002Fguides\u002Flogging\u002F","20.connector-development\u002F40.guides\u002F21.logging",{"title":786,"path":787,"stem":788},"Deployment","\u002Fconnector-development\u002Fguides\u002Fdeployment\u002F","20.connector-development\u002F40.guides\u002F30.deployment",{"title":790,"path":791,"stem":792},"Security","\u002Fconnector-development\u002Fguides\u002Fsecurity\u002F","20.connector-development\u002F40.guides\u002F40.security",{"title":794,"path":795,"stem":796},"Subscriptions","\u002Fconnector-development\u002Fguides\u002Fsubscriptions\u002F","20.connector-development\u002F40.guides\u002F45.subscriptions",{"title":216,"path":798,"stem":799},"\u002Fconnector-development\u002Fguides\u002Ferror-handling\u002F","20.connector-development\u002F40.guides\u002F50.error-handling",{"title":801,"path":802,"stem":803},"Serial Troubleshooting","\u002Fconnector-development\u002Fguides\u002Fserial-troubleshooting\u002F","20.connector-development\u002F40.guides\u002F70.serial-troubleshooting",{"title":805,"path":806,"stem":807},"Testing","\u002Fconnector-development\u002Fguides\u002Ftesting\u002F","20.connector-development\u002F40.guides\u002F80.testing",{"title":809,"path":810,"stem":811,"children":812},"References","\u002Fconnector-development\u002Freference\u002F","20.connector-development\u002F50.reference\u002Findex",[813],{"title":809,"path":810,"stem":811},{"id":815,"title":570,"body":816,"description":3932,"extension":3933,"links":3934,"meta":3935,"navigation":1270,"path":3936,"seo":3937,"stem":572,"__hash__":3938},"docs\u002F07.automate\u002F25.guides\u002F52.slack-notifications.md",{"type":817,"value":818,"toc":3919},"minimark",[819,823,837,846,857,862,865,909,915,922,926,929,964,975,986,1015,1029,1032,1041,1045,1048,1097,1102,1110,1114,1120,1154,1162,1165,1178,1185,1212,1217,1221,1228,1236,1247,1254,1310,1317,1488,1507,1514,1520,1839,1845,1865,1878,1887,1890,1908,1917,1921,1924,1941,1957,1966,1980,2071,2086,2090,2108,2114,2128,2145,2150,2153,2156,2217,2481,2487,2501,2512,2516,2524,2562,2566,2746,2749,2758,2761,2925,3832,3841,3866,3872,3875,3900,3915],[820,821,822],"p",{},"The workflow posts a Slack message, then pauses until an operator confirms in UniteLabs.",[820,824,825,826,830,831,836],{},"You add one workflow package to a clone of the workflow template and deploy it. On this main path the operator confirms in ",[827,828,829],"strong",{},"UniteLabs",", and replies, reactions, and buttons in Slack do nothing. ",[832,833,835],"a",{"href":834},"#answer-from-slack-instead-of-the-platform","Answer from Slack instead of the platform"," shows a short variant where the operator answers with an emoji reaction in Slack.",[820,838,839,842,843,845],{},[827,840,841],{},"Why the workflow has to be deployed."," A script started locally uses a temporary engine that ends with the process, so the platform never sees a run to confirm. ",[832,844,566],{"href":567}," requires a workflow engine connected to the platform.",[820,847,848,851,852,856],{},[827,849,850],{},"Two ways to get the workflow onto the platform."," You can either create the workflow directly in the platform UI or develop it locally in your IDE and deploy it using your credentials. The platform UI requires no repository clone, package-registry access, local credentials, or deploy command, but it does not provide version control. The local approach keeps the workflow code in a cloned repository and deploys it using the template script. Steps 2, 3, and 6 apply to both. Steps 1, 4, and 5 are the template path; ",[832,853,855],{"href":854},"#alternative-write-the-workflow-in-the-platform","Alternative: write the workflow in the platform"," replaces them.",[858,859,861],"h2",{"id":860},"before-you-start","Before you start",[820,863,864],{},"You need:",[866,867,868,881,888,906],"ul",{},[869,870,871,872,880],"li",{},"Python 3.12 or newer and ",[832,873,879],{"href":874,"rel":875,"target":878},"https:\u002F\u002Fdocs.astral.sh\u002Fuv\u002Fgetting-started\u002Finstallation\u002F",[876,877],"nofollow","noopener","_blank","uv",".",[869,882,883,884,880],{},"Access to the UniteLabs package registry, set up as described in ",[832,885,887],{"href":886},"\u002Fget-started\u002Fsetup\u002Fdeveloper-machine\u002F#set-up-registry-access","Set up registry access",[869,889,890,891,895,896,895,899,895,902,905],{},"A UniteLabs account that can create secrets, deploy workflows, and confirm runs, plus API client credentials (",[892,893,894],"code",{},"BASE_URL",", ",[892,897,898],{},"AUTH_URL",[892,900,901],{},"CLIENT_ID",[892,903,904],{},"CLIENT_SECRET","). Registry credentials and platform credentials are separate.",[869,907,908],{},"A Slack workspace where you can install an app, and a channel for test messages. For a private channel you must already be a member. If your workspace requires administrator approval for apps, arrange that first.",[820,910,911,912,914],{},"Use ",[832,913,53],{"href":54}," if you are missing UniteLabs credentials.",[820,916,917,918,921],{},"Work through ",[832,919,920],{"href":498},"Your first workflow"," first. It leaves you with the clone and the environment this guide builds on.",[858,923,925],{"id":924},"step-1-get-the-workflow-template","Step 1: Get the workflow template",[820,927,928],{},"Clone the template:",[930,931,936],"pre",{"className":932,"code":933,"language":934,"meta":935,"style":935},"language-bash shiki shiki-themes github-light-high-contrast github-light-high-contrast github-dark-default","git clone https:\u002F\u002Fgitlab.com\u002Funitelabs\u002Fworkflows\u002Fworkflow-template.git\ncd workflow-template\n","bash","",[892,937,938,954],{"__ignoreMap":935},[939,940,943,947,951],"span",{"class":941,"line":942},"line",1,[939,944,946],{"class":945},"sNqAu","git",[939,948,950],{"class":949},"sCx38"," clone",[939,952,953],{"class":949}," https:\u002F\u002Fgitlab.com\u002Funitelabs\u002Fworkflows\u002Fworkflow-template.git\n",[939,955,957,961],{"class":941,"line":956},2,[939,958,960],{"class":959},"sOiAX","cd",[939,962,963],{"class":949}," workflow-template\n",[820,965,966,967,970,971,974],{},"Every top-level ",[892,968,969],{},"w*-*\u002F"," directory is a standalone workflow package. The clone ships four, so the next free number is ",[892,972,973],{},"w05",". Check the numbering in your clone before choosing a name.",[820,976,977,978,981,982,985],{},"Copy ",[892,979,980],{},".env.example"," to ",[892,983,984],{},".env"," in the repo root and fill in the four values for your tenant:",[930,987,991],{"className":988,"code":989,"language":990,"meta":935,"style":935},"language-dotenv shiki shiki-themes github-light-high-contrast github-light-high-contrast github-dark-default","BASE_URL=\u003Cyour-tenant-api-base-url>\nAUTH_URL=\u003Cyour-tenant-authentication-url>\nCLIENT_ID=\u003Cyour-client-id>\nCLIENT_SECRET=\u003Cyour-client-secret>\n","dotenv",[892,992,993,998,1003,1009],{"__ignoreMap":935},[939,994,995],{"class":941,"line":942},[939,996,997],{},"BASE_URL=\u003Cyour-tenant-api-base-url>\n",[939,999,1000],{"class":941,"line":956},[939,1001,1002],{},"AUTH_URL=\u003Cyour-tenant-authentication-url>\n",[939,1004,1006],{"class":941,"line":1005},3,[939,1007,1008],{},"CLIENT_ID=\u003Cyour-client-id>\n",[939,1010,1012],{"class":941,"line":1011},4,[939,1013,1014],{},"CLIENT_SECRET=\u003Cyour-client-secret>\n",[820,1016,1017,1018,1020,1021,1024,1025,880],{},"Use the API and authentication URLs supplied for your tenant, not the browser address. ",[892,1019,894],{}," has no ",[892,1022,1023],{},"\u002Fv1"," suffix; the client appends the API version. URL examples are in ",[832,1026,1028],{"href":1027},"\u002Fget-started\u002Fsetup\u002Fsdk-installation\u002F#authenticate-client","Authenticate Client",[820,1030,1031],{},"The deploy script reads this file. Keep it private; the template ignores it in Git.",[820,1033,1034,1037,1038,1040],{},[827,1035,1036],{},"Expected result:"," A clone of the template, and a ",[892,1039,984],{}," in its root holding the four connection settings.",[858,1042,1044],{"id":1043},"step-2-create-a-slack-incoming-webhook","Step 2: Create a Slack incoming webhook",[820,1046,1047],{},"An incoming webhook is a URL that accepts messages for one Slack channel.",[1049,1050,1051,1067,1074,1083,1090],"ol",{},[869,1052,1053,1054,1059,1060,1063,1064,880],{},"Open ",[832,1055,1058],{"href":1056,"rel":1057,"target":878},"https:\u002F\u002Fapi.slack.com\u002Fapps",[876,877],"Your Apps",", select ",[827,1061,1062],{},"Create New App",", then ",[827,1065,1066],{},"From scratch",[869,1068,1069,1070,1073],{},"Enter a name such as ",[892,1071,1072],{},"UniteLabs Notifier",", select your workspace, and create the app.",[869,1075,1053,1076,1079,1080,880],{},[827,1077,1078],{},"Incoming Webhooks"," in the app settings and enable ",[827,1081,1082],{},"Activate Incoming Webhooks",[869,1084,1085,1086,1089],{},"Select ",[827,1087,1088],{},"Add New Webhook to Workspace",", choose your test channel, and authorize the installation.",[869,1091,1092,1093,1096],{},"Back in the app settings, find ",[827,1094,1095],{},"Webhook URLs for Your Workspace",". Check the channel and copy the new webhook URL for the next step.",[820,1098,1099,1101],{},[827,1100,1036],{}," Slack lists a webhook for the intended channel.",[820,1103,1104,1105,880],{},"This example needs no bot token. The URL is a credential: anyone who has it can post to that channel. Store it in the platform secret, never in workflow code. See ",[832,1106,1109],{"href":1107,"rel":1108,"target":878},"https:\u002F\u002Fdocs.slack.dev\u002Fmessaging\u002Fsending-messages-using-incoming-webhooks\u002F",[876,877],"Slack's incoming webhook guide",[858,1111,1113],{"id":1112},"step-3-save-the-webhook-as-a-platform-secret","Step 3: Save the webhook as a platform secret",[820,1115,1116,1117,1119],{},"Open UniteLabs in your browser and select the same tenant your ",[892,1118,984],{}," points to.",[1049,1121,1122,1130,1137,1148],{},[869,1123,1053,1124,1126,1127,880],{},[827,1125,607],{}," using the padlock entry in the navigation, then select ",[827,1128,1129],{},"Create",[869,1131,1132,1133,1136],{},"Name the secret exactly ",[892,1134,1135],{},"slack-webhook",". If that name already exists, check what uses it before reusing it; do not overwrite another integration's credential.",[869,1138,1139,1140,1143,1144,1147],{},"Select the type ",[827,1141,1142],{},"Slack Webhook",", paste the webhook URL into the ",[827,1145,1146],{},"Webhook URL"," field, and save.",[869,1149,1150,1151,1153],{},"Return to the Secrets list and check that ",[892,1152,1135],{}," appears.",[820,1155,1156,1158,1159,1161],{},[827,1157,1036],{}," The tenant holds a secret named ",[892,1160,1135],{}," whose value is the webhook URL.",[820,1163,1164],{},"A saved secret cannot be edited. To change a value, delete the secret and create it again under the same name.",[820,1166,1167,1169,1170,1173,1174,1177],{},[827,1168,1142],{}," stores the URL in ",[892,1171,1172],{},"url",". Do not use the generic ",[827,1175,1176],{},"Webhook"," type; it describes an arbitrary HTTP call and requires a method and headers.",[820,1179,1180,1181,1184],{},"The code in Step 4 loads this type by name through ",[892,1182,1183],{},"SlackWebhook",". Other types use different fields and cannot be loaded that way.",[820,1186,1187,1190,1191,1194,1195,895,1198,895,1201,1204,1205,1208,1209,1211],{},[827,1188,1189],{},"Notify Type"," and ",[827,1192,1193],{},"Allow Private Urls"," are optional. Notify Type sets the Slack message color (",[892,1196,1197],{},"info",[892,1199,1200],{},"success",[892,1202,1203],{},"warning",", or ",[892,1206,1207],{},"failure","; default ",[892,1210,1197],{},"). Allow Private Urls permits private-network URLs and is enabled by default. Leave both unchanged.",[820,1213,1214,1216],{},[832,1215,607],{"href":608}," explains the underlying API and the type schemas. Creating the secret in the interface needs no API request.",[858,1218,1220],{"id":1219},"step-4-add-the-notification-workflow","Step 4: Add the notification workflow",[820,1222,1223,1224,1227],{},"Create the directory ",[892,1225,1226],{},"w05-slack-notify\u002F"," in the repo root, next to the workflows the template ships. It holds three files:",[930,1229,1234],{"className":1230,"code":1232,"language":1233},[1231],"language-text","w05-slack-notify\u002F\n├── pyproject.toml\n└── src\u002F\n    └── w05_slack_notify\u002F\n        ├── __init__.py\n        └── workflow.py\n","text",[892,1235,1232],{"__ignoreMap":935},[820,1237,1238,1239,1242,1243,1246],{},"Place the Python files in ",[892,1240,1241],{},"src\u002Fw05_slack_notify\u002F",", not directly in ",[892,1244,1245],{},"src\u002F",". The deploy script derives the directory name from the slug by replacing hyphens with underscores; the build backend expects the same layout.",[820,1248,1249,1250,1253],{},"Create ",[892,1251,1252],{},"__init__.py"," to mark the directory as a Python package:",[930,1255,1259],{"className":1256,"code":1257,"language":1258,"meta":935,"style":935},"language-python shiki shiki-themes github-light-high-contrast github-light-high-contrast github-dark-default","\"\"\"Workflow 05: Slack Notifier Demo. See workflow.py for the @workflow entrypoint.\"\"\"\n\nfrom importlib.metadata import version\n\n__version__ = version(\"w05-slack-notify\")\n","python",[892,1260,1261,1266,1272,1288,1292],{"__ignoreMap":935},[939,1262,1263],{"class":941,"line":942},[939,1264,1265],{"class":949},"\"\"\"Workflow 05: Slack Notifier Demo. See workflow.py for the @workflow entrypoint.\"\"\"\n",[939,1267,1268],{"class":941,"line":956},[939,1269,1271],{"emptyLinePlaceholder":1270},true,"\n",[939,1273,1274,1278,1282,1285],{"class":941,"line":1005},[939,1275,1277],{"class":1276},"sxkbv","from",[939,1279,1281],{"class":1280},"sc-9e"," importlib.metadata ",[939,1283,1284],{"class":1276},"import",[939,1286,1287],{"class":1280}," version\n",[939,1289,1290],{"class":941,"line":1011},[939,1291,1271],{"emptyLinePlaceholder":1270},[939,1293,1295,1298,1301,1304,1307],{"class":941,"line":1294},5,[939,1296,1297],{"class":959},"__version__",[939,1299,1300],{"class":1276}," =",[939,1302,1303],{"class":1280}," version(",[939,1305,1306],{"class":949},"\"w05-slack-notify\"",[939,1308,1309],{"class":1280},")\n",[820,1311,1312,1313,1316],{},"Write ",[892,1314,1315],{},"pyproject.toml",":",[930,1318,1322],{"className":1319,"code":1320,"language":1321,"meta":935,"style":935},"language-toml shiki shiki-themes github-light-high-contrast github-light-high-contrast github-dark-default","[project]\nname = \"w05-slack-notify\"\nversion = \"0.1.0\"\ndescription = \"Posts a Slack message, then waits for operator confirmation.\"\nrequires-python = \">=3.12\"\ndependencies = [\n    \"unitelabs-sdk[automate]\",\n    \"httpx\",\n]\n\n[tool.unitelabs.workflow]\ndisplay_name = \"Slack confirmation demo\"\nentrypoint = \"workflow.py:slack_notify_flow\"\ntags = [\"slack\", \"hitl\"]\n\n[tool.uv]\nprerelease = \"if-necessary-or-explicit\"\n\n[[tool.uv.index]]\nname = \"unitelabs\"\nurl = \"https:\u002F\u002Fgitlab.com\u002Fapi\u002Fv4\u002Fgroups\u002F1009252\u002F-\u002Fpackages\u002Fpypi\u002Fsimple\"\nauthenticate = \"always\"\n\n[tool.uv.sources]\nunitelabs-sdk = { index = \"unitelabs\" }\n\n[build-system]\nrequires = [\"uv_build>=0.4,\u003C0.10\"]\nbuild-backend = \"uv_build\"\n","toml",[892,1323,1324,1329,1334,1339,1344,1349,1355,1361,1367,1373,1378,1384,1390,1396,1402,1407,1413,1419,1424,1430,1436,1442,1448,1453,1459,1465,1470,1476,1482],{"__ignoreMap":935},[939,1325,1326],{"class":941,"line":942},[939,1327,1328],{},"[project]\n",[939,1330,1331],{"class":941,"line":956},[939,1332,1333],{},"name = \"w05-slack-notify\"\n",[939,1335,1336],{"class":941,"line":1005},[939,1337,1338],{},"version = \"0.1.0\"\n",[939,1340,1341],{"class":941,"line":1011},[939,1342,1343],{},"description = \"Posts a Slack message, then waits for operator confirmation.\"\n",[939,1345,1346],{"class":941,"line":1294},[939,1347,1348],{},"requires-python = \">=3.12\"\n",[939,1350,1352],{"class":941,"line":1351},6,[939,1353,1354],{},"dependencies = [\n",[939,1356,1358],{"class":941,"line":1357},7,[939,1359,1360],{},"    \"unitelabs-sdk[automate]\",\n",[939,1362,1364],{"class":941,"line":1363},8,[939,1365,1366],{},"    \"httpx\",\n",[939,1368,1370],{"class":941,"line":1369},9,[939,1371,1372],{},"]\n",[939,1374,1376],{"class":941,"line":1375},10,[939,1377,1271],{"emptyLinePlaceholder":1270},[939,1379,1381],{"class":941,"line":1380},11,[939,1382,1383],{},"[tool.unitelabs.workflow]\n",[939,1385,1387],{"class":941,"line":1386},12,[939,1388,1389],{},"display_name = \"Slack confirmation demo\"\n",[939,1391,1393],{"class":941,"line":1392},13,[939,1394,1395],{},"entrypoint = \"workflow.py:slack_notify_flow\"\n",[939,1397,1399],{"class":941,"line":1398},14,[939,1400,1401],{},"tags = [\"slack\", \"hitl\"]\n",[939,1403,1405],{"class":941,"line":1404},15,[939,1406,1271],{"emptyLinePlaceholder":1270},[939,1408,1410],{"class":941,"line":1409},16,[939,1411,1412],{},"[tool.uv]\n",[939,1414,1416],{"class":941,"line":1415},17,[939,1417,1418],{},"prerelease = \"if-necessary-or-explicit\"\n",[939,1420,1422],{"class":941,"line":1421},18,[939,1423,1271],{"emptyLinePlaceholder":1270},[939,1425,1427],{"class":941,"line":1426},19,[939,1428,1429],{},"[[tool.uv.index]]\n",[939,1431,1433],{"class":941,"line":1432},20,[939,1434,1435],{},"name = \"unitelabs\"\n",[939,1437,1439],{"class":941,"line":1438},21,[939,1440,1441],{},"url = \"https:\u002F\u002Fgitlab.com\u002Fapi\u002Fv4\u002Fgroups\u002F1009252\u002F-\u002Fpackages\u002Fpypi\u002Fsimple\"\n",[939,1443,1445],{"class":941,"line":1444},22,[939,1446,1447],{},"authenticate = \"always\"\n",[939,1449,1451],{"class":941,"line":1450},23,[939,1452,1271],{"emptyLinePlaceholder":1270},[939,1454,1456],{"class":941,"line":1455},24,[939,1457,1458],{},"[tool.uv.sources]\n",[939,1460,1462],{"class":941,"line":1461},25,[939,1463,1464],{},"unitelabs-sdk = { index = \"unitelabs\" }\n",[939,1466,1468],{"class":941,"line":1467},26,[939,1469,1271],{"emptyLinePlaceholder":1270},[939,1471,1473],{"class":941,"line":1472},27,[939,1474,1475],{},"[build-system]\n",[939,1477,1479],{"class":941,"line":1478},28,[939,1480,1481],{},"requires = [\"uv_build>=0.4,\u003C0.10\"]\n",[939,1483,1485],{"class":941,"line":1484},29,[939,1486,1487],{},"build-backend = \"uv_build\"\n",[820,1489,1490,1491,1494,1495,1498,1499,1502,1503,1506],{},"The deploy script reads ",[892,1492,1493],{},"[tool.unitelabs.workflow]",": ",[892,1496,1497],{},"display_name"," is the platform name and ",[892,1500,1501],{},"entrypoint"," names the file and function to start. ",[892,1504,1505],{},"[project].name"," is the deploy slug.",[820,1508,1509,1510,1513],{},"The SDK requirement carries no version, so you get the current release. The code below needs ",[892,1511,1512],{},"operator_confirm",", which the SDK exports from 0.13.0 onwards; 0.12 does not have it. Keep that in mind if you reuse a workflow package that pins an older version.",[820,1515,1516,1517,1316],{},"Now write ",[892,1518,1519],{},"src\u002Fw05_slack_notify\u002Fworkflow.py",[930,1521,1523],{"className":1256,"code":1522,"language":1258,"meta":935,"style":935},"import httpx\nfrom prefect.blocks.notifications import SlackWebhook\nfrom unitelabs.sdk import get_logger, operator_confirm, phase, step, workflow\n\nSECRET_NAME = \"slack-webhook\"\n\n\n@step(name=\"Notify Slack\")\nasync def notify_slack(text: str) -> None:\n    webhook = await SlackWebhook.aload(SECRET_NAME)\n    async with httpx.AsyncClient() as http:\n        response = await http.post(webhook.url.get_secret_value(), json={\"text\": text})\n        response.raise_for_status()\n    get_logger().info(\"Slack notification sent.\")\n\n\n@phase(name=\"Request confirmation\")\nasync def request_confirmation() -> None:\n    await notify_slack(\n        \"Slack confirmation demo: open UniteLabs, find the waiting run, \"\n        \"and confirm the test request.\"\n    )\n    await operator_confirm(\"Confirm that you received the Slack test message.\", timeout=600)\n\n\n@workflow(name=\"Slack confirmation demo\")\nasync def slack_notify_flow() -> None:\n    await request_confirmation()\n    get_logger().info(\"Confirmation received; workflow complete.\")\n",[892,1524,1525,1532,1544,1556,1560,1570,1574,1578,1598,1624,1641,1658,1684,1689,1699,1703,1707,1723,1739,1747,1752,1757,1762,1784,1788,1792,1808,1823,1830],{"__ignoreMap":935},[939,1526,1527,1529],{"class":941,"line":942},[939,1528,1284],{"class":1276},[939,1530,1531],{"class":1280}," httpx\n",[939,1533,1534,1536,1539,1541],{"class":941,"line":956},[939,1535,1277],{"class":1276},[939,1537,1538],{"class":1280}," prefect.blocks.notifications ",[939,1540,1284],{"class":1276},[939,1542,1543],{"class":1280}," SlackWebhook\n",[939,1545,1546,1548,1551,1553],{"class":941,"line":1005},[939,1547,1277],{"class":1276},[939,1549,1550],{"class":1280}," unitelabs.sdk ",[939,1552,1284],{"class":1276},[939,1554,1555],{"class":1280}," get_logger, operator_confirm, phase, step, workflow\n",[939,1557,1558],{"class":941,"line":1011},[939,1559,1271],{"emptyLinePlaceholder":1270},[939,1561,1562,1565,1567],{"class":941,"line":1294},[939,1563,1564],{"class":959},"SECRET_NAME",[939,1566,1300],{"class":1276},[939,1568,1569],{"class":949}," \"slack-webhook\"\n",[939,1571,1572],{"class":941,"line":1351},[939,1573,1271],{"emptyLinePlaceholder":1270},[939,1575,1576],{"class":941,"line":1357},[939,1577,1271],{"emptyLinePlaceholder":1270},[939,1579,1580,1584,1587,1590,1593,1596],{"class":941,"line":1363},[939,1581,1583],{"class":1582},"s4qFZ","@step",[939,1585,1586],{"class":1280},"(",[939,1588,1589],{"class":945},"name",[939,1591,1592],{"class":1276},"=",[939,1594,1595],{"class":949},"\"Notify Slack\"",[939,1597,1309],{"class":1280},[939,1599,1600,1603,1606,1609,1612,1615,1618,1621],{"class":941,"line":1369},[939,1601,1602],{"class":1276},"async",[939,1604,1605],{"class":1276}," def",[939,1607,1608],{"class":1582}," notify_slack",[939,1610,1611],{"class":1280},"(text: ",[939,1613,1614],{"class":959},"str",[939,1616,1617],{"class":1280},") -> ",[939,1619,1620],{"class":959},"None",[939,1622,1623],{"class":1280},":\n",[939,1625,1626,1629,1631,1634,1637,1639],{"class":941,"line":1375},[939,1627,1628],{"class":1280},"    webhook ",[939,1630,1592],{"class":1276},[939,1632,1633],{"class":1276}," await",[939,1635,1636],{"class":1280}," SlackWebhook.aload(",[939,1638,1564],{"class":959},[939,1640,1309],{"class":1280},[939,1642,1643,1646,1649,1652,1655],{"class":941,"line":1380},[939,1644,1645],{"class":1276},"    async",[939,1647,1648],{"class":1276}," with",[939,1650,1651],{"class":1280}," httpx.AsyncClient() ",[939,1653,1654],{"class":1276},"as",[939,1656,1657],{"class":1280}," http:\n",[939,1659,1660,1663,1665,1667,1670,1673,1675,1678,1681],{"class":941,"line":1386},[939,1661,1662],{"class":1280},"        response ",[939,1664,1592],{"class":1276},[939,1666,1633],{"class":1276},[939,1668,1669],{"class":1280}," http.post(webhook.url.get_secret_value(), ",[939,1671,1672],{"class":945},"json",[939,1674,1592],{"class":1276},[939,1676,1677],{"class":1280},"{",[939,1679,1680],{"class":949},"\"text\"",[939,1682,1683],{"class":1280},": text})\n",[939,1685,1686],{"class":941,"line":1392},[939,1687,1688],{"class":1280},"        response.raise_for_status()\n",[939,1690,1691,1694,1697],{"class":941,"line":1398},[939,1692,1693],{"class":1280},"    get_logger().info(",[939,1695,1696],{"class":949},"\"Slack notification sent.\"",[939,1698,1309],{"class":1280},[939,1700,1701],{"class":941,"line":1404},[939,1702,1271],{"emptyLinePlaceholder":1270},[939,1704,1705],{"class":941,"line":1409},[939,1706,1271],{"emptyLinePlaceholder":1270},[939,1708,1709,1712,1714,1716,1718,1721],{"class":941,"line":1415},[939,1710,1711],{"class":1582},"@phase",[939,1713,1586],{"class":1280},[939,1715,1589],{"class":945},[939,1717,1592],{"class":1276},[939,1719,1720],{"class":949},"\"Request confirmation\"",[939,1722,1309],{"class":1280},[939,1724,1725,1727,1729,1732,1735,1737],{"class":941,"line":1421},[939,1726,1602],{"class":1276},[939,1728,1605],{"class":1276},[939,1730,1731],{"class":1582}," request_confirmation",[939,1733,1734],{"class":1280},"() -> ",[939,1736,1620],{"class":959},[939,1738,1623],{"class":1280},[939,1740,1741,1744],{"class":941,"line":1426},[939,1742,1743],{"class":1276},"    await",[939,1745,1746],{"class":1280}," notify_slack(\n",[939,1748,1749],{"class":941,"line":1432},[939,1750,1751],{"class":949},"        \"Slack confirmation demo: open UniteLabs, find the waiting run, \"\n",[939,1753,1754],{"class":941,"line":1438},[939,1755,1756],{"class":949},"        \"and confirm the test request.\"\n",[939,1758,1759],{"class":941,"line":1444},[939,1760,1761],{"class":1280},"    )\n",[939,1763,1764,1766,1769,1772,1774,1777,1779,1782],{"class":941,"line":1450},[939,1765,1743],{"class":1276},[939,1767,1768],{"class":1280}," operator_confirm(",[939,1770,1771],{"class":949},"\"Confirm that you received the Slack test message.\"",[939,1773,895],{"class":1280},[939,1775,1776],{"class":945},"timeout",[939,1778,1592],{"class":1276},[939,1780,1781],{"class":959},"600",[939,1783,1309],{"class":1280},[939,1785,1786],{"class":941,"line":1455},[939,1787,1271],{"emptyLinePlaceholder":1270},[939,1789,1790],{"class":941,"line":1461},[939,1791,1271],{"emptyLinePlaceholder":1270},[939,1793,1794,1797,1799,1801,1803,1806],{"class":941,"line":1467},[939,1795,1796],{"class":1582},"@workflow",[939,1798,1586],{"class":1280},[939,1800,1589],{"class":945},[939,1802,1592],{"class":1276},[939,1804,1805],{"class":949},"\"Slack confirmation demo\"",[939,1807,1309],{"class":1280},[939,1809,1810,1812,1814,1817,1819,1821],{"class":941,"line":1472},[939,1811,1602],{"class":1276},[939,1813,1605],{"class":1276},[939,1815,1816],{"class":1582}," slack_notify_flow",[939,1818,1734],{"class":1280},[939,1820,1620],{"class":959},[939,1822,1623],{"class":1280},[939,1824,1825,1827],{"class":941,"line":1478},[939,1826,1743],{"class":1276},[939,1828,1829],{"class":1280}," request_confirmation()\n",[939,1831,1832,1834,1837],{"class":941,"line":1484},[939,1833,1693],{"class":1280},[939,1835,1836],{"class":949},"\"Confirmation received; workflow complete.\"",[939,1838,1309],{"class":1280},[820,1840,1841,1844],{},[892,1842,1843],{},"notify_slack()"," loads the secret and posts the message without exposing the URL in code.",[820,1846,1847,1850,1851,1854,1855,1860,1861,1864],{},[892,1848,1849],{},"await webhook.anotify(text)"," uses the engine notification layer, which adds a fixed ",[892,1852,1853],{},"Prefect Notifications"," footer and a colored attachment. This example posts to the webhook directly and can therefore use any ",[832,1856,1859],{"href":1857,"rel":1858,"target":878},"https:\u002F\u002Fdocs.slack.dev\u002Fmessaging\u002Fformatting-message-text\u002F",[876,877],"Slack message payload",". See the ",[832,1862,1863],{"href":516},"Workflow, Phase, Step taxonomy"," when splitting larger workflows.",[820,1866,1867,1870,1871,1873,1874,1877],{},[892,1868,1869],{},"operator_confirm()"," follows ",[832,1872,566],{"href":567},". This example allows ten minutes (",[892,1875,1876],{},"timeout=600",") and performs no physical action. The SDK sets step retries to 0, so a failed send is not retried automatically.",[820,1879,1880,1881,1884,1885,880],{},"This package omits ",[892,1882,1883],{},"__main__.py",": the secret and confirmation require a platform run. The platform starts the function named in ",[892,1886,1501],{},[820,1888,1889],{},"Install the new package:",[930,1891,1893],{"className":932,"code":1892,"language":934,"meta":935,"style":935},"uv sync --directory w05-slack-notify\n",[892,1894,1895],{"__ignoreMap":935},[939,1896,1897,1899,1902,1905],{"class":941,"line":942},[939,1898,879],{"class":945},[939,1900,1901],{"class":949}," sync",[939,1903,1904],{"class":959}," --directory",[939,1906,1907],{"class":949}," w05-slack-notify\n",[820,1909,1910,1912,1913,1916],{},[827,1911,1036],{}," The command creates ",[892,1914,1915],{},"w05-slack-notify\u002F.venv"," and a lockfile without reporting a dependency conflict.",[858,1918,1920],{"id":1919},"step-5-deploy-the-workflow","Step 5: Deploy the workflow",[820,1922,1923],{},"From the repo root:",[930,1925,1927],{"className":932,"code":1926,"language":934,"meta":935,"style":935},"uv run scripts\u002Fdeploy.py w05-slack-notify\n",[892,1928,1929],{"__ignoreMap":935},[939,1930,1931,1933,1936,1939],{"class":941,"line":942},[939,1932,879],{"class":945},[939,1934,1935],{"class":949}," run",[939,1937,1938],{"class":949}," scripts\u002Fdeploy.py",[939,1940,1907],{"class":949},[820,1942,1943,1944,1946,1947,1950,1951,1953,1954,880],{},"The argument is ",[892,1945,1505],{},". The script bundles the workflow with the template's ",[892,1948,1949],{},"shared\u002F"," library and creates or updates the platform workflow. ",[832,1952,554],{"href":555}," documents other forms, including ",[892,1955,1956],{},"--channel dev",[820,1958,1959,1961,1962,1965],{},[827,1960,1036],{}," The script prints the display name, the resolved entrypoint, the bundle size, and whether it created or updated the record. The workflow then appears under ",[827,1963,1964],{},"Workflows"," in the platform.",[820,1967,1968,1969,1971,1972,1975,1976,1979],{},"If the upload rejects the SDK's ",[892,1970,1796],{}," entrypoint, replace ",[827,1973,1974],{},"both"," SDK decorators with ",[892,1977,1978],{},"flow"," and deploy again:",[930,1981,1983],{"className":1256,"code":1982,"language":1258,"meta":935,"style":935},"from prefect import flow\n\n@flow(name=\"Request confirmation\")\nasync def request_confirmation() -> None:\n    ...\n\n@flow(name=\"Slack confirmation demo\")\nasync def slack_notify_flow() -> None:\n    ...\n",[892,1984,1985,1997,2001,2016,2030,2035,2039,2053,2067],{"__ignoreMap":935},[939,1986,1987,1989,1992,1994],{"class":941,"line":942},[939,1988,1277],{"class":1276},[939,1990,1991],{"class":1280}," prefect ",[939,1993,1284],{"class":1276},[939,1995,1996],{"class":1280}," flow\n",[939,1998,1999],{"class":941,"line":956},[939,2000,1271],{"emptyLinePlaceholder":1270},[939,2002,2003,2006,2008,2010,2012,2014],{"class":941,"line":1005},[939,2004,2005],{"class":1582},"@flow",[939,2007,1586],{"class":1280},[939,2009,1589],{"class":945},[939,2011,1592],{"class":1276},[939,2013,1720],{"class":949},[939,2015,1309],{"class":1280},[939,2017,2018,2020,2022,2024,2026,2028],{"class":941,"line":1011},[939,2019,1602],{"class":1276},[939,2021,1605],{"class":1276},[939,2023,1731],{"class":1582},[939,2025,1734],{"class":1280},[939,2027,1620],{"class":959},[939,2029,1623],{"class":1280},[939,2031,2032],{"class":941,"line":1294},[939,2033,2034],{"class":959},"    ...\n",[939,2036,2037],{"class":941,"line":1351},[939,2038,1271],{"emptyLinePlaceholder":1270},[939,2040,2041,2043,2045,2047,2049,2051],{"class":941,"line":1357},[939,2042,2005],{"class":1582},[939,2044,1586],{"class":1280},[939,2046,1589],{"class":945},[939,2048,1592],{"class":1276},[939,2050,1805],{"class":949},[939,2052,1309],{"class":1280},[939,2054,2055,2057,2059,2061,2063,2065],{"class":941,"line":1363},[939,2056,1602],{"class":1276},[939,2058,1605],{"class":1276},[939,2060,1816],{"class":1582},[939,2062,1734],{"class":1280},[939,2064,1620],{"class":959},[939,2066,1623],{"class":1280},[939,2068,2069],{"class":941,"line":1369},[939,2070,2034],{"class":959},[820,2072,2073,2074,2076,2077,2079,2080,2082,2083,2085],{},"Swap the pair together. ",[892,2075,1711],{}," reads a context file that only ",[892,2078,1796],{}," writes, so a ",[892,2081,2005],{}," entrypoint combined with a ",[892,2084,1711],{}," crashes on the first phase call. The template's authoring rules state the same constraint. Keep the SDK decorators if the deploy succeeds.",[858,2087,2089],{"id":2088},"step-6-run-the-workflow-and-confirm-the-request","Step 6: Run the workflow and confirm the request",[1049,2091,2092,2100],{},[869,2093,1053,2094,2096,2097,880],{},[827,2095,1964],{}," in the platform and select ",[827,2098,2099],{},"Slack confirmation demo",[869,2101,1085,2102,2105,2106,880],{},[827,2103,2104],{},"Run",". The run appears under ",[827,2107,527],{},[820,2109,2110,2113],{},[827,2111,2112],{},"Expected result in Slack:"," A message from your app asking you to confirm the waiting run.",[820,2115,2116,2119,2120,2123,2124,2127],{},[827,2117,2118],{},"Expected result in the platform:"," The run reaches ",[892,2121,2122],{},"AWAITING_INPUT",". Open it and submit the confirmation. The input has a single field, ",[892,2125,2126],{},"confirmed",", already set to true.",[820,2129,2130,2119,2133,2136,2137,2140,2141,2144],{},[827,2131,2132],{},"Expected result after the confirmation:",[892,2134,2135],{},"COMPLETED",", and its log shows ",[892,2138,2139],{},"Slack notification sent."," followed by ",[892,2142,2143],{},"Confirmation received; workflow complete."," Without a confirmation within ten minutes, the run fails on the timeout.",[820,2146,2147,2149],{},[832,2148,558],{"href":559}," covers starting runs from Python and from the REST API, which is the way to trigger this workflow from an existing system.",[858,2151,855],{"id":2152},"alternative-write-the-workflow-in-the-platform",[820,2154,2155],{},"This path replaces Steps 1, 4, and 5. You still need the webhook and secret.",[1049,2157,2158,2165,2175,2193,2207],{},[869,2159,1053,2160,2162,2163,880],{},[827,2161,1964],{}," and select ",[827,2164,1129],{},[869,2166,2167,2168,2170,2171,2174],{},"Enter the name ",[892,2169,2099],{},". Tags and description are optional. Leave ",[827,2172,2173],{},"Runtime Image"," empty to use the platform default.",[869,2176,2177,2178,2181,2182,1190,2185,2188,2189,2192],{},"Under ",[827,2179,2180],{},"Dependencies",", add two entries: ",[892,2183,2184],{},"unitelabs-sdk[automate]",[892,2186,2187],{},"httpx",". The field takes the same syntax as ",[892,2190,2191],{},"pip install",", so extras and version specifiers both work; without a version you get the current release. The platform installs these packages for the run, which is why this path needs no registry access from you.",[869,2194,2195,2196,2199,2200,2202,2203,2206],{},"Leave the ",[827,2197,2198],{},"Files"," upload empty and select ",[827,2201,1129],{},". The platform creates the workflow with a file ",[892,2204,2205],{},"workflow.py"," holding a placeholder flow.",[869,2208,2209,2210,2213,2214,2216],{},"Open the new workflow, select the ",[827,2211,2212],{},"Code"," tab, and replace the content of ",[892,2215,2205],{}," with the code below. Save it.",[930,2218,2220],{"className":1256,"code":2219,"language":1258,"meta":935,"style":935},"import httpx\nfrom prefect.blocks.notifications import SlackWebhook\nfrom unitelabs.sdk import get_logger, operator_confirm, phase, step, workflow\n\nSECRET_NAME = \"slack-webhook\"\n\n\n@step(name=\"Notify Slack\")\nasync def notify_slack(text: str) -> None:\n    webhook = await SlackWebhook.aload(SECRET_NAME)\n    async with httpx.AsyncClient() as http:\n        response = await http.post(webhook.url.get_secret_value(), json={\"text\": text})\n        response.raise_for_status()\n    get_logger().info(\"Slack notification sent.\")\n\n\n@phase(name=\"Request confirmation\")\nasync def request_confirmation() -> None:\n    await notify_slack(\n        \"Slack confirmation demo: open UniteLabs, find the waiting run, \"\n        \"and confirm the test request.\"\n    )\n    await operator_confirm(\"Confirm that you received the Slack test message.\", timeout=600)\n\n\n@workflow(name=\"Slack confirmation demo\")\nasync def entrypoint() -> None:\n    await request_confirmation()\n    get_logger().info(\"Confirmation received; workflow complete.\")\n",[892,2221,2222,2228,2238,2248,2252,2260,2264,2268,2282,2300,2314,2326,2346,2350,2358,2362,2366,2380,2394,2400,2404,2408,2412,2430,2434,2438,2452,2467,2473],{"__ignoreMap":935},[939,2223,2224,2226],{"class":941,"line":942},[939,2225,1284],{"class":1276},[939,2227,1531],{"class":1280},[939,2229,2230,2232,2234,2236],{"class":941,"line":956},[939,2231,1277],{"class":1276},[939,2233,1538],{"class":1280},[939,2235,1284],{"class":1276},[939,2237,1543],{"class":1280},[939,2239,2240,2242,2244,2246],{"class":941,"line":1005},[939,2241,1277],{"class":1276},[939,2243,1550],{"class":1280},[939,2245,1284],{"class":1276},[939,2247,1555],{"class":1280},[939,2249,2250],{"class":941,"line":1011},[939,2251,1271],{"emptyLinePlaceholder":1270},[939,2253,2254,2256,2258],{"class":941,"line":1294},[939,2255,1564],{"class":959},[939,2257,1300],{"class":1276},[939,2259,1569],{"class":949},[939,2261,2262],{"class":941,"line":1351},[939,2263,1271],{"emptyLinePlaceholder":1270},[939,2265,2266],{"class":941,"line":1357},[939,2267,1271],{"emptyLinePlaceholder":1270},[939,2269,2270,2272,2274,2276,2278,2280],{"class":941,"line":1363},[939,2271,1583],{"class":1582},[939,2273,1586],{"class":1280},[939,2275,1589],{"class":945},[939,2277,1592],{"class":1276},[939,2279,1595],{"class":949},[939,2281,1309],{"class":1280},[939,2283,2284,2286,2288,2290,2292,2294,2296,2298],{"class":941,"line":1369},[939,2285,1602],{"class":1276},[939,2287,1605],{"class":1276},[939,2289,1608],{"class":1582},[939,2291,1611],{"class":1280},[939,2293,1614],{"class":959},[939,2295,1617],{"class":1280},[939,2297,1620],{"class":959},[939,2299,1623],{"class":1280},[939,2301,2302,2304,2306,2308,2310,2312],{"class":941,"line":1375},[939,2303,1628],{"class":1280},[939,2305,1592],{"class":1276},[939,2307,1633],{"class":1276},[939,2309,1636],{"class":1280},[939,2311,1564],{"class":959},[939,2313,1309],{"class":1280},[939,2315,2316,2318,2320,2322,2324],{"class":941,"line":1380},[939,2317,1645],{"class":1276},[939,2319,1648],{"class":1276},[939,2321,1651],{"class":1280},[939,2323,1654],{"class":1276},[939,2325,1657],{"class":1280},[939,2327,2328,2330,2332,2334,2336,2338,2340,2342,2344],{"class":941,"line":1386},[939,2329,1662],{"class":1280},[939,2331,1592],{"class":1276},[939,2333,1633],{"class":1276},[939,2335,1669],{"class":1280},[939,2337,1672],{"class":945},[939,2339,1592],{"class":1276},[939,2341,1677],{"class":1280},[939,2343,1680],{"class":949},[939,2345,1683],{"class":1280},[939,2347,2348],{"class":941,"line":1392},[939,2349,1688],{"class":1280},[939,2351,2352,2354,2356],{"class":941,"line":1398},[939,2353,1693],{"class":1280},[939,2355,1696],{"class":949},[939,2357,1309],{"class":1280},[939,2359,2360],{"class":941,"line":1404},[939,2361,1271],{"emptyLinePlaceholder":1270},[939,2363,2364],{"class":941,"line":1409},[939,2365,1271],{"emptyLinePlaceholder":1270},[939,2367,2368,2370,2372,2374,2376,2378],{"class":941,"line":1415},[939,2369,1711],{"class":1582},[939,2371,1586],{"class":1280},[939,2373,1589],{"class":945},[939,2375,1592],{"class":1276},[939,2377,1720],{"class":949},[939,2379,1309],{"class":1280},[939,2381,2382,2384,2386,2388,2390,2392],{"class":941,"line":1421},[939,2383,1602],{"class":1276},[939,2385,1605],{"class":1276},[939,2387,1731],{"class":1582},[939,2389,1734],{"class":1280},[939,2391,1620],{"class":959},[939,2393,1623],{"class":1280},[939,2395,2396,2398],{"class":941,"line":1426},[939,2397,1743],{"class":1276},[939,2399,1746],{"class":1280},[939,2401,2402],{"class":941,"line":1432},[939,2403,1751],{"class":949},[939,2405,2406],{"class":941,"line":1438},[939,2407,1756],{"class":949},[939,2409,2410],{"class":941,"line":1444},[939,2411,1761],{"class":1280},[939,2413,2414,2416,2418,2420,2422,2424,2426,2428],{"class":941,"line":1450},[939,2415,1743],{"class":1276},[939,2417,1768],{"class":1280},[939,2419,1771],{"class":949},[939,2421,895],{"class":1280},[939,2423,1776],{"class":945},[939,2425,1592],{"class":1276},[939,2427,1781],{"class":959},[939,2429,1309],{"class":1280},[939,2431,2432],{"class":941,"line":1455},[939,2433,1271],{"emptyLinePlaceholder":1270},[939,2435,2436],{"class":941,"line":1461},[939,2437,1271],{"emptyLinePlaceholder":1270},[939,2439,2440,2442,2444,2446,2448,2450],{"class":941,"line":1467},[939,2441,1796],{"class":1582},[939,2443,1586],{"class":1280},[939,2445,1589],{"class":945},[939,2447,1592],{"class":1276},[939,2449,1805],{"class":949},[939,2451,1309],{"class":1280},[939,2453,2454,2456,2458,2461,2463,2465],{"class":941,"line":1472},[939,2455,1602],{"class":1276},[939,2457,1605],{"class":1276},[939,2459,2460],{"class":1582}," entrypoint",[939,2462,1734],{"class":1280},[939,2464,1620],{"class":959},[939,2466,1623],{"class":1280},[939,2468,2469,2471],{"class":941,"line":1478},[939,2470,1743],{"class":1276},[939,2472,1829],{"class":1280},[939,2474,2475,2477,2479],{"class":941,"line":1484},[939,2476,1693],{"class":1280},[939,2478,1836],{"class":949},[939,2480,1309],{"class":1280},[820,2482,2483,2484,2486],{},"This is the Step 4 code with the last function renamed to ",[892,2485,1501],{},", which matches the record created by the form.",[820,2488,2489,2491,2492,2494,2495,2497,2498,2500],{},[827,2490,1036],{}," The workflow appears under ",[827,2493,1964],{},", its ",[827,2496,2212],{}," tab shows your ",[892,2499,2205],{},", and its dependency list shows the SDK entry. Continue with Step 6.",[820,2502,2503,2504,2506,2507,2509,2510,880],{},"Files in the ",[827,2505,2212],{}," tab share one directory and can import each other directly. This path has no package layout or ",[892,2508,1252],{},"; uploading a ZIP instead uses the template layout, including ",[892,2511,1315],{},[858,2513,2515],{"id":2514},"add-the-notification-to-an-existing-workflow","Add the notification to an existing workflow",[820,2517,977,2518,2520,2521,2523],{},[892,2519,1843],{}," and its imports into your workflow, then call it before ",[892,2522,1869],{},". Name the action, run, and confirmation location in the message. Keep the webhook in the platform secret.",[820,2525,2526,2527,2529,2530,2533,2534,2536,2537,2540,2541,2543,2544,981,2547,1190,2550,2553,2554,2557,2558,2561],{},"When several workflows need the notification, move the function into the template's ",[892,2528,1949],{}," library, under ",[892,2531,2532],{},"shared\u002Fsrc\u002Fshared\u002Fsteps\u002F",", and import it from there. The deploy script ships ",[892,2535,1949],{}," with every bundle, so the import resolves on the platform exactly as it does locally. A workflow that imports from ",[892,2538,2539],{},"shared"," has to declare it, which the ",[892,2542,1315],{}," above does not: add ",[892,2545,2546],{},"\"shared\"",[892,2548,2549],{},"[project].dependencies",[892,2551,2552],{},"shared = { path = \"..\u002Fshared\", editable = true }"," under ",[892,2555,2556],{},"[tool.uv.sources]",", then run ",[892,2559,2560],{},"uv sync --directory shared"," once before you sync the workflow. The workflows the template ships show both lines.",[858,2563,2565],{"id":2564},"troubleshooting","Troubleshooting",[2567,2568,2569,2582],"table",{},[2570,2571,2572],"thead",{},[2573,2574,2575,2579],"tr",{},[2576,2577,2578],"th",{},"What you observe",[2576,2580,2581],{},"What to check",[2583,2584,2585,2594,2611,2628,2643,2654,2662,2673,2684,2701,2712,2720,2730,2738],"tbody",{},[2573,2586,2587,2591],{},[2588,2589,2590],"td",{},"uv is missing, or installing dependencies fails",[2588,2592,2593],{},"Check the uv installation and registry access. Registry credentials and platform credentials are different.",[2573,2595,2596,2605],{},[2588,2597,2598,2601,2602],{},[892,2599,2600],{},"uv sync"," cannot resolve ",[892,2603,2604],{},"unitelabs-sdk",[2588,2606,2607,2608,2610],{},"Check the index entry in ",[892,2609,1315],{}," and your registry authentication.",[2573,2612,2613,2616],{},[2588,2614,2615],{},"The deploy script reports a missing package directory",[2588,2617,2618,2619,2621,2622,2625,2626,880],{},"The directory under ",[892,2620,1245],{}," must be the slug with underscores, so ",[892,2623,2624],{},"w05-slack-notify"," needs ",[892,2627,1241],{},[2573,2629,2630,2633],{},[2588,2631,2632],{},"The deploy script cannot find the entrypoint file",[2588,2634,2635,2636,2639,2640,880],{},"Check ",[892,2637,2638],{},"[tool.unitelabs.workflow].entrypoint",". The path is relative to the package directory, and the format is ",[892,2641,2642],{},"\u003Cfile>:\u003Cfunction>",[2573,2644,2645,2648],{},[2588,2646,2647],{},"The deploy fails with HTTP 400 or 500",[2588,2649,2650,2651,2653],{},"Switch the entrypoint and the phase together to the engine's ",[892,2652,1978],{}," decorator, as described in Step 5. Switching only one of them crashes the run.",[2573,2655,2656,2659],{},[2588,2657,2658],{},"The deploy stops on a duplicate display name",[2588,2660,2661],{},"Two platform records share that name. Resolve it in the interface first; the script refuses to guess which one to overwrite.",[2573,2663,2664,2667],{},[2588,2665,2666],{},"Authentication fails",[2588,2668,2669,2670,2672],{},"Check the four values in ",[892,2671,984],{},", the tenant, and network access.",[2573,2674,2675,2681],{},[2588,2676,2677,2678],{},"The run fails at ",[892,2679,2680],{},"SlackWebhook.aload",[2588,2682,2683],{},"Check the secret name and that its type is Slack Webhook. Another type stores the value under a different field, and the block cannot load it.",[2573,2685,2686,2692],{},[2588,2687,2688,2689],{},"The run fails on an import, for example on ",[892,2690,2691],{},"unitelabs.sdk",[2588,2693,2694,2695,2697,2698,2700],{},"On the platform path, check the workflow's ",[827,2696,2180],{}," list. On the template path, check ",[892,2699,2549],{}," and run the sync again.",[2573,2702,2703,2706],{},[2588,2704,2705],{},"The platform reports no function to start",[2588,2707,2708,2709,2711],{},"On the platform path, the started function has to be called ",[892,2710,1501],{},", matching the record the form created.",[2573,2713,2714,2717],{},[2588,2715,2716],{},"Slack rejects the message",[2588,2718,2719],{},"Check the webhook, the channel, and workspace restrictions on apps. Redact the webhook URL before sharing logs.",[2573,2721,2722,2727],{},[2588,2723,2724,2725],{},"The run never reaches ",[892,2726,2122],{},[2588,2728,2729],{},"Read the run's log. The notification step runs first, so a failure there stops the run before the confirmation request.",[2573,2731,2732,2735],{},[2588,2733,2734],{},"A Slack reply or reaction does nothing",[2588,2736,2737],{},"Expected. This workflow takes the confirmation in UniteLabs.",[2573,2739,2740,2743],{},[2588,2741,2742],{},"Several messages arrive",[2588,2744,2745],{},"Check whether the workflow was started more than once.",[858,2747,835],{"id":2748},"answer-from-slack-instead-of-the-platform",[820,2750,2751,2752,2757],{},"An incoming webhook only delivers a message. Somebody has to read the answer, and Slack pushes button clicks only to a server that listens on a public URL or holds a ",[832,2753,2756],{"href":2754,"rel":2755,"target":878},"https:\u002F\u002Fdocs.slack.dev\u002Fapis\u002Fevents-api\u002Fusing-socket-mode\u002F",[876,877],"Socket Mode"," connection. Emoji reactions need no such server: they are readable through the Web API, so the workflow posts the question and then checks its own message until an agreed reaction appears.",[820,2759,2760],{},"This replaces the notification step and incoming webhook; reading reactions needs the message timestamp, which a webhook does not return.",[1049,2762,2763,2844,2862,2869,2884,2910],{},[869,2764,2765,2766,2769,2770,2773,2774,2777,2778,2807],{},"Open the Slack app from Step 2 at ",[832,2767,1058],{"href":1056,"rel":2768,"target":878},[876,877]," and give it two more permissions, ",[892,2771,2772],{},"chat:write"," to post and ",[892,2775,2776],{},"reactions:read"," to see the reactions. There are two places to do this, and both end up in the same app configuration:",[866,2779,2780,2797],{},[869,2781,2782,2785,2786,2789,2790,2793,2794,880],{},[827,2783,2784],{},"OAuth & Permissions"," in the sidebar, under ",[827,2787,2788],{},"Features",". Scroll past the token boxes and the redirect URLs to the ",[827,2791,2792],{},"Scopes"," section, then add the two entries under ",[827,2795,2796],{},"Bot Token Scopes",[869,2798,2799,2802,2803,2806],{},[827,2800,2801],{},"App Manifest"," in the sidebar. Add the two entries to the existing list under ",[892,2804,2805],{},"oauth_config.scopes.bot"," and save. Do not replace the rest of the manifest, or the incoming webhook from Step 2 disappears with it.",[930,2808,2812],{"className":2809,"code":2810,"language":2811,"meta":935,"style":935},"language-yaml shiki shiki-themes github-light-high-contrast github-light-high-contrast github-dark-default","oauth_config:\n  scopes:\n    bot:\n      - incoming-webhook\n      - chat:write\n      - reactions:read\n","yaml",[892,2813,2814,2819,2824,2829,2834,2839],{"__ignoreMap":935},[939,2815,2816],{"class":941,"line":942},[939,2817,2818],{},"oauth_config:\n",[939,2820,2821],{"class":941,"line":956},[939,2822,2823],{},"  scopes:\n",[939,2825,2826],{"class":941,"line":1005},[939,2827,2828],{},"    bot:\n",[939,2830,2831],{"class":941,"line":1011},[939,2832,2833],{},"      - incoming-webhook\n",[939,2835,2836],{"class":941,"line":1294},[939,2837,2838],{},"      - chat:write\n",[939,2840,2841],{"class":941,"line":1351},[939,2842,2843],{},"      - reactions:read\n",[869,2845,2846,2847,2849,2850,2853,2854,2857,2858,2861],{},"Changing the permissions invalidates the installation, so install the app again. Slack shows a banner asking for it; the button sits at the top of ",[827,2848,2784],{}," and reads ",[827,2851,2852],{},"Reinstall to Workspace",". After that, the same page shows the ",[827,2855,2856],{},"Bot User OAuth Token",", which starts with ",[892,2859,2860],{},"xoxb-",". Copy it.",[869,2863,2864,2865,2868],{},"In your Slack channel, invite the bot with ",[892,2866,2867],{},"\u002Finvite @UniteLabs Notifier",". It has to post there, and posting requires membership. This has to be a channel, not a direct message.",[869,2870,2871,2872,2875,2876,2879,2880,2883],{},"In the platform, create a second secret named ",[892,2873,2874],{},"slack-bot-token"," of type ",[827,2877,2878],{},"Slack Credentials",", and paste the token into its ",[827,2881,2882],{},"token"," field.",[869,2885,2886,2887,2890,2891,2893,2894,2897,2898,2900,2901,2903,2904,2906,2907,880],{},"Add the package ",[892,2888,2889],{},"prefect-slack"," to the workflow, next to ",[892,2892,2187],{},". It holds the ",[892,2895,2896],{},"SlackCredentials"," class that reads the secret. On the platform path that means one more entry in the ",[827,2899,2180],{}," field of the workflow; on the template path one more line in ",[892,2902,2549],{},", followed by ",[892,2905,2600],{},". Without it the run fails while loading the code, with ",[892,2908,2909],{},"ModuleNotFoundError: No module named 'prefect_slack'",[869,2911,2912,2913,2916,2917,2919,2920,2922,2923,880],{},"Replace the notification step with the one below, and set ",[892,2914,2915],{},"CHANNEL"," to your channel. Keep the name of the last function as your path requires: ",[892,2918,1501],{}," on the platform path, or whatever ",[892,2921,2638],{}," names on the template path. The example below uses ",[892,2924,1501],{},[930,2926,2928],{"className":1256,"code":2927,"language":1258,"meta":935,"style":935},"import asyncio\n\nimport httpx\nfrom prefect_slack import SlackCredentials\nfrom unitelabs.sdk import get_logger, phase, step, workflow\n\nTOKEN_SECRET = \"slack-bot-token\"\nCHANNEL = \"#lab-notifications\"\nAPPROVE = {\"white_check_mark\", \"heavy_check_mark\"}\nREJECT = {\"x\", \"no_entry\"}\nPOLL_SECONDS = 10\nATTEMPTS = 60\n\n\n@step(name=\"Ask in Slack and wait\")\nasync def ask_in_slack(question: str) -> str:\n    credentials = await SlackCredentials.aload(TOKEN_SECRET)\n    token = credentials.token.get_secret_value()\n    logger = get_logger()\n\n    async with httpx.AsyncClient(\n        base_url=\"https:\u002F\u002Fslack.com\u002Fapi\",\n        headers={\"Authorization\": f\"Bearer {token}\"},\n    ) as slack:\n        posted = await slack.post(\n            \"\u002Fchat.postMessage\",\n            json={\n                \"channel\": CHANNEL,\n                \"text\": f\"{question}\\n\\nReact with :white_check_mark: to continue or :x: to abort.\",\n            },\n        )\n        posted.raise_for_status()\n        result = posted.json()\n        if not result.get(\"ok\"):\n            raise ValueError(f\"Slack rejected the message: {result.get('error')}\")\n        channel_id, message_ts = result[\"channel\"], result[\"ts\"]\n        logger.info(\"Question posted to Slack.\")\n\n        for _ in range(ATTEMPTS):\n            await asyncio.sleep(POLL_SECONDS)\n            read = await slack.get(\n                \"\u002Freactions.get\",\n                params={\"channel\": channel_id, \"timestamp\": message_ts},\n            )\n            read.raise_for_status()\n            body = read.json()\n            if not body.get(\"ok\"):\n                raise ValueError(f\"Slack rejected the read: {body.get('error')}\")\n            for reaction in body.get(\"message\", {}).get(\"reactions\", []):\n                name = reaction[\"name\"].split(\"::\")[0]\n                if name in REJECT:\n                    raise RuntimeError(f\"Aborted in Slack with :{name}:.\")\n                if name in APPROVE:\n                    logger.info(f\"Confirmed in Slack with :{name}: by {reaction['users']}.\")\n                    return name\n\n    raise TimeoutError(f\"No reaction within {POLL_SECONDS * ATTEMPTS} seconds.\")\n\n\n@phase(name=\"Request confirmation\")\nasync def request_confirmation() -> None:\n    await ask_in_slack(\"Slack reaction demo: a workflow is waiting for your go-ahead.\")\n\n\n@workflow(name=\"Slack reaction demo\")\nasync def entrypoint() -> None:\n    await request_confirmation()\n    get_logger().info(\"Confirmation received; workflow complete.\")\n",[892,2929,2930,2937,2941,2947,2959,2970,2974,2984,2993,3014,3033,3043,3053,3057,3061,3076,3096,3112,3122,3132,3136,3145,3158,3191,3201,3213,3220,3230,3241,3265,3271,3277,3283,3294,3312,3345,3367,3378,3383,3404,3417,3430,3438,3459,3465,3471,3482,3497,3527,3552,3580,3596,3623,3637,3675,3684,3689,3722,3727,3732,3747,3762,3775,3780,3785,3801,3816,3823],{"__ignoreMap":935},[939,2931,2932,2934],{"class":941,"line":942},[939,2933,1284],{"class":1276},[939,2935,2936],{"class":1280}," asyncio\n",[939,2938,2939],{"class":941,"line":956},[939,2940,1271],{"emptyLinePlaceholder":1270},[939,2942,2943,2945],{"class":941,"line":1005},[939,2944,1284],{"class":1276},[939,2946,1531],{"class":1280},[939,2948,2949,2951,2954,2956],{"class":941,"line":1011},[939,2950,1277],{"class":1276},[939,2952,2953],{"class":1280}," prefect_slack ",[939,2955,1284],{"class":1276},[939,2957,2958],{"class":1280}," SlackCredentials\n",[939,2960,2961,2963,2965,2967],{"class":941,"line":1294},[939,2962,1277],{"class":1276},[939,2964,1550],{"class":1280},[939,2966,1284],{"class":1276},[939,2968,2969],{"class":1280}," get_logger, phase, step, workflow\n",[939,2971,2972],{"class":941,"line":1351},[939,2973,1271],{"emptyLinePlaceholder":1270},[939,2975,2976,2979,2981],{"class":941,"line":1357},[939,2977,2978],{"class":959},"TOKEN_SECRET",[939,2980,1300],{"class":1276},[939,2982,2983],{"class":949}," \"slack-bot-token\"\n",[939,2985,2986,2988,2990],{"class":941,"line":1363},[939,2987,2915],{"class":959},[939,2989,1300],{"class":1276},[939,2991,2992],{"class":949}," \"#lab-notifications\"\n",[939,2994,2995,2998,3000,3003,3006,3008,3011],{"class":941,"line":1369},[939,2996,2997],{"class":959},"APPROVE",[939,2999,1300],{"class":1276},[939,3001,3002],{"class":1280}," {",[939,3004,3005],{"class":949},"\"white_check_mark\"",[939,3007,895],{"class":1280},[939,3009,3010],{"class":949},"\"heavy_check_mark\"",[939,3012,3013],{"class":1280},"}\n",[939,3015,3016,3019,3021,3023,3026,3028,3031],{"class":941,"line":1375},[939,3017,3018],{"class":959},"REJECT",[939,3020,1300],{"class":1276},[939,3022,3002],{"class":1280},[939,3024,3025],{"class":949},"\"x\"",[939,3027,895],{"class":1280},[939,3029,3030],{"class":949},"\"no_entry\"",[939,3032,3013],{"class":1280},[939,3034,3035,3038,3040],{"class":941,"line":1380},[939,3036,3037],{"class":959},"POLL_SECONDS",[939,3039,1300],{"class":1276},[939,3041,3042],{"class":959}," 10\n",[939,3044,3045,3048,3050],{"class":941,"line":1386},[939,3046,3047],{"class":959},"ATTEMPTS",[939,3049,1300],{"class":1276},[939,3051,3052],{"class":959}," 60\n",[939,3054,3055],{"class":941,"line":1392},[939,3056,1271],{"emptyLinePlaceholder":1270},[939,3058,3059],{"class":941,"line":1398},[939,3060,1271],{"emptyLinePlaceholder":1270},[939,3062,3063,3065,3067,3069,3071,3074],{"class":941,"line":1404},[939,3064,1583],{"class":1582},[939,3066,1586],{"class":1280},[939,3068,1589],{"class":945},[939,3070,1592],{"class":1276},[939,3072,3073],{"class":949},"\"Ask in Slack and wait\"",[939,3075,1309],{"class":1280},[939,3077,3078,3080,3082,3085,3088,3090,3092,3094],{"class":941,"line":1409},[939,3079,1602],{"class":1276},[939,3081,1605],{"class":1276},[939,3083,3084],{"class":1582}," ask_in_slack",[939,3086,3087],{"class":1280},"(question: ",[939,3089,1614],{"class":959},[939,3091,1617],{"class":1280},[939,3093,1614],{"class":959},[939,3095,1623],{"class":1280},[939,3097,3098,3101,3103,3105,3108,3110],{"class":941,"line":1415},[939,3099,3100],{"class":1280},"    credentials ",[939,3102,1592],{"class":1276},[939,3104,1633],{"class":1276},[939,3106,3107],{"class":1280}," SlackCredentials.aload(",[939,3109,2978],{"class":959},[939,3111,1309],{"class":1280},[939,3113,3114,3117,3119],{"class":941,"line":1421},[939,3115,3116],{"class":1280},"    token ",[939,3118,1592],{"class":1276},[939,3120,3121],{"class":1280}," credentials.token.get_secret_value()\n",[939,3123,3124,3127,3129],{"class":941,"line":1426},[939,3125,3126],{"class":1280},"    logger ",[939,3128,1592],{"class":1276},[939,3130,3131],{"class":1280}," get_logger()\n",[939,3133,3134],{"class":941,"line":1432},[939,3135,1271],{"emptyLinePlaceholder":1270},[939,3137,3138,3140,3142],{"class":941,"line":1438},[939,3139,1645],{"class":1276},[939,3141,1648],{"class":1276},[939,3143,3144],{"class":1280}," httpx.AsyncClient(\n",[939,3146,3147,3150,3152,3155],{"class":941,"line":1444},[939,3148,3149],{"class":945},"        base_url",[939,3151,1592],{"class":1276},[939,3153,3154],{"class":949},"\"https:\u002F\u002Fslack.com\u002Fapi\"",[939,3156,3157],{"class":1280},",\n",[939,3159,3160,3163,3165,3167,3170,3172,3175,3178,3180,3182,3185,3188],{"class":941,"line":1450},[939,3161,3162],{"class":945},"        headers",[939,3164,1592],{"class":1276},[939,3166,1677],{"class":1280},[939,3168,3169],{"class":949},"\"Authorization\"",[939,3171,1494],{"class":1280},[939,3173,3174],{"class":1276},"f",[939,3176,3177],{"class":949},"\"Bearer ",[939,3179,1677],{"class":1276},[939,3181,2882],{"class":1280},[939,3183,3184],{"class":1276},"}",[939,3186,3187],{"class":949},"\"",[939,3189,3190],{"class":1280},"},\n",[939,3192,3193,3196,3198],{"class":941,"line":1455},[939,3194,3195],{"class":1280},"    ) ",[939,3197,1654],{"class":1276},[939,3199,3200],{"class":1280}," slack:\n",[939,3202,3203,3206,3208,3210],{"class":941,"line":1461},[939,3204,3205],{"class":1280},"        posted ",[939,3207,1592],{"class":1276},[939,3209,1633],{"class":1276},[939,3211,3212],{"class":1280}," slack.post(\n",[939,3214,3215,3218],{"class":941,"line":1467},[939,3216,3217],{"class":949},"            \"\u002Fchat.postMessage\"",[939,3219,3157],{"class":1280},[939,3221,3222,3225,3227],{"class":941,"line":1472},[939,3223,3224],{"class":945},"            json",[939,3226,1592],{"class":1276},[939,3228,3229],{"class":1280},"{\n",[939,3231,3232,3235,3237,3239],{"class":941,"line":1478},[939,3233,3234],{"class":949},"                \"channel\"",[939,3236,1494],{"class":1280},[939,3238,2915],{"class":959},[939,3240,3157],{"class":1280},[939,3242,3243,3246,3248,3250,3252,3254,3257,3260,3263],{"class":941,"line":1484},[939,3244,3245],{"class":949},"                \"text\"",[939,3247,1494],{"class":1280},[939,3249,3174],{"class":1276},[939,3251,3187],{"class":949},[939,3253,1677],{"class":1276},[939,3255,3256],{"class":1280},"question",[939,3258,3259],{"class":1276},"}\\n\\n",[939,3261,3262],{"class":949},"React with :white_check_mark: to continue or :x: to abort.\"",[939,3264,3157],{"class":1280},[939,3266,3268],{"class":941,"line":3267},30,[939,3269,3270],{"class":1280},"            },\n",[939,3272,3274],{"class":941,"line":3273},31,[939,3275,3276],{"class":1280},"        )\n",[939,3278,3280],{"class":941,"line":3279},32,[939,3281,3282],{"class":1280},"        posted.raise_for_status()\n",[939,3284,3286,3289,3291],{"class":941,"line":3285},33,[939,3287,3288],{"class":1280},"        result ",[939,3290,1592],{"class":1276},[939,3292,3293],{"class":1280}," posted.json()\n",[939,3295,3297,3300,3303,3306,3309],{"class":941,"line":3296},34,[939,3298,3299],{"class":1276},"        if",[939,3301,3302],{"class":1276}," not",[939,3304,3305],{"class":1280}," result.get(",[939,3307,3308],{"class":949},"\"ok\"",[939,3310,3311],{"class":1280},"):\n",[939,3313,3315,3318,3321,3323,3325,3328,3330,3333,3336,3339,3341,3343],{"class":941,"line":3314},35,[939,3316,3317],{"class":1276},"            raise",[939,3319,3320],{"class":959}," ValueError",[939,3322,1586],{"class":1280},[939,3324,3174],{"class":1276},[939,3326,3327],{"class":949},"\"Slack rejected the message: ",[939,3329,1677],{"class":1276},[939,3331,3332],{"class":1280},"result.get(",[939,3334,3335],{"class":949},"'error'",[939,3337,3338],{"class":1280},")",[939,3340,3184],{"class":1276},[939,3342,3187],{"class":949},[939,3344,1309],{"class":1280},[939,3346,3348,3351,3353,3356,3359,3362,3365],{"class":941,"line":3347},36,[939,3349,3350],{"class":1280},"        channel_id, message_ts ",[939,3352,1592],{"class":1276},[939,3354,3355],{"class":1280}," result[",[939,3357,3358],{"class":949},"\"channel\"",[939,3360,3361],{"class":1280},"], result[",[939,3363,3364],{"class":949},"\"ts\"",[939,3366,1372],{"class":1280},[939,3368,3370,3373,3376],{"class":941,"line":3369},37,[939,3371,3372],{"class":1280},"        logger.info(",[939,3374,3375],{"class":949},"\"Question posted to Slack.\"",[939,3377,1309],{"class":1280},[939,3379,3381],{"class":941,"line":3380},38,[939,3382,1271],{"emptyLinePlaceholder":1270},[939,3384,3386,3389,3392,3395,3398,3400,3402],{"class":941,"line":3385},39,[939,3387,3388],{"class":1276},"        for",[939,3390,3391],{"class":1280}," _ ",[939,3393,3394],{"class":1276},"in",[939,3396,3397],{"class":959}," range",[939,3399,1586],{"class":1280},[939,3401,3047],{"class":959},[939,3403,3311],{"class":1280},[939,3405,3407,3410,3413,3415],{"class":941,"line":3406},40,[939,3408,3409],{"class":1276},"            await",[939,3411,3412],{"class":1280}," asyncio.sleep(",[939,3414,3037],{"class":959},[939,3416,1309],{"class":1280},[939,3418,3420,3423,3425,3427],{"class":941,"line":3419},41,[939,3421,3422],{"class":1280},"            read ",[939,3424,1592],{"class":1276},[939,3426,1633],{"class":1276},[939,3428,3429],{"class":1280}," slack.get(\n",[939,3431,3433,3436],{"class":941,"line":3432},42,[939,3434,3435],{"class":949},"                \"\u002Freactions.get\"",[939,3437,3157],{"class":1280},[939,3439,3441,3444,3446,3448,3450,3453,3456],{"class":941,"line":3440},43,[939,3442,3443],{"class":945},"                params",[939,3445,1592],{"class":1276},[939,3447,1677],{"class":1280},[939,3449,3358],{"class":949},[939,3451,3452],{"class":1280},": channel_id, ",[939,3454,3455],{"class":949},"\"timestamp\"",[939,3457,3458],{"class":1280},": message_ts},\n",[939,3460,3462],{"class":941,"line":3461},44,[939,3463,3464],{"class":1280},"            )\n",[939,3466,3468],{"class":941,"line":3467},45,[939,3469,3470],{"class":1280},"            read.raise_for_status()\n",[939,3472,3474,3477,3479],{"class":941,"line":3473},46,[939,3475,3476],{"class":1280},"            body ",[939,3478,1592],{"class":1276},[939,3480,3481],{"class":1280}," read.json()\n",[939,3483,3485,3488,3490,3493,3495],{"class":941,"line":3484},47,[939,3486,3487],{"class":1276},"            if",[939,3489,3302],{"class":1276},[939,3491,3492],{"class":1280}," body.get(",[939,3494,3308],{"class":949},[939,3496,3311],{"class":1280},[939,3498,3500,3503,3505,3507,3509,3512,3514,3517,3519,3521,3523,3525],{"class":941,"line":3499},48,[939,3501,3502],{"class":1276},"                raise",[939,3504,3320],{"class":959},[939,3506,1586],{"class":1280},[939,3508,3174],{"class":1276},[939,3510,3511],{"class":949},"\"Slack rejected the read: ",[939,3513,1677],{"class":1276},[939,3515,3516],{"class":1280},"body.get(",[939,3518,3335],{"class":949},[939,3520,3338],{"class":1280},[939,3522,3184],{"class":1276},[939,3524,3187],{"class":949},[939,3526,1309],{"class":1280},[939,3528,3530,3533,3536,3538,3540,3543,3546,3549],{"class":941,"line":3529},49,[939,3531,3532],{"class":1276},"            for",[939,3534,3535],{"class":1280}," reaction ",[939,3537,3394],{"class":1276},[939,3539,3492],{"class":1280},[939,3541,3542],{"class":949},"\"message\"",[939,3544,3545],{"class":1280},", {}).get(",[939,3547,3548],{"class":949},"\"reactions\"",[939,3550,3551],{"class":1280},", []):\n",[939,3553,3555,3558,3560,3563,3566,3569,3572,3575,3578],{"class":941,"line":3554},50,[939,3556,3557],{"class":1280},"                name ",[939,3559,1592],{"class":1276},[939,3561,3562],{"class":1280}," reaction[",[939,3564,3565],{"class":949},"\"name\"",[939,3567,3568],{"class":1280},"].split(",[939,3570,3571],{"class":949},"\"::\"",[939,3573,3574],{"class":1280},")[",[939,3576,3577],{"class":959},"0",[939,3579,1372],{"class":1280},[939,3581,3583,3586,3589,3591,3594],{"class":941,"line":3582},51,[939,3584,3585],{"class":1276},"                if",[939,3587,3588],{"class":1280}," name ",[939,3590,3394],{"class":1276},[939,3592,3593],{"class":959}," REJECT",[939,3595,1623],{"class":1280},[939,3597,3599,3602,3605,3607,3609,3612,3614,3616,3618,3621],{"class":941,"line":3598},52,[939,3600,3601],{"class":1276},"                    raise",[939,3603,3604],{"class":959}," RuntimeError",[939,3606,1586],{"class":1280},[939,3608,3174],{"class":1276},[939,3610,3611],{"class":949},"\"Aborted in Slack with :",[939,3613,1677],{"class":1276},[939,3615,1589],{"class":1280},[939,3617,3184],{"class":1276},[939,3619,3620],{"class":949},":.\"",[939,3622,1309],{"class":1280},[939,3624,3626,3628,3630,3632,3635],{"class":941,"line":3625},53,[939,3627,3585],{"class":1276},[939,3629,3588],{"class":1280},[939,3631,3394],{"class":1276},[939,3633,3634],{"class":959}," APPROVE",[939,3636,1623],{"class":1280},[939,3638,3640,3643,3645,3648,3650,3652,3654,3657,3659,3662,3665,3668,3670,3673],{"class":941,"line":3639},54,[939,3641,3642],{"class":1280},"                    logger.info(",[939,3644,3174],{"class":1276},[939,3646,3647],{"class":949},"\"Confirmed in Slack with :",[939,3649,1677],{"class":1276},[939,3651,1589],{"class":1280},[939,3653,3184],{"class":1276},[939,3655,3656],{"class":949},": by ",[939,3658,1677],{"class":1276},[939,3660,3661],{"class":1280},"reaction[",[939,3663,3664],{"class":949},"'users'",[939,3666,3667],{"class":1280},"]",[939,3669,3184],{"class":1276},[939,3671,3672],{"class":949},".\"",[939,3674,1309],{"class":1280},[939,3676,3678,3681],{"class":941,"line":3677},55,[939,3679,3680],{"class":1276},"                    return",[939,3682,3683],{"class":1280}," name\n",[939,3685,3687],{"class":941,"line":3686},56,[939,3688,1271],{"emptyLinePlaceholder":1270},[939,3690,3692,3695,3698,3700,3702,3705,3707,3709,3712,3715,3717,3720],{"class":941,"line":3691},57,[939,3693,3694],{"class":1276},"    raise",[939,3696,3697],{"class":959}," TimeoutError",[939,3699,1586],{"class":1280},[939,3701,3174],{"class":1276},[939,3703,3704],{"class":949},"\"No reaction within ",[939,3706,1677],{"class":1276},[939,3708,3037],{"class":959},[939,3710,3711],{"class":1276}," *",[939,3713,3714],{"class":959}," ATTEMPTS",[939,3716,3184],{"class":1276},[939,3718,3719],{"class":949}," seconds.\"",[939,3721,1309],{"class":1280},[939,3723,3725],{"class":941,"line":3724},58,[939,3726,1271],{"emptyLinePlaceholder":1270},[939,3728,3730],{"class":941,"line":3729},59,[939,3731,1271],{"emptyLinePlaceholder":1270},[939,3733,3735,3737,3739,3741,3743,3745],{"class":941,"line":3734},60,[939,3736,1711],{"class":1582},[939,3738,1586],{"class":1280},[939,3740,1589],{"class":945},[939,3742,1592],{"class":1276},[939,3744,1720],{"class":949},[939,3746,1309],{"class":1280},[939,3748,3750,3752,3754,3756,3758,3760],{"class":941,"line":3749},61,[939,3751,1602],{"class":1276},[939,3753,1605],{"class":1276},[939,3755,1731],{"class":1582},[939,3757,1734],{"class":1280},[939,3759,1620],{"class":959},[939,3761,1623],{"class":1280},[939,3763,3765,3767,3770,3773],{"class":941,"line":3764},62,[939,3766,1743],{"class":1276},[939,3768,3769],{"class":1280}," ask_in_slack(",[939,3771,3772],{"class":949},"\"Slack reaction demo: a workflow is waiting for your go-ahead.\"",[939,3774,1309],{"class":1280},[939,3776,3778],{"class":941,"line":3777},63,[939,3779,1271],{"emptyLinePlaceholder":1270},[939,3781,3783],{"class":941,"line":3782},64,[939,3784,1271],{"emptyLinePlaceholder":1270},[939,3786,3788,3790,3792,3794,3796,3799],{"class":941,"line":3787},65,[939,3789,1796],{"class":1582},[939,3791,1586],{"class":1280},[939,3793,1589],{"class":945},[939,3795,1592],{"class":1276},[939,3797,3798],{"class":949},"\"Slack reaction demo\"",[939,3800,1309],{"class":1280},[939,3802,3804,3806,3808,3810,3812,3814],{"class":941,"line":3803},66,[939,3805,1602],{"class":1276},[939,3807,1605],{"class":1276},[939,3809,2460],{"class":1582},[939,3811,1734],{"class":1280},[939,3813,1620],{"class":959},[939,3815,1623],{"class":1280},[939,3817,3819,3821],{"class":941,"line":3818},67,[939,3820,1743],{"class":1276},[939,3822,1829],{"class":1280},[939,3824,3826,3828,3830],{"class":941,"line":3825},68,[939,3827,1693],{"class":1280},[939,3829,1836],{"class":949},[939,3831,1309],{"class":1280},[820,3833,3834,3837,3838,880],{},[892,3835,3836],{},"chat.postMessage"," returns the channel ID and the message timestamp, and the two together address the message for ",[892,3839,3840],{},"reactions.get",[820,3842,3843,3844,3847,3848,3851,3852,3854,3855,3858,3859,3862,3863,3865],{},"Emoji names carry no colons. ✅ is ",[892,3845,3846],{},"white_check_mark",", and ",[892,3849,3850],{},"heavy_check_mark"," is the narrower ✔️, which is why ",[892,3853,2997],{}," holds both. For emoji with skin tones Slack appends ",[892,3856,3857],{},"::skin-tone-3",", which the ",[892,3860,3861],{},"split"," removes. A reaction from ",[892,3864,3018],{}," fails the step, so the run ends as failed.",[820,3867,3868,3869,880],{},"Ten minutes of checking every ten seconds is the timeout; a run that nobody answers fails with the ",[892,3870,3871],{},"TimeoutError",[820,3873,3874],{},"What this version gives up:",[866,3876,3877,3883,3894,3897],{},[869,3878,3879,3880,3882],{},"The run never pauses. The platform shows it as running instead of waiting for input, so there is no input form and the answer is not recorded with the run. Resource use is not the difference: in the SDK version checked here, ",[892,3881,1869],{}," registers the paused state on the platform and then waits in place, asking every few seconds whether the run has been resumed. Both variants therefore occupy their worker while they wait.",[869,3884,3885,3886,3889,3890,3893],{},"Anyone in the channel can react. The code logs the reaction's ",[892,3887,3888],{},"users"," but does not check whether they are allowed to answer, and Slack states that this list may be incomplete while ",[892,3891,3892],{},"count"," is not.",[869,3895,3896],{},"The answer arrives with the next check, up to ten seconds late.",[869,3898,3899],{},"Approval and rejection at the same time are not handled. Whichever reaction the loop sees first wins.",[820,3901,3902,3903,3907,3908,3911,3912,3914],{},"For buttons, forms, and typed answers, the person's action has to reach a listening service, which then resumes the paused run through the API. The platform side of that is described in ",[832,3904,3906],{"href":3905},"\u002Fautomate\u002Fguides\u002Fbasic-hitl\u002F#pattern-3-resume-via-api","HITL basics: Pattern 3, Resume via API",": send ",[892,3909,3910],{},"confirmed: true"," under the waiting phase ID from the run's current status. Other required fields have to match the schema described in ",[832,3913,574],{"href":575},". That service needs its own credentials and its own code for authorization, duplicate answers, stale requests, and API errors, and it has to run somewhere. It is a separate piece of software from the workflow.",[3916,3917,3918],"style",{},"html pre.shiki code .sNqAu, html code.shiki .sNqAu{--shiki-light:#702C00;--shiki-default:#702C00;--shiki-dark:#FFA657}html pre.shiki code .sCx38, html code.shiki .sCx38{--shiki-light:#032563;--shiki-default:#032563;--shiki-dark:#A5D6FF}html pre.shiki code .sOiAX, html code.shiki .sOiAX{--shiki-light:#023B95;--shiki-default:#023B95;--shiki-dark:#79C0FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sxkbv, html code.shiki .sxkbv{--shiki-light:#A0111F;--shiki-default:#A0111F;--shiki-dark:#FF7B72}html pre.shiki code .sc-9e, html code.shiki .sc-9e{--shiki-light:#0E1116;--shiki-default:#0E1116;--shiki-dark:#E6EDF3}html pre.shiki code .s4qFZ, html code.shiki .s4qFZ{--shiki-light:#622CBC;--shiki-default:#622CBC;--shiki-dark:#D2A8FF}",{"title":935,"searchDepth":942,"depth":956,"links":3920},[3921,3922,3923,3924,3925,3926,3927,3928,3929,3930,3931],{"id":860,"depth":956,"text":861},{"id":924,"depth":956,"text":925},{"id":1043,"depth":956,"text":1044},{"id":1112,"depth":956,"text":1113},{"id":1219,"depth":956,"text":1220},{"id":1919,"depth":956,"text":1920},{"id":2088,"depth":956,"text":2089},{"id":2152,"depth":956,"text":855},{"id":2514,"depth":956,"text":2515},{"id":2564,"depth":956,"text":2565},{"id":2748,"depth":956,"text":835},"Post a Slack message when a workflow needs a person, then confirm the request in the UniteLabs platform.","md",null,{},"\u002Fautomate\u002Fguides\u002Fslack-notifications",{"title":570,"description":3932},"J4sOHte9qd5EMrN9Q1CMUOEdEcrGDdvg6vAyD4xmsiU",[3940,3942],{"title":566,"path":567,"stem":568,"description":3941,"children":-1},"Pause a workflow run so an operator can take a physical action, then resume automatically.",{"title":574,"path":575,"stem":576,"description":3943,"children":-1},"Collect structured data from operators at runtime using a RunInput model — enums, booleans, numbers, and text fields rendered as a platform form.",1789144603543]