[{"data":1,"prerenderedAt":5050},["ShallowReactive",2],{"navigation_docs":3,"-docs-guides-flowdsl-to-redelay":968,"-docs-guides-flowdsl-to-redelay-surround":5045},[4,27,63,129,287,311,893,902,950,964],{"title":5,"path":6,"stem":7,"children":8,"order":10,"icon":11},"Overview","\u002Fdocs\u002Foverview","docs\u002F1.overview\u002Findex",[9,12,18,24],{"title":5,"path":6,"stem":7,"order":10,"icon":11},1,"i-lucide-info",{"title":13,"path":14,"stem":15,"order":16,"icon":17},"Getting Started","\u002Fdocs\u002Foverview\u002Fgetting-started","docs\u002F1.overview\u002F2.getting-started",2,"i-lucide-rocket",{"title":19,"path":20,"stem":21,"order":22,"icon":23},"Contributing","\u002Fdocs\u002Foverview\u002Fcontributing","docs\u002F1.overview\u002F3.contributing",3,"i-lucide-heart",{"title":5,"path":25,"stem":26,"order":10},"\u002Fdocs\u002Foverview\u002F_dir","docs\u002F1.overview\u002F_dir",{"title":28,"path":29,"stem":30,"children":31,"page":62},"Concepts","\u002Fdocs\u002Fconcepts","docs\u002F2.concepts",[32,37,42,47,53,59],{"title":33,"path":34,"stem":35,"order":10,"icon":36},"Events","\u002Fdocs\u002Fconcepts\u002Fevents","docs\u002F2.concepts\u002F1.events","i-lucide-zap",{"title":38,"path":39,"stem":40,"order":16,"icon":41},"Transports","\u002Fdocs\u002Fconcepts\u002Ftransports","docs\u002F2.concepts\u002F2.transports","i-lucide-network",{"title":43,"path":44,"stem":45,"order":22,"icon":46},"Module System","\u002Fdocs\u002Fconcepts\u002Fmodules","docs\u002F2.concepts\u002F3.modules","i-lucide-puzzle",{"title":48,"path":49,"stem":50,"order":51,"icon":52},"Flows","\u002Fdocs\u002Fconcepts\u002Fflows","docs\u002F2.concepts\u002F4.flows",4,"i-lucide-workflow",{"title":54,"path":55,"stem":56,"order":57,"icon":58},"Scheduler","\u002Fdocs\u002Fconcepts\u002Fscheduler","docs\u002F2.concepts\u002F5.scheduler",5,"i-lucide-clock",{"title":28,"path":60,"stem":61,"order":16},"\u002Fdocs\u002Fconcepts\u002F_dir","docs\u002F2.concepts\u002F_dir",false,{"title":64,"path":65,"stem":66,"children":67,"page":62},"Guides","\u002Fdocs\u002Fguides","docs\u002F3.guides",[68,73,77,81,85,89,93,98,104,110,116,122,126],{"title":69,"path":70,"stem":71,"order":10,"icon":72},"Creating a Go App","\u002Fdocs\u002Fguides\u002Fcreating-go-app","docs\u002F3.guides\u002F1.creating-go-app","i-lucide-terminal",{"title":74,"path":75,"stem":76},"E2E tests & demo videos","\u002Fdocs\u002Fguides\u002Fe2e-and-demo-videos","docs\u002F3.guides\u002F10.e2e-and-demo-videos",{"title":78,"path":79,"stem":80,"icon":17},"Deploying to production","\u002Fdocs\u002Fguides\u002Fdeploying-to-production","docs\u002F3.guides\u002F11.deploying-to-production",{"title":82,"path":83,"stem":84,"order":16,"icon":46},"Creating a Go Module","\u002Fdocs\u002Fguides\u002Fcreating-go-module","docs\u002F3.guides\u002F2.creating-go-module",{"title":86,"path":87,"stem":88,"order":22,"icon":36},"Adding Events","\u002Fdocs\u002Fguides\u002Fadding-events-to-go-module","docs\u002F3.guides\u002F3.adding-events-to-go-module",{"title":90,"path":91,"stem":92,"order":51,"icon":52},"FlowDSL to Service","\u002Fdocs\u002Fguides\u002Fflowdsl-to-redelay","docs\u002F3.guides\u002F4.flowdsl-to-redelay",{"title":94,"path":95,"stem":96,"order":57,"icon":97},"Choosing a Transport","\u002Fdocs\u002Fguides\u002Fchoosing-a-transport","docs\u002F3.guides\u002F5.choosing-a-transport","i-lucide-git-branch",{"title":99,"path":100,"stem":101,"order":102,"icon":103},"Module Browser (\u002Fmodules)","\u002Fdocs\u002Fguides\u002Fmodules-browser","docs\u002F3.guides\u002F6.modules-browser",6,"i-lucide-layout-dashboard",{"title":105,"path":106,"stem":107,"order":108,"icon":109},"Running Flows","\u002Fdocs\u002Fguides\u002Frunning-flows","docs\u002F3.guides\u002F7.running-flows",7,"i-lucide-play-circle",{"title":111,"path":112,"stem":113,"order":114,"icon":115},"Trace + Live Observability","\u002Fdocs\u002Fguides\u002Fobservability","docs\u002F3.guides\u002F8.observability",8,"i-lucide-activity",{"title":117,"path":118,"stem":119,"order":120,"icon":121},"Project AI Assistant","\u002Fdocs\u002Fguides\u002Fai-assistant","docs\u002F3.guides\u002F9.ai-assistant",9,"i-lucide-sparkles",{"title":123,"path":124,"stem":125},"Debug taps — trace live data through any node","\u002Fdocs\u002Fguides\u002Fdebug-taps","docs\u002F3.guides\u002F9.debug-taps",{"title":64,"path":127,"stem":128,"order":22},"\u002Fdocs\u002Fguides\u002F_dir","docs\u002F3.guides\u002F_dir",{"title":130,"path":131,"stem":132,"children":133,"page":62},"Reference","\u002Fdocs\u002Freference","docs\u002F4.reference",[134,139,143,148,153,158,163,168,172,177,182,187,193,199,205,210,215,219,225,230,236,242,247,253,259,263,267,273,278,284],{"title":135,"path":136,"stem":137,"order":10,"icon":138},"Go Framework","\u002Fdocs\u002Freference\u002Fgo-framework","docs\u002F4.reference\u002F01.go-framework","i-simple-icons-go",{"title":140,"path":141,"stem":142,"order":16,"icon":46},"Built-in Modules","\u002Fdocs\u002Freference\u002Fgo-modules","docs\u002F4.reference\u002F02.go-modules",{"title":144,"path":145,"stem":146,"order":22,"icon":147},"Domain Addon Modules","\u002Fdocs\u002Freference\u002Fgo-modules-addons","docs\u002F4.reference\u002F03.go-modules-addons","i-lucide-blocks",{"title":149,"path":150,"stem":151,"order":51,"icon":152},"Go Events","\u002Fdocs\u002Freference\u002Fgo-events","docs\u002F4.reference\u002F04.go-events","i-lucide-radio",{"title":154,"path":155,"stem":156,"order":57,"icon":157},"Email Module","\u002Fdocs\u002Freference\u002Fgo-module-email","docs\u002F4.reference\u002F05.go-module-email","i-lucide-mail",{"title":159,"path":160,"stem":161,"order":102,"icon":162},"Go Core & IR","\u002Fdocs\u002Freference\u002Fgo-core","docs\u002F4.reference\u002F06.go-core","i-lucide-cpu",{"title":164,"path":165,"stem":166,"order":108,"icon":167},"Module Definition Files","\u002Fdocs\u002Freference\u002Fgo-module-files","docs\u002F4.reference\u002F07.go-module-files","i-lucide-file-code",{"title":169,"path":170,"stem":171,"order":114,"icon":72},"CLI Reference","\u002Fdocs\u002Freference\u002Fgo-cli","docs\u002F4.reference\u002F08.go-cli",{"title":173,"path":174,"stem":175,"order":120,"icon":176},"Go Formats","\u002Fdocs\u002Freference\u002Fgo-formats","docs\u002F4.reference\u002F09.go-formats","i-lucide-file-json",{"title":178,"path":179,"stem":180,"order":181,"icon":152},"AsyncAPI Module","\u002Fdocs\u002Freference\u002Fgo-asyncapi","docs\u002F4.reference\u002F10.go-asyncapi",10,{"title":183,"path":184,"stem":185,"order":186,"icon":52},"Go FlowDSL","\u002Fdocs\u002Freference\u002Fgo-flowdsl","docs\u002F4.reference\u002F11.go-flowdsl",11,{"title":188,"path":189,"stem":190,"order":191,"icon":192},"FlowDSL Examples","\u002Fdocs\u002Freference\u002Fflowdsl-examples","docs\u002F4.reference\u002F12.flowdsl-examples",12,"i-lucide-file-code-2",{"title":194,"path":195,"stem":196,"order":197,"icon":198},"FlowDSL Sync","\u002Fdocs\u002Freference\u002Fflowdsl-sync","docs\u002F4.reference\u002F13.flowdsl-sync",13,"i-lucide-refresh-cw",{"title":200,"path":201,"stem":202,"order":203,"icon":204},"Node.js Framework","\u002Fdocs\u002Freference\u002Fjs-framework","docs\u002F4.reference\u002F14.js-framework",14,"i-simple-icons-nodedotjs",{"title":206,"path":207,"stem":208,"order":203,"icon":209},"Python Framework","\u002Fdocs\u002Freference\u002Fpy-framework","docs\u002F4.reference\u002F14.py-framework","i-simple-icons-python",{"title":211,"path":212,"stem":213,"order":214,"icon":204},"Node.js Events","\u002Fdocs\u002Freference\u002Fjs-events","docs\u002F4.reference\u002F15.js-events",15,{"title":216,"path":217,"stem":218,"order":214,"icon":209},"Python Events","\u002Fdocs\u002Freference\u002Fpy-events","docs\u002F4.reference\u002F15.py-events",{"title":220,"path":221,"stem":222,"order":223,"icon":224},"go-ai — LLM Providers, Guard, Ledger","\u002Fdocs\u002Freference\u002Fgo-ai","docs\u002F4.reference\u002F16.go-ai",16,"i-lucide-brain-circuit",{"title":226,"path":227,"stem":228,"order":229,"icon":41},"Infrastructure Modules","\u002Fdocs\u002Freference\u002Fgeneric-infra-modules","docs\u002F4.reference\u002F17.generic-infra-modules",17,{"title":231,"path":232,"stem":233,"order":234,"icon":235},"Flow Templates","\u002Fdocs\u002Freference\u002Fflow-templates","docs\u002F4.reference\u002F18.flow-templates",18,"i-lucide-library-big",{"title":237,"path":238,"stem":239,"order":240,"icon":241},"Assistant (chat + handoff)","\u002Fdocs\u002Freference\u002Fassistant","docs\u002F4.reference\u002F19.assistant",19,"i-lucide-messages-square",{"title":243,"path":244,"stem":245,"order":246,"icon":41},"Coordination","\u002Fdocs\u002Freference\u002Fcoordination","docs\u002F4.reference\u002F20.coordination",20,{"title":248,"path":249,"stem":250,"order":251,"icon":252},"Search (vector)","\u002Fdocs\u002Freference\u002Fsearch","docs\u002F4.reference\u002F21.search",21,"i-lucide-search",{"title":254,"path":255,"stem":256,"order":257,"icon":258},"Profiles","\u002Fdocs\u002Freference\u002Fprofiles","docs\u002F4.reference\u002F22.profiles",22,"i-lucide-bookmark",{"title":260,"path":261,"stem":262},"Flow-driven HTTP endpoints","\u002Fdocs\u002Freference\u002Fflow-driven-endpoints","docs\u002F4.reference\u002F23.flow-driven-endpoints",{"title":264,"path":265,"stem":266},"Deployments","\u002Fdocs\u002Freference\u002Fdeployments","docs\u002F4.reference\u002F24.deployments",{"title":268,"path":269,"stem":270,"order":271,"icon":272},"i18n (translations)","\u002Fdocs\u002Freference\u002Fi18n","docs\u002F4.reference\u002F25.i18n",25,"i-lucide-languages",{"title":274,"path":275,"stem":276,"order":277,"icon":152},"WebSockets","\u002Fdocs\u002Freference\u002Fwebsockets","docs\u002F4.reference\u002F26.websockets",26,{"title":279,"path":280,"stem":281,"order":282,"icon":283},"Localized content","\u002Fdocs\u002Freference\u002Flocalized-content","docs\u002F4.reference\u002F27.localized-content",27,"i-lucide-file-text",{"title":130,"path":285,"stem":286,"order":51},"\u002Fdocs\u002Freference\u002F_dir","docs\u002F4.reference\u002F_dir",{"title":288,"path":289,"stem":290,"children":291,"page":62},"Integrations","\u002Fdocs\u002Fintegrations","docs\u002F5.integrations",[292,296,300,304,308],{"title":293,"path":294,"stem":295,"order":10,"icon":52},"FlowDSL","\u002Fdocs\u002Fintegrations\u002Fflowdsl","docs\u002F5.integrations\u002F1.flowdsl",{"title":297,"path":298,"stem":299,"order":16,"icon":283},"OpenAPI","\u002Fdocs\u002Fintegrations\u002Fopenapi","docs\u002F5.integrations\u002F2.openapi",{"title":301,"path":302,"stem":303,"order":22,"icon":152},"AsyncAPI","\u002Fdocs\u002Fintegrations\u002Fasyncapi","docs\u002F5.integrations\u002F3.asyncapi",{"title":305,"path":306,"stem":307},"MCP Integration","\u002Fdocs\u002Fintegrations\u002Fmcp","docs\u002F5.integrations\u002F4.mcp",{"title":288,"path":309,"stem":310,"order":57},"\u002Fdocs\u002Fintegrations\u002F_dir","docs\u002F5.integrations\u002F_dir",{"title":312,"path":313,"stem":314,"children":315},"FlowDSL Node Catalog","\u002Fdocs\u002Fnodes","docs\u002F5.nodes\u002Findex",[316,317,321,325,329,333,337,341,345,349,353,357,361,365,369,373,377,381,385,389,393,397,401,405,409,413,417,421,425,429,433,437,441,445,449,453,457,461,465,469,473,477,481,485,489,493,497,501,505,509,513,517,521,525,529,533,537,541,545,549,553,557,561,565,569,573,577,581,585,589,593,597,601,605,609,613,617,621,625,629,633,637,641,645,649,653,657,661,665,669,673,677,681,685,689,693,697,701,705,709,713,717,721,725,729,733,737,741,745,749,753,757,761,765,769,773,777,781,785,789,793,797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865,869,873,877,881,885,889],{"title":312,"path":313,"stem":314},{"title":318,"path":319,"stem":320,"order":57},"Node Catalog","\u002Fdocs\u002Fnodes\u002F_dir","docs\u002F5.nodes\u002F_dir",{"title":322,"path":323,"stem":324},"Record Alert","\u002Fdocs\u002Fnodes\u002Falerts_record","docs\u002F5.nodes\u002Falerts_record",{"title":326,"path":327,"stem":328},"Log to API Log","\u002Fdocs\u002Fnodes\u002Fapilog_record","docs\u002F5.nodes\u002Fapilog_record",{"title":330,"path":331,"stem":332},"ClickHouse Exec","\u002Fdocs\u002Fnodes\u002Fclickhouse_exec","docs\u002F5.nodes\u002Fclickhouse_exec",{"title":334,"path":335,"stem":336},"ClickHouse Insert","\u002Fdocs\u002Fnodes\u002Fclickhouse_insert","docs\u002F5.nodes\u002Fclickhouse_insert",{"title":338,"path":339,"stem":340},"ClickHouse Query","\u002Fdocs\u002Fnodes\u002Fclickhouse_query","docs\u002F5.nodes\u002Fclickhouse_query",{"title":342,"path":343,"stem":344},"Array Op","\u002Fdocs\u002Fnodes\u002Fcore_array-ops","docs\u002F5.nodes\u002Fcore_array-ops",{"title":346,"path":347,"stem":348},"Assert","\u002Fdocs\u002Fnodes\u002Fcore_assert","docs\u002F5.nodes\u002Fcore_assert",{"title":350,"path":351,"stem":352},"Audit Emit","\u002Fdocs\u002Fnodes\u002Fcore_audit-emit","docs\u002F5.nodes\u002Fcore_audit-emit",{"title":354,"path":355,"stem":356},"Catch","\u002Fdocs\u002Fnodes\u002Fcore_catch","docs\u002F5.nodes\u002Fcore_catch",{"title":358,"path":359,"stem":360},"DateTime","\u002Fdocs\u002Fnodes\u002Fcore_datetime","docs\u002F5.nodes\u002Fcore_datetime",{"title":362,"path":363,"stem":364},"Dead Letter","\u002Fdocs\u002Fnodes\u002Fcore_dead-letter","docs\u002F5.nodes\u002Fcore_dead-letter",{"title":366,"path":367,"stem":368},"Delay","\u002Fdocs\u002Fnodes\u002Fcore_delay","docs\u002F5.nodes\u002Fcore_delay",{"title":370,"path":371,"stem":372},"ForEach","\u002Fdocs\u002Fnodes\u002Fcore_foreach","docs\u002F5.nodes\u002Fcore_foreach",{"title":374,"path":375,"stem":376},"Idempotency Key","\u002Fdocs\u002Fnodes\u002Fcore_idempotency-key","docs\u002F5.nodes\u002Fcore_idempotency-key",{"title":378,"path":379,"stem":380},"If","\u002Fdocs\u002Fnodes\u002Fcore_if","docs\u002F5.nodes\u002Fcore_if",{"title":382,"path":383,"stem":384},"Inspect","\u002Fdocs\u002Fnodes\u002Fcore_inspect","docs\u002F5.nodes\u002Fcore_inspect",{"title":386,"path":387,"stem":388},"JSON Merge","\u002Fdocs\u002Fnodes\u002Fcore_json-merge","docs\u002F5.nodes\u002Fcore_json-merge",{"title":390,"path":391,"stem":392},"JSON Schema Validate","\u002Fdocs\u002Fnodes\u002Fcore_json-schema-validate","docs\u002F5.nodes\u002Fcore_json-schema-validate",{"title":394,"path":395,"stem":396},"JSONPath Extract","\u002Fdocs\u002Fnodes\u002Fcore_jsonpath-extract","docs\u002F5.nodes\u002Fcore_jsonpath-extract",{"title":398,"path":399,"stem":400},"Log","\u002Fdocs\u002Fnodes\u002Fcore_log","docs\u002F5.nodes\u002Fcore_log",{"title":402,"path":403,"stem":404},"Math","\u002Fdocs\u002Fnodes\u002Fcore_math","docs\u002F5.nodes\u002Fcore_math",{"title":406,"path":407,"stem":408},"Merge","\u002Fdocs\u002Fnodes\u002Fcore_merge","docs\u002F5.nodes\u002Fcore_merge",{"title":410,"path":411,"stem":412},"Metric Counter","\u002Fdocs\u002Fnodes\u002Fcore_metric-counter","docs\u002F5.nodes\u002Fcore_metric-counter",{"title":414,"path":415,"stem":416},"Metric Gauge","\u002Fdocs\u002Fnodes\u002Fcore_metric-gauge","docs\u002F5.nodes\u002Fcore_metric-gauge",{"title":418,"path":419,"stem":420},"Mock","\u002Fdocs\u002Fnodes\u002Fcore_mock","docs\u002F5.nodes\u002Fcore_mock",{"title":422,"path":423,"stem":424},"No-op","\u002Fdocs\u002Fnodes\u002Fcore_noop","docs\u002F5.nodes\u002Fcore_noop",{"title":426,"path":427,"stem":428},"Object Omit","\u002Fdocs\u002Fnodes\u002Fcore_object-omit","docs\u002F5.nodes\u002Fcore_object-omit",{"title":430,"path":431,"stem":432},"Object Pick","\u002Fdocs\u002Fnodes\u002Fcore_object-pick","docs\u002F5.nodes\u002Fcore_object-pick",{"title":434,"path":435,"stem":436},"Parallel Fan-Out","\u002Fdocs\u002Fnodes\u002Fcore_parallel","docs\u002F5.nodes\u002Fcore_parallel",{"title":438,"path":439,"stem":440},"Raise","\u002Fdocs\u002Fnodes\u002Fcore_raise","docs\u002F5.nodes\u002Fcore_raise",{"title":442,"path":443,"stem":444},"Rate Limit Check","\u002Fdocs\u002Fnodes\u002Fcore_rate-limit-check","docs\u002F5.nodes\u002Fcore_rate-limit-check",{"title":446,"path":447,"stem":448},"Regex Extract","\u002Fdocs\u002Fnodes\u002Fcore_regex-extract","docs\u002F5.nodes\u002Fcore_regex-extract",{"title":450,"path":451,"stem":452},"Require Fields","\u002Fdocs\u002Fnodes\u002Fcore_require-fields","docs\u002F5.nodes\u002Fcore_require-fields",{"title":454,"path":455,"stem":456},"Rethrow","\u002Fdocs\u002Fnodes\u002Fcore_rethrow","docs\u002F5.nodes\u002Fcore_rethrow",{"title":458,"path":459,"stem":460},"Return","\u002Fdocs\u002Fnodes\u002Fcore_return","docs\u002F5.nodes\u002Fcore_return",{"title":462,"path":463,"stem":464},"HMAC Signature Verify","\u002Fdocs\u002Fnodes\u002Fcore_signature-verify","docs\u002F5.nodes\u002Fcore_signature-verify",{"title":466,"path":467,"stem":468},"String Op","\u002Fdocs\u002Fnodes\u002Fcore_string-ops","docs\u002F5.nodes\u002Fcore_string-ops",{"title":470,"path":471,"stem":472},"Subflow Invoke","\u002Fdocs\u002Fnodes\u002Fcore_subflow-invoke","docs\u002F5.nodes\u002Fcore_subflow-invoke",{"title":474,"path":475,"stem":476},"Switch","\u002Fdocs\u002Fnodes\u002Fcore_switch","docs\u002F5.nodes\u002Fcore_switch",{"title":478,"path":479,"stem":480},"Template Render","\u002Fdocs\u002Fnodes\u002Fcore_template-render","docs\u002F5.nodes\u002Fcore_template-render",{"title":482,"path":483,"stem":484},"Throttle","\u002Fdocs\u002Fnodes\u002Fcore_throttle","docs\u002F5.nodes\u002Fcore_throttle",{"title":486,"path":487,"stem":488},"Timeout","\u002Fdocs\u002Fnodes\u002Fcore_timeout","docs\u002F5.nodes\u002Fcore_timeout",{"title":490,"path":491,"stem":492},"Type Cast","\u002Fdocs\u002Fnodes\u002Fcore_type-cast","docs\u002F5.nodes\u002Fcore_type-cast",{"title":494,"path":495,"stem":496},"Webhook Respond","\u002Fdocs\u002Fnodes\u002Fcore_webhook-respond","docs\u002F5.nodes\u002Fcore_webhook-respond",{"title":498,"path":499,"stem":500},"File Delete","\u002Fdocs\u002Fnodes\u002Ffilesys_delete","docs\u002F5.nodes\u002Ffilesys_delete",{"title":502,"path":503,"stem":504},"Directory List","\u002Fdocs\u002Fnodes\u002Ffilesys_list","docs\u002F5.nodes\u002Ffilesys_list",{"title":506,"path":507,"stem":508},"File Read","\u002Fdocs\u002Fnodes\u002Ffilesys_read","docs\u002F5.nodes\u002Ffilesys_read",{"title":510,"path":511,"stem":512},"File Stat","\u002Fdocs\u002Fnodes\u002Ffilesys_stat","docs\u002F5.nodes\u002Ffilesys_stat",{"title":514,"path":515,"stem":516},"File Write","\u002Fdocs\u002Fnodes\u002Ffilesys_write","docs\u002F5.nodes\u002Ffilesys_write",{"title":518,"path":519,"stem":520},"FTP Delete","\u002Fdocs\u002Fnodes\u002Fftp_delete","docs\u002F5.nodes\u002Fftp_delete",{"title":522,"path":523,"stem":524},"FTP Download","\u002Fdocs\u002Fnodes\u002Fftp_download","docs\u002F5.nodes\u002Fftp_download",{"title":526,"path":527,"stem":528},"FTP List","\u002Fdocs\u002Fnodes\u002Fftp_list","docs\u002F5.nodes\u002Fftp_list",{"title":530,"path":531,"stem":532},"FTP Upload","\u002Fdocs\u002Fnodes\u002Fftp_upload","docs\u002F5.nodes\u002Fftp_upload",{"title":534,"path":535,"stem":536},"HTTP DELETE","\u002Fdocs\u002Fnodes\u002Fhttp_delete","docs\u002F5.nodes\u002Fhttp_delete",{"title":538,"path":539,"stem":540},"HTTP GET","\u002Fdocs\u002Fnodes\u002Fhttp_get","docs\u002F5.nodes\u002Fhttp_get",{"title":542,"path":543,"stem":544},"HTTP POST","\u002Fdocs\u002Fnodes\u002Fhttp_post","docs\u002F5.nodes\u002Fhttp_post",{"title":546,"path":547,"stem":548},"HTTP PUT","\u002Fdocs\u002Fnodes\u002Fhttp_put","docs\u002F5.nodes\u002Fhttp_put",{"title":550,"path":551,"stem":552},"HTTP Request","\u002Fdocs\u002Fnodes\u002Fhttp_request","docs\u002F5.nodes\u002Fhttp_request",{"title":554,"path":555,"stem":556},"Fill Translations","\u002Fdocs\u002Fnodes\u002Fi18n_fill-translations","docs\u002F5.nodes\u002Fi18n_fill-translations",{"title":558,"path":559,"stem":560},"Resolve Locale","\u002Fdocs\u002Fnodes\u002Fi18n_resolve-locale","docs\u002F5.nodes\u002Fi18n_resolve-locale",{"title":562,"path":563,"stem":564},"Translate","\u002Fdocs\u002Fnodes\u002Fi18n_translate","docs\u002F5.nodes\u002Fi18n_translate",{"title":566,"path":567,"stem":568},"Mongo AGGREGATE","\u002Fdocs\u002Fnodes\u002Fmongoops_aggregate","docs\u002F5.nodes\u002Fmongoops_aggregate",{"title":570,"path":571,"stem":572},"Mongo DELETE","\u002Fdocs\u002Fnodes\u002Fmongoops_delete","docs\u002F5.nodes\u002Fmongoops_delete",{"title":574,"path":575,"stem":576},"Mongo FIND","\u002Fdocs\u002Fnodes\u002Fmongoops_find","docs\u002F5.nodes\u002Fmongoops_find",{"title":578,"path":579,"stem":580},"Mongo FIND ONE","\u002Fdocs\u002Fnodes\u002Fmongoops_find-one","docs\u002F5.nodes\u002Fmongoops_find-one",{"title":582,"path":583,"stem":584},"Mongo INSERT","\u002Fdocs\u002Fnodes\u002Fmongoops_insert","docs\u002F5.nodes\u002Fmongoops_insert",{"title":586,"path":587,"stem":588},"Mongo UPDATE","\u002Fdocs\u002Fnodes\u002Fmongoops_update","docs\u002F5.nodes\u002Fmongoops_update",{"title":590,"path":591,"stem":592},"Postgres COPY FROM","\u002Fdocs\u002Fnodes\u002Fpostgres_copy-from","docs\u002F5.nodes\u002Fpostgres_copy-from",{"title":594,"path":595,"stem":596},"Postgres Exec","\u002Fdocs\u002Fnodes\u002Fpostgres_exec","docs\u002F5.nodes\u002Fpostgres_exec",{"title":598,"path":599,"stem":600},"Postgres Query","\u002Fdocs\u002Fnodes\u002Fpostgres_query","docs\u002F5.nodes\u002Fpostgres_query",{"title":602,"path":603,"stem":604},"Postgres Transaction","\u002Fdocs\u002Fnodes\u002Fpostgres_transaction","docs\u002F5.nodes\u002Fpostgres_transaction",{"title":606,"path":607,"stem":608},"RabbitMQ Consume","\u002Fdocs\u002Fnodes\u002Frabbitmq_consume","docs\u002F5.nodes\u002Frabbitmq_consume",{"title":610,"path":611,"stem":612},"RabbitMQ Declare","\u002Fdocs\u002Fnodes\u002Frabbitmq_declare","docs\u002F5.nodes\u002Frabbitmq_declare",{"title":614,"path":615,"stem":616},"RabbitMQ Publish","\u002Fdocs\u002Fnodes\u002Frabbitmq_publish","docs\u002F5.nodes\u002Frabbitmq_publish",{"title":618,"path":619,"stem":620},"Auth Login","\u002Fdocs\u002Fnodes\u002Fredelay_auth-login","docs\u002F5.nodes\u002Fredelay_auth-login",{"title":622,"path":623,"stem":624},"Require Permission","\u002Fdocs\u002Fnodes\u002Fredelay_auth-require-permission","docs\u002F5.nodes\u002Fredelay_auth-require-permission",{"title":626,"path":627,"stem":628},"Validate Token","\u002Fdocs\u002Fnodes\u002Fredelay_auth-validate-token","docs\u002F5.nodes\u002Fredelay_auth-validate-token",{"title":630,"path":631,"stem":632},"Render Email Template","\u002Fdocs\u002Fnodes\u002Fredelay_email-render-template","docs\u002F5.nodes\u002Fredelay_email-render-template",{"title":634,"path":635,"stem":636},"Send Email","\u002Fdocs\u002Fnodes\u002Fredelay_email-send","docs\u002F5.nodes\u002Fredelay_email-send",{"title":638,"path":639,"stem":640},"Send Templated Email","\u002Fdocs\u002Fnodes\u002Fredelay_email-send-templated","docs\u002F5.nodes\u002Fredelay_email-send-templated",{"title":642,"path":643,"stem":644},"Event Source","\u002Fdocs\u002Fnodes\u002Fredelay_event-source","docs\u002F5.nodes\u002Fredelay_event-source",{"title":646,"path":647,"stem":648},"Create Group","\u002Fdocs\u002Fnodes\u002Fredelay_groups-create-group","docs\u002F5.nodes\u002Fredelay_groups-create-group",{"title":650,"path":651,"stem":652},"Delete Group","\u002Fdocs\u002Fnodes\u002Fredelay_groups-delete-group","docs\u002F5.nodes\u002Fredelay_groups-delete-group",{"title":654,"path":655,"stem":656},"Resolve Permissions","\u002Fdocs\u002Fnodes\u002Fredelay_groups-resolve-permissions","docs\u002F5.nodes\u002Fredelay_groups-resolve-permissions",{"title":658,"path":659,"stem":660},"Update Group","\u002Fdocs\u002Fnodes\u002Fredelay_groups-update-group","docs\u002F5.nodes\u002Fredelay_groups-update-group",{"title":662,"path":663,"stem":664},"HTTP Endpoint","\u002Fdocs\u002Fnodes\u002Fredelay_http-endpoint","docs\u002F5.nodes\u002Fredelay_http-endpoint",{"title":666,"path":667,"stem":668},"JSON Stream Source","\u002Fdocs\u002Fnodes\u002Fredelay_json-stream-source","docs\u002F5.nodes\u002Fredelay_json-stream-source",{"title":670,"path":671,"stem":672},"JSON → Event","\u002Fdocs\u002Fnodes\u002Fredelay_json-to-event","docs\u002F5.nodes\u002Fredelay_json-to-event",{"title":674,"path":675,"stem":676},"Kafka Produce","\u002Fdocs\u002Fnodes\u002Fredelay_kafka-produce","docs\u002F5.nodes\u002Fredelay_kafka-produce",{"title":678,"path":679,"stem":680},"Create Short Link","\u002Fdocs\u002Fnodes\u002Fredelay_links-create","docs\u002F5.nodes\u002Fredelay_links-create",{"title":682,"path":683,"stem":684},"Delete Short Link","\u002Fdocs\u002Fnodes\u002Fredelay_links-delete","docs\u002F5.nodes\u002Fredelay_links-delete",{"title":686,"path":687,"stem":688},"Resolve Short Link","\u002Fdocs\u002Fnodes\u002Fredelay_links-resolve","docs\u002F5.nodes\u002Fredelay_links-resolve",{"title":690,"path":691,"stem":692},"LLM Chat","\u002Fdocs\u002Fnodes\u002Fredelay_llm-chat","docs\u002F5.nodes\u002Fredelay_llm-chat",{"title":694,"path":695,"stem":696},"LLM Embed","\u002Fdocs\u002Fnodes\u002Fredelay_llm-embed","docs\u002F5.nodes\u002Fredelay_llm-embed",{"title":698,"path":699,"stem":700},"LLM Guard","\u002Fdocs\u002Fnodes\u002Fredelay_llm-guard","docs\u002F5.nodes\u002Fredelay_llm-guard",{"title":702,"path":703,"stem":704},"LLM Tool Router","\u002Fdocs\u002Fnodes\u002Fredelay_llm-tool-router","docs\u002F5.nodes\u002Fredelay_llm-tool-router",{"title":706,"path":707,"stem":708},"NATS Publish","\u002Fdocs\u002Fnodes\u002Fredelay_nats-publish","docs\u002F5.nodes\u002Fredelay_nats-publish",{"title":710,"path":711,"stem":712},"Mark Notification Read","\u002Fdocs\u002Fnodes\u002Fredelay_notifications-mark-read","docs\u002F5.nodes\u002Fredelay_notifications-mark-read",{"title":714,"path":715,"stem":716},"Send Notification","\u002Fdocs\u002Fnodes\u002Fredelay_notifications-send","docs\u002F5.nodes\u002Fredelay_notifications-send",{"title":718,"path":719,"stem":720},"Redis Stream XADD","\u002Fdocs\u002Fnodes\u002Fredelay_redis-stream-publish","docs\u002F5.nodes\u002Fredelay_redis-stream-publish",{"title":722,"path":723,"stem":724},"S3 Copy Object","\u002Fdocs\u002Fnodes\u002Fredelay_s3-copy-object","docs\u002F5.nodes\u002Fredelay_s3-copy-object",{"title":726,"path":727,"stem":728},"S3 Delete Many","\u002Fdocs\u002Fnodes\u002Fredelay_s3-delete-many","docs\u002F5.nodes\u002Fredelay_s3-delete-many",{"title":730,"path":731,"stem":732},"S3 Head Object","\u002Fdocs\u002Fnodes\u002Fredelay_s3-head-object","docs\u002F5.nodes\u002Fredelay_s3-head-object",{"title":734,"path":735,"stem":736},"S3 List Objects","\u002Fdocs\u002Fnodes\u002Fredelay_s3-list-objects","docs\u002F5.nodes\u002Fredelay_s3-list-objects",{"title":738,"path":739,"stem":740},"Create Schedule","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-create-schedule","docs\u002F5.nodes\u002Fredelay_scheduler-create-schedule",{"title":742,"path":743,"stem":744},"Cron Trigger","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-cron-trigger","docs\u002F5.nodes\u002Fredelay_scheduler-cron-trigger",{"title":746,"path":747,"stem":748},"Delete Schedule","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-delete-schedule","docs\u002F5.nodes\u002Fredelay_scheduler-delete-schedule",{"title":750,"path":751,"stem":752},"Interval Trigger","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-interval-trigger","docs\u002F5.nodes\u002Fredelay_scheduler-interval-trigger",{"title":754,"path":755,"stem":756},"Search Query","\u002Fdocs\u002Fnodes\u002Fredelay_search-query","docs\u002F5.nodes\u002Fredelay_search-query",{"title":758,"path":759,"stem":760},"Search Upsert","\u002Fdocs\u002Fnodes\u002Fredelay_search-upsert","docs\u002F5.nodes\u002Fredelay_search-upsert",{"title":762,"path":763,"stem":764},"Delete Setting","\u002Fdocs\u002Fnodes\u002Fredelay_settings-delete","docs\u002F5.nodes\u002Fredelay_settings-delete",{"title":766,"path":767,"stem":768},"Get Setting","\u002Fdocs\u002Fnodes\u002Fredelay_settings-get","docs\u002F5.nodes\u002Fredelay_settings-get",{"title":770,"path":771,"stem":772},"Set Setting","\u002Fdocs\u002Fnodes\u002Fredelay_settings-set","docs\u002F5.nodes\u002Fredelay_settings-set",{"title":774,"path":775,"stem":776},"Delete File","\u002Fdocs\u002Fnodes\u002Fredelay_storage-delete","docs\u002F5.nodes\u002Fredelay_storage-delete",{"title":778,"path":779,"stem":780},"Download File","\u002Fdocs\u002Fnodes\u002Fredelay_storage-download","docs\u002F5.nodes\u002Fredelay_storage-download",{"title":782,"path":783,"stem":784},"Generate Presigned URL","\u002Fdocs\u002Fnodes\u002Fredelay_storage-presign-url","docs\u002F5.nodes\u002Fredelay_storage-presign-url",{"title":786,"path":787,"stem":788},"Upload File","\u002Fdocs\u002Fnodes\u002Fredelay_storage-upload","docs\u002F5.nodes\u002Fredelay_storage-upload",{"title":790,"path":791,"stem":792},"Subworkflow","\u002Fdocs\u002Fnodes\u002Fredelay_subworkflow","docs\u002F5.nodes\u002Fredelay_subworkflow",{"title":794,"path":795,"stem":796},"Activate User","\u002Fdocs\u002Fnodes\u002Fredelay_users-activate","docs\u002F5.nodes\u002Fredelay_users-activate",{"title":798,"path":799,"stem":800},"Create Pending User","\u002Fdocs\u002Fnodes\u002Fredelay_users-create-pending","docs\u002F5.nodes\u002Fredelay_users-create-pending",{"title":802,"path":803,"stem":804},"Create User","\u002Fdocs\u002Fnodes\u002Fredelay_users-create-user","docs\u002F5.nodes\u002Fredelay_users-create-user",{"title":806,"path":807,"stem":808},"Delete User","\u002Fdocs\u002Fnodes\u002Fredelay_users-delete-user","docs\u002F5.nodes\u002Fredelay_users-delete-user",{"title":810,"path":811,"stem":812},"Find User By Email","\u002Fdocs\u002Fnodes\u002Fredelay_users-find-by-email","docs\u002F5.nodes\u002Fredelay_users-find-by-email",{"title":814,"path":815,"stem":816},"Get User","\u002Fdocs\u002Fnodes\u002Fredelay_users-get-user","docs\u002F5.nodes\u002Fredelay_users-get-user",{"title":818,"path":819,"stem":820},"List Users","\u002Fdocs\u002Fnodes\u002Fredelay_users-list-users","docs\u002F5.nodes\u002Fredelay_users-list-users",{"title":822,"path":823,"stem":824},"Update User","\u002Fdocs\u002Fnodes\u002Fredelay_users-update-user","docs\u002F5.nodes\u002Fredelay_users-update-user",{"title":826,"path":827,"stem":828},"Request Email Verification (legacy)","\u002Fdocs\u002Fnodes\u002Fredelay_verification-request","docs\u002F5.nodes\u002Fredelay_verification-request",{"title":830,"path":831,"stem":832},"Request Email Verification","\u002Fdocs\u002Fnodes\u002Fredelay_verification-request-email","docs\u002F5.nodes\u002Fredelay_verification-request-email",{"title":834,"path":835,"stem":836},"Request SMS Verification","\u002Fdocs\u002Fnodes\u002Fredelay_verification-request-sms","docs\u002F5.nodes\u002Fredelay_verification-request-sms",{"title":838,"path":839,"stem":840},"Verify Token","\u002Fdocs\u002Fnodes\u002Fredelay_verification-verify","docs\u002F5.nodes\u002Fredelay_verification-verify",{"title":842,"path":843,"stem":844},"Redis DEL","\u002Fdocs\u002Fnodes\u002Fredisops_del","docs\u002F5.nodes\u002Fredisops_del",{"title":846,"path":847,"stem":848},"Redis EXPIRE","\u002Fdocs\u002Fnodes\u002Fredisops_expire","docs\u002F5.nodes\u002Fredisops_expire",{"title":850,"path":851,"stem":852},"Redis GET","\u002Fdocs\u002Fnodes\u002Fredisops_get","docs\u002F5.nodes\u002Fredisops_get",{"title":854,"path":855,"stem":856},"Redis HGET","\u002Fdocs\u002Fnodes\u002Fredisops_hget","docs\u002F5.nodes\u002Fredisops_hget",{"title":858,"path":859,"stem":860},"Redis HSET","\u002Fdocs\u002Fnodes\u002Fredisops_hset","docs\u002F5.nodes\u002Fredisops_hset",{"title":862,"path":863,"stem":864},"Redis INCR","\u002Fdocs\u002Fnodes\u002Fredisops_incr","docs\u002F5.nodes\u002Fredisops_incr",{"title":866,"path":867,"stem":868},"Redis LPOP","\u002Fdocs\u002Fnodes\u002Fredisops_lpop","docs\u002F5.nodes\u002Fredisops_lpop",{"title":870,"path":871,"stem":872},"Redis LPUSH","\u002Fdocs\u002Fnodes\u002Fredisops_lpush","docs\u002F5.nodes\u002Fredisops_lpush",{"title":874,"path":875,"stem":876},"Redis PUBLISH","\u002Fdocs\u002Fnodes\u002Fredisops_publish","docs\u002F5.nodes\u002Fredisops_publish",{"title":878,"path":879,"stem":880},"Redis SET","\u002Fdocs\u002Fnodes\u002Fredisops_set","docs\u002F5.nodes\u002Fredisops_set",{"title":882,"path":883,"stem":884},"SFTP Download","\u002Fdocs\u002Fnodes\u002Fsftp_download","docs\u002F5.nodes\u002Fsftp_download",{"title":886,"path":887,"stem":888},"SFTP Upload","\u002Fdocs\u002Fnodes\u002Fsftp_upload","docs\u002F5.nodes\u002Fsftp_upload",{"title":890,"path":891,"stem":892},"Send SMS","\u002Fdocs\u002Fnodes\u002Fsms_send","docs\u002F5.nodes\u002Fsms_send",{"title":894,"path":895,"stem":896,"children":897,"page":62},"Rfcs","\u002Fdocs\u002Frfcs","docs\u002F6.rfcs",[898],{"title":899,"path":900,"stem":901},"RFC 0001 — Worker Assignment for Flow Deployments","\u002Fdocs\u002Frfcs\u002F0001-worker-assignment","docs\u002F6.rfcs\u002F0001-worker-assignment",{"title":903,"path":904,"stem":905,"children":906,"page":62},"Admin","\u002Fdocs\u002Fadmin","docs\u002F7.admin",[907,912,917,922,927,932,937,941,946],{"title":5,"path":908,"stem":909,"order":910,"icon":911},"\u002Fdocs\u002Fadmin\u002Foverview","docs\u002F7.admin\u002F00.overview",0,"i-lucide-compass",{"title":913,"path":914,"stem":915,"order":10,"icon":916},"Getting started","\u002Fdocs\u002Fadmin\u002Fgetting-started","docs\u002F7.admin\u002F01.getting-started","i-lucide-play",{"title":918,"path":919,"stem":920,"order":16,"icon":921},"Layered architecture","\u002Fdocs\u002Fadmin\u002Flayered-architecture","docs\u002F7.admin\u002F02.layered-architecture","i-lucide-layers",{"title":923,"path":924,"stem":925,"order":22,"icon":926},"Building a custom admin project","\u002Fdocs\u002Fadmin\u002Fbuilding-a-custom-admin-project","docs\u002F7.admin\u002F03.building-a-custom-admin-project","i-lucide-folder-plus",{"title":928,"path":929,"stem":930,"order":51,"icon":931},"Building admin modules","\u002Fdocs\u002Fadmin\u002Fbuilding-admin-modules","docs\u002F7.admin\u002F04.building-admin-modules","i-lucide-package-plus",{"title":933,"path":934,"stem":935,"order":57,"icon":936},"Base module reference","\u002Fdocs\u002Fadmin\u002Freference-base-modules","docs\u002F7.admin\u002F05.reference-base-modules","i-lucide-boxes",{"title":938,"path":939,"stem":940,"order":102,"icon":17},"Deployment","\u002Fdocs\u002Fadmin\u002Fdeployment","docs\u002F7.admin\u002F06.deployment",{"title":942,"path":943,"stem":944,"order":108,"icon":945},"Consuming the base layer (versioned)","\u002Fdocs\u002Fadmin\u002Fconsuming-the-base-layer","docs\u002F7.admin\u002F07.consuming-the-base-layer","i-lucide-package",{"title":947,"path":948,"stem":949,"order":108},"Admin UI","\u002Fdocs\u002Fadmin\u002F_dir","docs\u002F7.admin\u002F_dir",{"title":951,"path":952,"stem":953,"children":954,"page":62},"Services","\u002Fdocs\u002Fservices","docs\u002F8.services",[955,960],{"title":956,"path":957,"stem":958,"order":10,"icon":959},"Custom development & support services","\u002Fdocs\u002Fservices\u002Fcustom-development","docs\u002F8.services\u002F1.custom-development","i-lucide-briefcase",{"title":951,"path":961,"stem":962,"icon":959,"order":963},"\u002Fdocs\u002Fservices\u002F_dir","docs\u002F8.services\u002F_dir",80,{"title":965,"path":966,"stem":967,"order":910},"Dir","\u002Fdocs\u002F_dir","docs\u002F_dir",{"id":969,"title":90,"body":970,"description":5038,"extension":5039,"links":5040,"meta":5041,"navigation":5042,"path":91,"seo":5043,"stem":92,"__hash__":5044},"docs\u002Fdocs\u002F3.guides\u002F4.flowdsl-to-redelay.md",{"type":971,"value":972,"toc":5009},"minimark",[973,985,993,996,1001,1004,1055,1060,1205,1213,1294,1296,1300,1311,2685,2687,2691,2729,2743,2745,2749,2752,2900,2909,2911,2915,2922,2926,2934,2938,3089,3093,3191,3199,3205,3459,3467,3472,3609,3617,3622,3802,3810,3816,4020,4028,4033,4156,4158,4162,4470,4472,4476,4627,4682,4685,4739,4741,4745,4748,4947,4949,4953,5005],[974,975,976,977,984],"p",{},"This guide takes you from a FlowDSL diagram — as seen on ",[978,979,983],"a",{"href":980,"rel":981},"https:\u002F\u002Fflowdsl.com\u002Fexamples",[982],"nofollow","flowdsl.com\u002Fexamples"," — through every step to a running Redelay service with typed, testable node implementations.",[986,987,988,992],"callout",{"icon":921},[989,990,991],"strong",{},"What you'll build:"," A webhook alert pipeline — ingests HTTP webhooks, filters by severity, enriches the payload, then dispatches alerts to PagerDuty and Slack.",[994,995],"hr",{},[997,998,1000],"h2",{"id":999},"_1-understand-the-flowdsl-vocabulary","1. Understand the FlowDSL vocabulary",[974,1002,1003],{},"A FlowDSL document has three sections:",[1005,1006,1007,1020],"table",{},[1008,1009,1010],"thead",{},[1011,1012,1013,1017],"tr",{},[1014,1015,1016],"th",{},"Section",[1014,1018,1019],{},"What it declares",[1021,1022,1023,1035,1045],"tbody",{},[1011,1024,1025,1032],{},[1026,1027,1028],"td",{},[1029,1030,1031],"code",{},"nodes",[1026,1033,1034],{},"Business logic units — each maps to one handler function",[1011,1036,1037,1042],{},[1026,1038,1039],{},[1029,1040,1041],{},"edges",[1026,1043,1044],{},"How packets flow between nodes — transport, durability, retries",[1011,1046,1047,1052],{},[1026,1048,1049],{},[1029,1050,1051],{},"components.packets",[1026,1053,1054],{},"Typed schemas for the data on each edge",[1056,1057,1059],"h3",{"id":1058},"node-kinds","Node kinds",[1005,1061,1062,1075],{},[1008,1063,1064],{},[1011,1065,1066,1069,1072],{},[1014,1067,1068],{},"Kind",[1014,1070,1071],{},"Role in Redelay",[1014,1073,1074],{},"Go pattern",[1021,1076,1077,1093,1110,1123,1136,1149,1162,1179,1192],{},[1011,1078,1079,1084,1087],{},[1026,1080,1081],{},[1029,1082,1083],{},"source",[1026,1085,1086],{},"Entry point — no inputs",[1026,1088,1089,1090],{},"HTTP handler \u002F Kafka consumer that publishes a ",[1029,1091,1092],{},"typed.EventDefinition",[1011,1094,1095,1100,1103],{},[1026,1096,1097],{},[1029,1098,1099],{},"transform",[1026,1101,1102],{},"Maps one packet type to another",[1026,1104,1105,1106,1109],{},"Pure function: ",[1029,1107,1108],{},"input → output",", no side effects",[1011,1111,1112,1117,1120],{},[1026,1113,1114],{},[1029,1115,1116],{},"router",[1026,1118,1119],{},"Routes to one of several outputs based on content",[1026,1121,1122],{},"Switch\u002Fmap on a field value, returns the port name",[1011,1124,1125,1130,1133],{},[1026,1126,1127],{},[1029,1128,1129],{},"llm",[1026,1131,1132],{},"Calls a language model",[1026,1134,1135],{},"HTTP call to OpenAI\u002FAnthropic + JSON extraction",[1011,1137,1138,1143,1146],{},[1026,1139,1140],{},[1029,1141,1142],{},"action",[1026,1144,1145],{},"Side effect in an external system",[1026,1147,1148],{},"Calls external API, sends message, creates ticket",[1011,1150,1151,1156,1159],{},[1026,1152,1153],{},[1029,1154,1155],{},"terminal",[1026,1157,1158],{},"End of path — no outputs",[1026,1160,1161],{},"Archive to DB, emit audit event, discard",[1011,1163,1164,1169,1172],{},[1026,1165,1166],{},[1029,1167,1168],{},"publish",[1026,1170,1171],{},"Emits to the event bus",[1026,1173,1174,1175,1178],{},"Calls ",[1029,1176,1177],{},"eventBus.Publish()"," with a typed message",[1011,1180,1181,1186,1189],{},[1026,1182,1183],{},[1029,1184,1185],{},"checkpoint",[1026,1187,1188],{},"Saves pipeline state and passes through",[1026,1190,1191],{},"Upsert to MongoDB before yielding",[1011,1193,1194,1199,1202],{},[1026,1195,1196],{},[1029,1197,1198],{},"integration",[1026,1200,1201],{},"Bridges to another FlowDSL flow",[1026,1203,1204],{},"Calls the other flow's source node via the runtime API",[1056,1206,1208,1209,1212],{"id":1207},"delivery-modes-edge-mode","Delivery modes (edge ",[1029,1210,1211],{},"mode",")",[1005,1214,1215,1228],{},[1008,1216,1217],{},[1011,1218,1219,1222,1225],{},[1014,1220,1221],{},"Mode",[1014,1223,1224],{},"Transport",[1014,1226,1227],{},"When to use",[1021,1229,1230,1243,1256,1268,1281],{},[1011,1231,1232,1237,1240],{},[1026,1233,1234],{},[1029,1235,1236],{},"direct",[1026,1238,1239],{},"In-process, synchronous",[1026,1241,1242],{},"Cheap deterministic transforms — field extraction, validation",[1011,1244,1245,1250,1253],{},[1026,1246,1247],{},[1029,1248,1249],{},"ephemeral",[1026,1251,1252],{},"Redis Streams \u002F NATS",[1026,1254,1255],{},"Burst absorption, worker pool smoothing",[1011,1257,1258,1262,1265],{},[1026,1259,1260],{},[1029,1261,1185],{},[1026,1263,1264],{},"MongoDB per stage",[1026,1266,1267],{},"Long ETL pipelines — resume from last successful stage",[1011,1269,1270,1275,1278],{},[1026,1271,1272],{},[1029,1273,1274],{},"durable",[1026,1276,1277],{},"MongoDB packet store",[1026,1279,1280],{},"Business-critical — payments, email\u002FSMS sends, LLM calls",[1011,1282,1283,1288,1291],{},[1026,1284,1285],{},[1029,1286,1287],{},"stream",[1026,1289,1290],{},"Kafka \u002F Redis \u002F NATS",[1026,1292,1293],{},"Fan-out, external integration, event sourcing",[994,1295],{},[997,1297,1299],{"id":1298},"_2-pick-an-example-from-flowdslcomexamples","2. Pick an example from flowdsl.com\u002Fexamples",[974,1301,1302,1303,1306,1307,1310],{},"Go to ",[978,1304,983],{"href":980,"rel":1305},[982]," and pick a flow. For this guide we'll build the ",[989,1308,1309],{},"Webhook Alert Pipeline"," pattern:",[1312,1313,1318],"pre",{"className":1314,"code":1315,"language":1316,"meta":1317,"style":1317},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# webhook-alert.flowdsl.yaml\nversion: \"1.0\"\ntitle: Webhook Alert Pipeline\ndescription: >\n  Ingest HTTP webhooks, filter by severity, enrich with metadata,\n  then dispatch to PagerDuty and Slack.\n\nnodes:\n  IngestWebhook:\n    operationId: ingest_webhook\n    kind: source\n    summary: Receives raw webhook payloads over HTTP POST \u002Fwebhooks\n\n  FilterBySeverity:\n    operationId: filter_by_severity\n    kind: router\n    summary: Routes critical\u002Fhigh to the alert path, low to the archive\n    inputs:\n      in: { packet: RawWebhook }\n    outputs:\n      alert: { packet: RawWebhook }\n      archive: { packet: RawWebhook }\n\n  EnrichPayload:\n    operationId: enrich_webhook\n    kind: transform\n    summary: Adds team ownership and service metadata from the config service\n    inputs:\n      in: { packet: RawWebhook }\n    outputs:\n      out: { packet: EnrichedWebhook }\n\n  DispatchPagerDuty:\n    operationId: dispatch_pagerduty\n    kind: action\n    summary: Opens a PagerDuty incident\n    inputs:\n      in: { packet: EnrichedWebhook }\n    outputs:\n      out: { packet: IncidentResult }\n\n  DispatchSlack:\n    operationId: dispatch_slack\n    kind: action\n    summary: Posts an alert message to the team Slack channel\n    inputs:\n      in: { packet: EnrichedWebhook }\n\n  ArchiveLow:\n    operationId: archive_low_severity\n    kind: terminal\n    summary: Writes low-severity webhooks to the archive collection\n    inputs:\n      in: { packet: RawWebhook }\n\nedges:\n  - from: IngestWebhook\n    to: FilterBySeverity\n    delivery:\n      mode: direct\n      packet: RawWebhook\n\n  - from: FilterBySeverity.alert\n    to: EnrichPayload\n    delivery:\n      mode: durable\n      packet: RawWebhook\n\n  - from: FilterBySeverity.archive\n    to: ArchiveLow\n    delivery:\n      mode: ephemeral\n      packet: RawWebhook\n      stream: archive-queue\n\n  - from: EnrichPayload\n    to: DispatchPagerDuty\n    delivery:\n      mode: durable\n      packet: EnrichedWebhook\n      retryPolicy:\n        maxAttempts: 3\n        backoff: exponential\n        initialDelay: PT2S\n      idempotencyKey: \"{{payload.webhookId}}-pagerduty\"\n\n  - from: EnrichPayload\n    to: DispatchSlack\n    delivery:\n      mode: durable\n      packet: EnrichedWebhook\n      idempotencyKey: \"{{payload.webhookId}}-slack\"\n\ncomponents:\n  packets:\n    RawWebhook:\n      type: object\n      properties:\n        webhookId: { type: string }\n        source: { type: string }\n        severity:\n          type: string\n          enum: [critical, high, medium, low]\n        payload: { type: object }\n        receivedAt: { type: string, format: date-time }\n      required: [webhookId, source, severity]\n\n    EnrichedWebhook:\n      type: object\n      properties:\n        webhookId: { type: string }\n        source: { type: string }\n        severity: { type: string }\n        payload: { type: object }\n        teamOwner: { type: string }\n        serviceSlug: { type: string }\n        receivedAt: { type: string, format: date-time }\n      required: [webhookId, source, severity, teamOwner]\n\n    IncidentResult:\n      type: object\n      properties:\n        incidentId: { type: string }\n        url: { type: string }\n        status: { type: string }\n","yaml","",[1029,1319,1320,1328,1348,1358,1369,1374,1379,1385,1392,1399,1409,1419,1429,1433,1440,1449,1458,1467,1474,1495,1502,1519,1536,1541,1549,1558,1567,1576,1583,1600,1607,1626,1631,1639,1649,1659,1669,1676,1693,1700,1718,1723,1731,1741,1750,1760,1767,1784,1789,1797,1807,1817,1827,1834,1851,1856,1863,1877,1888,1896,1907,1918,1923,1935,1945,1952,1962,1971,1976,1988,1998,2005,2015,2024,2035,2040,2051,2061,2068,2077,2086,2094,2106,2117,2128,2143,2148,2159,2169,2176,2185,2194,2208,2213,2221,2229,2237,2248,2256,2276,2294,2302,2313,2346,2365,2393,2418,2423,2431,2440,2447,2464,2481,2498,2515,2533,2551,2576,2602,2607,2615,2624,2631,2649,2667],{"__ignoreMap":1317},[1321,1322,1324],"span",{"class":1323,"line":10},"line",[1321,1325,1327],{"class":1326},"sHwdD","# webhook-alert.flowdsl.yaml\n",[1321,1329,1330,1334,1338,1341,1345],{"class":1323,"line":16},[1321,1331,1333],{"class":1332},"swJcz","version",[1321,1335,1337],{"class":1336},"sMK4o",":",[1321,1339,1340],{"class":1336}," \"",[1321,1342,1344],{"class":1343},"sfazB","1.0",[1321,1346,1347],{"class":1336},"\"\n",[1321,1349,1350,1353,1355],{"class":1323,"line":22},[1321,1351,1352],{"class":1332},"title",[1321,1354,1337],{"class":1336},[1321,1356,1357],{"class":1343}," Webhook Alert Pipeline\n",[1321,1359,1360,1363,1365],{"class":1323,"line":51},[1321,1361,1362],{"class":1332},"description",[1321,1364,1337],{"class":1336},[1321,1366,1368],{"class":1367},"s7zQu"," >\n",[1321,1370,1371],{"class":1323,"line":57},[1321,1372,1373],{"class":1343},"  Ingest HTTP webhooks, filter by severity, enrich with metadata,\n",[1321,1375,1376],{"class":1323,"line":102},[1321,1377,1378],{"class":1343},"  then dispatch to PagerDuty and Slack.\n",[1321,1380,1381],{"class":1323,"line":108},[1321,1382,1384],{"emptyLinePlaceholder":1383},true,"\n",[1321,1386,1387,1389],{"class":1323,"line":114},[1321,1388,1031],{"class":1332},[1321,1390,1391],{"class":1336},":\n",[1321,1393,1394,1397],{"class":1323,"line":120},[1321,1395,1396],{"class":1332},"  IngestWebhook",[1321,1398,1391],{"class":1336},[1321,1400,1401,1404,1406],{"class":1323,"line":181},[1321,1402,1403],{"class":1332},"    operationId",[1321,1405,1337],{"class":1336},[1321,1407,1408],{"class":1343}," ingest_webhook\n",[1321,1410,1411,1414,1416],{"class":1323,"line":186},[1321,1412,1413],{"class":1332},"    kind",[1321,1415,1337],{"class":1336},[1321,1417,1418],{"class":1343}," source\n",[1321,1420,1421,1424,1426],{"class":1323,"line":191},[1321,1422,1423],{"class":1332},"    summary",[1321,1425,1337],{"class":1336},[1321,1427,1428],{"class":1343}," Receives raw webhook payloads over HTTP POST \u002Fwebhooks\n",[1321,1430,1431],{"class":1323,"line":197},[1321,1432,1384],{"emptyLinePlaceholder":1383},[1321,1434,1435,1438],{"class":1323,"line":203},[1321,1436,1437],{"class":1332},"  FilterBySeverity",[1321,1439,1391],{"class":1336},[1321,1441,1442,1444,1446],{"class":1323,"line":214},[1321,1443,1403],{"class":1332},[1321,1445,1337],{"class":1336},[1321,1447,1448],{"class":1343}," filter_by_severity\n",[1321,1450,1451,1453,1455],{"class":1323,"line":223},[1321,1452,1413],{"class":1332},[1321,1454,1337],{"class":1336},[1321,1456,1457],{"class":1343}," router\n",[1321,1459,1460,1462,1464],{"class":1323,"line":229},[1321,1461,1423],{"class":1332},[1321,1463,1337],{"class":1336},[1321,1465,1466],{"class":1343}," Routes critical\u002Fhigh to the alert path, low to the archive\n",[1321,1468,1469,1472],{"class":1323,"line":234},[1321,1470,1471],{"class":1332},"    inputs",[1321,1473,1391],{"class":1336},[1321,1475,1476,1479,1481,1484,1487,1489,1492],{"class":1323,"line":240},[1321,1477,1478],{"class":1332},"      in",[1321,1480,1337],{"class":1336},[1321,1482,1483],{"class":1336}," {",[1321,1485,1486],{"class":1332}," packet",[1321,1488,1337],{"class":1336},[1321,1490,1491],{"class":1343}," RawWebhook",[1321,1493,1494],{"class":1336}," }\n",[1321,1496,1497,1500],{"class":1323,"line":246},[1321,1498,1499],{"class":1332},"    outputs",[1321,1501,1391],{"class":1336},[1321,1503,1504,1507,1509,1511,1513,1515,1517],{"class":1323,"line":251},[1321,1505,1506],{"class":1332},"      alert",[1321,1508,1337],{"class":1336},[1321,1510,1483],{"class":1336},[1321,1512,1486],{"class":1332},[1321,1514,1337],{"class":1336},[1321,1516,1491],{"class":1343},[1321,1518,1494],{"class":1336},[1321,1520,1521,1524,1526,1528,1530,1532,1534],{"class":1323,"line":257},[1321,1522,1523],{"class":1332},"      archive",[1321,1525,1337],{"class":1336},[1321,1527,1483],{"class":1336},[1321,1529,1486],{"class":1332},[1321,1531,1337],{"class":1336},[1321,1533,1491],{"class":1343},[1321,1535,1494],{"class":1336},[1321,1537,1539],{"class":1323,"line":1538},23,[1321,1540,1384],{"emptyLinePlaceholder":1383},[1321,1542,1544,1547],{"class":1323,"line":1543},24,[1321,1545,1546],{"class":1332},"  EnrichPayload",[1321,1548,1391],{"class":1336},[1321,1550,1551,1553,1555],{"class":1323,"line":271},[1321,1552,1403],{"class":1332},[1321,1554,1337],{"class":1336},[1321,1556,1557],{"class":1343}," enrich_webhook\n",[1321,1559,1560,1562,1564],{"class":1323,"line":277},[1321,1561,1413],{"class":1332},[1321,1563,1337],{"class":1336},[1321,1565,1566],{"class":1343}," transform\n",[1321,1568,1569,1571,1573],{"class":1323,"line":282},[1321,1570,1423],{"class":1332},[1321,1572,1337],{"class":1336},[1321,1574,1575],{"class":1343}," Adds team ownership and service metadata from the config service\n",[1321,1577,1579,1581],{"class":1323,"line":1578},28,[1321,1580,1471],{"class":1332},[1321,1582,1391],{"class":1336},[1321,1584,1586,1588,1590,1592,1594,1596,1598],{"class":1323,"line":1585},29,[1321,1587,1478],{"class":1332},[1321,1589,1337],{"class":1336},[1321,1591,1483],{"class":1336},[1321,1593,1486],{"class":1332},[1321,1595,1337],{"class":1336},[1321,1597,1491],{"class":1343},[1321,1599,1494],{"class":1336},[1321,1601,1603,1605],{"class":1323,"line":1602},30,[1321,1604,1499],{"class":1332},[1321,1606,1391],{"class":1336},[1321,1608,1610,1613,1615,1617,1619,1621,1624],{"class":1323,"line":1609},31,[1321,1611,1612],{"class":1332},"      out",[1321,1614,1337],{"class":1336},[1321,1616,1483],{"class":1336},[1321,1618,1486],{"class":1332},[1321,1620,1337],{"class":1336},[1321,1622,1623],{"class":1343}," EnrichedWebhook",[1321,1625,1494],{"class":1336},[1321,1627,1629],{"class":1323,"line":1628},32,[1321,1630,1384],{"emptyLinePlaceholder":1383},[1321,1632,1634,1637],{"class":1323,"line":1633},33,[1321,1635,1636],{"class":1332},"  DispatchPagerDuty",[1321,1638,1391],{"class":1336},[1321,1640,1642,1644,1646],{"class":1323,"line":1641},34,[1321,1643,1403],{"class":1332},[1321,1645,1337],{"class":1336},[1321,1647,1648],{"class":1343}," dispatch_pagerduty\n",[1321,1650,1652,1654,1656],{"class":1323,"line":1651},35,[1321,1653,1413],{"class":1332},[1321,1655,1337],{"class":1336},[1321,1657,1658],{"class":1343}," action\n",[1321,1660,1662,1664,1666],{"class":1323,"line":1661},36,[1321,1663,1423],{"class":1332},[1321,1665,1337],{"class":1336},[1321,1667,1668],{"class":1343}," Opens a PagerDuty incident\n",[1321,1670,1672,1674],{"class":1323,"line":1671},37,[1321,1673,1471],{"class":1332},[1321,1675,1391],{"class":1336},[1321,1677,1679,1681,1683,1685,1687,1689,1691],{"class":1323,"line":1678},38,[1321,1680,1478],{"class":1332},[1321,1682,1337],{"class":1336},[1321,1684,1483],{"class":1336},[1321,1686,1486],{"class":1332},[1321,1688,1337],{"class":1336},[1321,1690,1623],{"class":1343},[1321,1692,1494],{"class":1336},[1321,1694,1696,1698],{"class":1323,"line":1695},39,[1321,1697,1499],{"class":1332},[1321,1699,1391],{"class":1336},[1321,1701,1703,1705,1707,1709,1711,1713,1716],{"class":1323,"line":1702},40,[1321,1704,1612],{"class":1332},[1321,1706,1337],{"class":1336},[1321,1708,1483],{"class":1336},[1321,1710,1486],{"class":1332},[1321,1712,1337],{"class":1336},[1321,1714,1715],{"class":1343}," IncidentResult",[1321,1717,1494],{"class":1336},[1321,1719,1721],{"class":1323,"line":1720},41,[1321,1722,1384],{"emptyLinePlaceholder":1383},[1321,1724,1726,1729],{"class":1323,"line":1725},42,[1321,1727,1728],{"class":1332},"  DispatchSlack",[1321,1730,1391],{"class":1336},[1321,1732,1734,1736,1738],{"class":1323,"line":1733},43,[1321,1735,1403],{"class":1332},[1321,1737,1337],{"class":1336},[1321,1739,1740],{"class":1343}," dispatch_slack\n",[1321,1742,1744,1746,1748],{"class":1323,"line":1743},44,[1321,1745,1413],{"class":1332},[1321,1747,1337],{"class":1336},[1321,1749,1658],{"class":1343},[1321,1751,1753,1755,1757],{"class":1323,"line":1752},45,[1321,1754,1423],{"class":1332},[1321,1756,1337],{"class":1336},[1321,1758,1759],{"class":1343}," Posts an alert message to the team Slack channel\n",[1321,1761,1763,1765],{"class":1323,"line":1762},46,[1321,1764,1471],{"class":1332},[1321,1766,1391],{"class":1336},[1321,1768,1770,1772,1774,1776,1778,1780,1782],{"class":1323,"line":1769},47,[1321,1771,1478],{"class":1332},[1321,1773,1337],{"class":1336},[1321,1775,1483],{"class":1336},[1321,1777,1486],{"class":1332},[1321,1779,1337],{"class":1336},[1321,1781,1623],{"class":1343},[1321,1783,1494],{"class":1336},[1321,1785,1787],{"class":1323,"line":1786},48,[1321,1788,1384],{"emptyLinePlaceholder":1383},[1321,1790,1792,1795],{"class":1323,"line":1791},49,[1321,1793,1794],{"class":1332},"  ArchiveLow",[1321,1796,1391],{"class":1336},[1321,1798,1800,1802,1804],{"class":1323,"line":1799},50,[1321,1801,1403],{"class":1332},[1321,1803,1337],{"class":1336},[1321,1805,1806],{"class":1343}," archive_low_severity\n",[1321,1808,1810,1812,1814],{"class":1323,"line":1809},51,[1321,1811,1413],{"class":1332},[1321,1813,1337],{"class":1336},[1321,1815,1816],{"class":1343}," terminal\n",[1321,1818,1820,1822,1824],{"class":1323,"line":1819},52,[1321,1821,1423],{"class":1332},[1321,1823,1337],{"class":1336},[1321,1825,1826],{"class":1343}," Writes low-severity webhooks to the archive collection\n",[1321,1828,1830,1832],{"class":1323,"line":1829},53,[1321,1831,1471],{"class":1332},[1321,1833,1391],{"class":1336},[1321,1835,1837,1839,1841,1843,1845,1847,1849],{"class":1323,"line":1836},54,[1321,1838,1478],{"class":1332},[1321,1840,1337],{"class":1336},[1321,1842,1483],{"class":1336},[1321,1844,1486],{"class":1332},[1321,1846,1337],{"class":1336},[1321,1848,1491],{"class":1343},[1321,1850,1494],{"class":1336},[1321,1852,1854],{"class":1323,"line":1853},55,[1321,1855,1384],{"emptyLinePlaceholder":1383},[1321,1857,1859,1861],{"class":1323,"line":1858},56,[1321,1860,1041],{"class":1332},[1321,1862,1391],{"class":1336},[1321,1864,1866,1869,1872,1874],{"class":1323,"line":1865},57,[1321,1867,1868],{"class":1336},"  -",[1321,1870,1871],{"class":1332}," from",[1321,1873,1337],{"class":1336},[1321,1875,1876],{"class":1343}," IngestWebhook\n",[1321,1878,1880,1883,1885],{"class":1323,"line":1879},58,[1321,1881,1882],{"class":1332},"    to",[1321,1884,1337],{"class":1336},[1321,1886,1887],{"class":1343}," FilterBySeverity\n",[1321,1889,1891,1894],{"class":1323,"line":1890},59,[1321,1892,1893],{"class":1332},"    delivery",[1321,1895,1391],{"class":1336},[1321,1897,1899,1902,1904],{"class":1323,"line":1898},60,[1321,1900,1901],{"class":1332},"      mode",[1321,1903,1337],{"class":1336},[1321,1905,1906],{"class":1343}," direct\n",[1321,1908,1910,1913,1915],{"class":1323,"line":1909},61,[1321,1911,1912],{"class":1332},"      packet",[1321,1914,1337],{"class":1336},[1321,1916,1917],{"class":1343}," RawWebhook\n",[1321,1919,1921],{"class":1323,"line":1920},62,[1321,1922,1384],{"emptyLinePlaceholder":1383},[1321,1924,1926,1928,1930,1932],{"class":1323,"line":1925},63,[1321,1927,1868],{"class":1336},[1321,1929,1871],{"class":1332},[1321,1931,1337],{"class":1336},[1321,1933,1934],{"class":1343}," FilterBySeverity.alert\n",[1321,1936,1938,1940,1942],{"class":1323,"line":1937},64,[1321,1939,1882],{"class":1332},[1321,1941,1337],{"class":1336},[1321,1943,1944],{"class":1343}," EnrichPayload\n",[1321,1946,1948,1950],{"class":1323,"line":1947},65,[1321,1949,1893],{"class":1332},[1321,1951,1391],{"class":1336},[1321,1953,1955,1957,1959],{"class":1323,"line":1954},66,[1321,1956,1901],{"class":1332},[1321,1958,1337],{"class":1336},[1321,1960,1961],{"class":1343}," durable\n",[1321,1963,1965,1967,1969],{"class":1323,"line":1964},67,[1321,1966,1912],{"class":1332},[1321,1968,1337],{"class":1336},[1321,1970,1917],{"class":1343},[1321,1972,1974],{"class":1323,"line":1973},68,[1321,1975,1384],{"emptyLinePlaceholder":1383},[1321,1977,1979,1981,1983,1985],{"class":1323,"line":1978},69,[1321,1980,1868],{"class":1336},[1321,1982,1871],{"class":1332},[1321,1984,1337],{"class":1336},[1321,1986,1987],{"class":1343}," FilterBySeverity.archive\n",[1321,1989,1991,1993,1995],{"class":1323,"line":1990},70,[1321,1992,1882],{"class":1332},[1321,1994,1337],{"class":1336},[1321,1996,1997],{"class":1343}," ArchiveLow\n",[1321,1999,2001,2003],{"class":1323,"line":2000},71,[1321,2002,1893],{"class":1332},[1321,2004,1391],{"class":1336},[1321,2006,2008,2010,2012],{"class":1323,"line":2007},72,[1321,2009,1901],{"class":1332},[1321,2011,1337],{"class":1336},[1321,2013,2014],{"class":1343}," ephemeral\n",[1321,2016,2018,2020,2022],{"class":1323,"line":2017},73,[1321,2019,1912],{"class":1332},[1321,2021,1337],{"class":1336},[1321,2023,1917],{"class":1343},[1321,2025,2027,2030,2032],{"class":1323,"line":2026},74,[1321,2028,2029],{"class":1332},"      stream",[1321,2031,1337],{"class":1336},[1321,2033,2034],{"class":1343}," archive-queue\n",[1321,2036,2038],{"class":1323,"line":2037},75,[1321,2039,1384],{"emptyLinePlaceholder":1383},[1321,2041,2043,2045,2047,2049],{"class":1323,"line":2042},76,[1321,2044,1868],{"class":1336},[1321,2046,1871],{"class":1332},[1321,2048,1337],{"class":1336},[1321,2050,1944],{"class":1343},[1321,2052,2054,2056,2058],{"class":1323,"line":2053},77,[1321,2055,1882],{"class":1332},[1321,2057,1337],{"class":1336},[1321,2059,2060],{"class":1343}," DispatchPagerDuty\n",[1321,2062,2064,2066],{"class":1323,"line":2063},78,[1321,2065,1893],{"class":1332},[1321,2067,1391],{"class":1336},[1321,2069,2071,2073,2075],{"class":1323,"line":2070},79,[1321,2072,1901],{"class":1332},[1321,2074,1337],{"class":1336},[1321,2076,1961],{"class":1343},[1321,2078,2079,2081,2083],{"class":1323,"line":963},[1321,2080,1912],{"class":1332},[1321,2082,1337],{"class":1336},[1321,2084,2085],{"class":1343}," EnrichedWebhook\n",[1321,2087,2089,2092],{"class":1323,"line":2088},81,[1321,2090,2091],{"class":1332},"      retryPolicy",[1321,2093,1391],{"class":1336},[1321,2095,2097,2100,2102],{"class":1323,"line":2096},82,[1321,2098,2099],{"class":1332},"        maxAttempts",[1321,2101,1337],{"class":1336},[1321,2103,2105],{"class":2104},"sbssI"," 3\n",[1321,2107,2109,2112,2114],{"class":1323,"line":2108},83,[1321,2110,2111],{"class":1332},"        backoff",[1321,2113,1337],{"class":1336},[1321,2115,2116],{"class":1343}," exponential\n",[1321,2118,2120,2123,2125],{"class":1323,"line":2119},84,[1321,2121,2122],{"class":1332},"        initialDelay",[1321,2124,1337],{"class":1336},[1321,2126,2127],{"class":1343}," PT2S\n",[1321,2129,2131,2134,2136,2138,2141],{"class":1323,"line":2130},85,[1321,2132,2133],{"class":1332},"      idempotencyKey",[1321,2135,1337],{"class":1336},[1321,2137,1340],{"class":1336},[1321,2139,2140],{"class":1343},"{{payload.webhookId}}-pagerduty",[1321,2142,1347],{"class":1336},[1321,2144,2146],{"class":1323,"line":2145},86,[1321,2147,1384],{"emptyLinePlaceholder":1383},[1321,2149,2151,2153,2155,2157],{"class":1323,"line":2150},87,[1321,2152,1868],{"class":1336},[1321,2154,1871],{"class":1332},[1321,2156,1337],{"class":1336},[1321,2158,1944],{"class":1343},[1321,2160,2162,2164,2166],{"class":1323,"line":2161},88,[1321,2163,1882],{"class":1332},[1321,2165,1337],{"class":1336},[1321,2167,2168],{"class":1343}," DispatchSlack\n",[1321,2170,2172,2174],{"class":1323,"line":2171},89,[1321,2173,1893],{"class":1332},[1321,2175,1391],{"class":1336},[1321,2177,2179,2181,2183],{"class":1323,"line":2178},90,[1321,2180,1901],{"class":1332},[1321,2182,1337],{"class":1336},[1321,2184,1961],{"class":1343},[1321,2186,2188,2190,2192],{"class":1323,"line":2187},91,[1321,2189,1912],{"class":1332},[1321,2191,1337],{"class":1336},[1321,2193,2085],{"class":1343},[1321,2195,2197,2199,2201,2203,2206],{"class":1323,"line":2196},92,[1321,2198,2133],{"class":1332},[1321,2200,1337],{"class":1336},[1321,2202,1340],{"class":1336},[1321,2204,2205],{"class":1343},"{{payload.webhookId}}-slack",[1321,2207,1347],{"class":1336},[1321,2209,2211],{"class":1323,"line":2210},93,[1321,2212,1384],{"emptyLinePlaceholder":1383},[1321,2214,2216,2219],{"class":1323,"line":2215},94,[1321,2217,2218],{"class":1332},"components",[1321,2220,1391],{"class":1336},[1321,2222,2224,2227],{"class":1323,"line":2223},95,[1321,2225,2226],{"class":1332},"  packets",[1321,2228,1391],{"class":1336},[1321,2230,2232,2235],{"class":1323,"line":2231},96,[1321,2233,2234],{"class":1332},"    RawWebhook",[1321,2236,1391],{"class":1336},[1321,2238,2240,2243,2245],{"class":1323,"line":2239},97,[1321,2241,2242],{"class":1332},"      type",[1321,2244,1337],{"class":1336},[1321,2246,2247],{"class":1343}," object\n",[1321,2249,2251,2254],{"class":1323,"line":2250},98,[1321,2252,2253],{"class":1332},"      properties",[1321,2255,1391],{"class":1336},[1321,2257,2259,2262,2264,2266,2269,2271,2274],{"class":1323,"line":2258},99,[1321,2260,2261],{"class":1332},"        webhookId",[1321,2263,1337],{"class":1336},[1321,2265,1483],{"class":1336},[1321,2267,2268],{"class":1332}," type",[1321,2270,1337],{"class":1336},[1321,2272,2273],{"class":1343}," string",[1321,2275,1494],{"class":1336},[1321,2277,2279,2282,2284,2286,2288,2290,2292],{"class":1323,"line":2278},100,[1321,2280,2281],{"class":1332},"        source",[1321,2283,1337],{"class":1336},[1321,2285,1483],{"class":1336},[1321,2287,2268],{"class":1332},[1321,2289,1337],{"class":1336},[1321,2291,2273],{"class":1343},[1321,2293,1494],{"class":1336},[1321,2295,2297,2300],{"class":1323,"line":2296},101,[1321,2298,2299],{"class":1332},"        severity",[1321,2301,1391],{"class":1336},[1321,2303,2305,2308,2310],{"class":1323,"line":2304},102,[1321,2306,2307],{"class":1332},"          type",[1321,2309,1337],{"class":1336},[1321,2311,2312],{"class":1343}," string\n",[1321,2314,2316,2319,2321,2324,2327,2330,2333,2335,2338,2340,2343],{"class":1323,"line":2315},103,[1321,2317,2318],{"class":1332},"          enum",[1321,2320,1337],{"class":1336},[1321,2322,2323],{"class":1336}," [",[1321,2325,2326],{"class":1343},"critical",[1321,2328,2329],{"class":1336},",",[1321,2331,2332],{"class":1343}," high",[1321,2334,2329],{"class":1336},[1321,2336,2337],{"class":1343}," medium",[1321,2339,2329],{"class":1336},[1321,2341,2342],{"class":1343}," low",[1321,2344,2345],{"class":1336},"]\n",[1321,2347,2349,2352,2354,2356,2358,2360,2363],{"class":1323,"line":2348},104,[1321,2350,2351],{"class":1332},"        payload",[1321,2353,1337],{"class":1336},[1321,2355,1483],{"class":1336},[1321,2357,2268],{"class":1332},[1321,2359,1337],{"class":1336},[1321,2361,2362],{"class":1343}," object",[1321,2364,1494],{"class":1336},[1321,2366,2368,2371,2373,2375,2377,2379,2381,2383,2386,2388,2391],{"class":1323,"line":2367},105,[1321,2369,2370],{"class":1332},"        receivedAt",[1321,2372,1337],{"class":1336},[1321,2374,1483],{"class":1336},[1321,2376,2268],{"class":1332},[1321,2378,1337],{"class":1336},[1321,2380,2273],{"class":1343},[1321,2382,2329],{"class":1336},[1321,2384,2385],{"class":1332}," format",[1321,2387,1337],{"class":1336},[1321,2389,2390],{"class":1343}," date-time",[1321,2392,1494],{"class":1336},[1321,2394,2396,2399,2401,2403,2406,2408,2411,2413,2416],{"class":1323,"line":2395},106,[1321,2397,2398],{"class":1332},"      required",[1321,2400,1337],{"class":1336},[1321,2402,2323],{"class":1336},[1321,2404,2405],{"class":1343},"webhookId",[1321,2407,2329],{"class":1336},[1321,2409,2410],{"class":1343}," source",[1321,2412,2329],{"class":1336},[1321,2414,2415],{"class":1343}," severity",[1321,2417,2345],{"class":1336},[1321,2419,2421],{"class":1323,"line":2420},107,[1321,2422,1384],{"emptyLinePlaceholder":1383},[1321,2424,2426,2429],{"class":1323,"line":2425},108,[1321,2427,2428],{"class":1332},"    EnrichedWebhook",[1321,2430,1391],{"class":1336},[1321,2432,2434,2436,2438],{"class":1323,"line":2433},109,[1321,2435,2242],{"class":1332},[1321,2437,1337],{"class":1336},[1321,2439,2247],{"class":1343},[1321,2441,2443,2445],{"class":1323,"line":2442},110,[1321,2444,2253],{"class":1332},[1321,2446,1391],{"class":1336},[1321,2448,2450,2452,2454,2456,2458,2460,2462],{"class":1323,"line":2449},111,[1321,2451,2261],{"class":1332},[1321,2453,1337],{"class":1336},[1321,2455,1483],{"class":1336},[1321,2457,2268],{"class":1332},[1321,2459,1337],{"class":1336},[1321,2461,2273],{"class":1343},[1321,2463,1494],{"class":1336},[1321,2465,2467,2469,2471,2473,2475,2477,2479],{"class":1323,"line":2466},112,[1321,2468,2281],{"class":1332},[1321,2470,1337],{"class":1336},[1321,2472,1483],{"class":1336},[1321,2474,2268],{"class":1332},[1321,2476,1337],{"class":1336},[1321,2478,2273],{"class":1343},[1321,2480,1494],{"class":1336},[1321,2482,2484,2486,2488,2490,2492,2494,2496],{"class":1323,"line":2483},113,[1321,2485,2299],{"class":1332},[1321,2487,1337],{"class":1336},[1321,2489,1483],{"class":1336},[1321,2491,2268],{"class":1332},[1321,2493,1337],{"class":1336},[1321,2495,2273],{"class":1343},[1321,2497,1494],{"class":1336},[1321,2499,2501,2503,2505,2507,2509,2511,2513],{"class":1323,"line":2500},114,[1321,2502,2351],{"class":1332},[1321,2504,1337],{"class":1336},[1321,2506,1483],{"class":1336},[1321,2508,2268],{"class":1332},[1321,2510,1337],{"class":1336},[1321,2512,2362],{"class":1343},[1321,2514,1494],{"class":1336},[1321,2516,2518,2521,2523,2525,2527,2529,2531],{"class":1323,"line":2517},115,[1321,2519,2520],{"class":1332},"        teamOwner",[1321,2522,1337],{"class":1336},[1321,2524,1483],{"class":1336},[1321,2526,2268],{"class":1332},[1321,2528,1337],{"class":1336},[1321,2530,2273],{"class":1343},[1321,2532,1494],{"class":1336},[1321,2534,2536,2539,2541,2543,2545,2547,2549],{"class":1323,"line":2535},116,[1321,2537,2538],{"class":1332},"        serviceSlug",[1321,2540,1337],{"class":1336},[1321,2542,1483],{"class":1336},[1321,2544,2268],{"class":1332},[1321,2546,1337],{"class":1336},[1321,2548,2273],{"class":1343},[1321,2550,1494],{"class":1336},[1321,2552,2554,2556,2558,2560,2562,2564,2566,2568,2570,2572,2574],{"class":1323,"line":2553},117,[1321,2555,2370],{"class":1332},[1321,2557,1337],{"class":1336},[1321,2559,1483],{"class":1336},[1321,2561,2268],{"class":1332},[1321,2563,1337],{"class":1336},[1321,2565,2273],{"class":1343},[1321,2567,2329],{"class":1336},[1321,2569,2385],{"class":1332},[1321,2571,1337],{"class":1336},[1321,2573,2390],{"class":1343},[1321,2575,1494],{"class":1336},[1321,2577,2579,2581,2583,2585,2587,2589,2591,2593,2595,2597,2600],{"class":1323,"line":2578},118,[1321,2580,2398],{"class":1332},[1321,2582,1337],{"class":1336},[1321,2584,2323],{"class":1336},[1321,2586,2405],{"class":1343},[1321,2588,2329],{"class":1336},[1321,2590,2410],{"class":1343},[1321,2592,2329],{"class":1336},[1321,2594,2415],{"class":1343},[1321,2596,2329],{"class":1336},[1321,2598,2599],{"class":1343}," teamOwner",[1321,2601,2345],{"class":1336},[1321,2603,2605],{"class":1323,"line":2604},119,[1321,2606,1384],{"emptyLinePlaceholder":1383},[1321,2608,2610,2613],{"class":1323,"line":2609},120,[1321,2611,2612],{"class":1332},"    IncidentResult",[1321,2614,1391],{"class":1336},[1321,2616,2618,2620,2622],{"class":1323,"line":2617},121,[1321,2619,2242],{"class":1332},[1321,2621,1337],{"class":1336},[1321,2623,2247],{"class":1343},[1321,2625,2627,2629],{"class":1323,"line":2626},122,[1321,2628,2253],{"class":1332},[1321,2630,1391],{"class":1336},[1321,2632,2634,2637,2639,2641,2643,2645,2647],{"class":1323,"line":2633},123,[1321,2635,2636],{"class":1332},"        incidentId",[1321,2638,1337],{"class":1336},[1321,2640,1483],{"class":1336},[1321,2642,2268],{"class":1332},[1321,2644,1337],{"class":1336},[1321,2646,2273],{"class":1343},[1321,2648,1494],{"class":1336},[1321,2650,2652,2655,2657,2659,2661,2663,2665],{"class":1323,"line":2651},124,[1321,2653,2654],{"class":1332},"        url",[1321,2656,1337],{"class":1336},[1321,2658,1483],{"class":1336},[1321,2660,2268],{"class":1332},[1321,2662,1337],{"class":1336},[1321,2664,2273],{"class":1343},[1321,2666,1494],{"class":1336},[1321,2668,2670,2673,2675,2677,2679,2681,2683],{"class":1323,"line":2669},125,[1321,2671,2672],{"class":1332},"        status",[1321,2674,1337],{"class":1336},[1321,2676,1483],{"class":1336},[1321,2678,2268],{"class":1332},[1321,2680,1337],{"class":1336},[1321,2682,2273],{"class":1343},[1321,2684,1494],{"class":1336},[994,2686],{},[997,2688,2690],{"id":2689},"_3-validate-the-flow","3. Validate the flow",[1312,2692,2696],{"className":2693,"code":2694,"language":2695,"meta":1317,"style":1317},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Validate FlowDSL syntax and cross-references\nredelayctl validate webhook-alert.flowdsl.yaml\n\n# Output on success:\n# OK — Webhook Alert Pipeline: 0 errors, 0 warnings\n","bash",[1029,2697,2698,2703,2715,2719,2724],{"__ignoreMap":1317},[1321,2699,2700],{"class":1323,"line":10},[1321,2701,2702],{"class":1326},"# Validate FlowDSL syntax and cross-references\n",[1321,2704,2705,2709,2712],{"class":1323,"line":16},[1321,2706,2708],{"class":2707},"sBMFI","redelayctl",[1321,2710,2711],{"class":1343}," validate",[1321,2713,2714],{"class":1343}," webhook-alert.flowdsl.yaml\n",[1321,2716,2717],{"class":1323,"line":22},[1321,2718,1384],{"emptyLinePlaceholder":1383},[1321,2720,2721],{"class":1323,"line":51},[1321,2722,2723],{"class":1326},"# Output on success:\n",[1321,2725,2726],{"class":1323,"line":57},[1321,2727,2728],{"class":1326},"# OK — Webhook Alert Pipeline: 0 errors, 0 warnings\n",[974,2730,2731,2732,2735,2736,2738,2739,2742],{},"Validation checks: ",[1029,2733,2734],{},"entity_type"," \u002F ",[1029,2737,1142],{}," on every event, step IDs, ",[1029,2740,2741],{},"depends_on"," refs, field types, channel protocols, schedule action refs.",[994,2744],{},[997,2746,2748],{"id":2747},"_4-generate-ir-and-module-files","4. Generate IR and module files",[974,2750,2751],{},"Redelay's compile pipeline converts FlowDSL → IR (Intermediate Representation) → Go module scaffolds.",[1312,2753,2755],{"className":2693,"code":2754,"language":2695,"meta":1317,"style":1317},"# Step 1: Convert FlowDSL to IR JSON\nredelayctl convert flowdsl json webhook-alert.flowdsl.yaml > webhook-alert.ir.json\n\n# Step 2: Inspect the IR (optional — useful for debugging)\nredelayctl ir webhook-alert.flowdsl.yaml\n\n# Step 3: Generate module YAML files from IR\nredelayctl generate webhook-alert.ir.json .\u002Fmodules\u002F\n\n# Step 4: Export to AsyncAPI (for API docs and schema contracts)\nredelayctl convert flowdsl asyncapi webhook-alert.flowdsl.yaml > asyncapi.yaml\n\n# Step 5: Export to OpenAPI (for HTTP action docs)\nredelayctl convert flowdsl openapi webhook-alert.flowdsl.yaml > openapi.yaml\n\n# Step 6: Generate SVG diagram for each module\nredelayctl diagram .\u002Fmodules\u002Falerts\u002Fmodule.yaml diagram.svg\n",[1029,2756,2757,2762,2784,2788,2793,2802,2806,2811,2824,2828,2833,2851,2855,2860,2878,2882,2887],{"__ignoreMap":1317},[1321,2758,2759],{"class":1323,"line":10},[1321,2760,2761],{"class":1326},"# Step 1: Convert FlowDSL to IR JSON\n",[1321,2763,2764,2766,2769,2772,2775,2778,2781],{"class":1323,"line":16},[1321,2765,2708],{"class":2707},[1321,2767,2768],{"class":1343}," convert",[1321,2770,2771],{"class":1343}," flowdsl",[1321,2773,2774],{"class":1343}," json",[1321,2776,2777],{"class":1343}," webhook-alert.flowdsl.yaml",[1321,2779,2780],{"class":1336}," >",[1321,2782,2783],{"class":1343}," webhook-alert.ir.json\n",[1321,2785,2786],{"class":1323,"line":22},[1321,2787,1384],{"emptyLinePlaceholder":1383},[1321,2789,2790],{"class":1323,"line":51},[1321,2791,2792],{"class":1326},"# Step 2: Inspect the IR (optional — useful for debugging)\n",[1321,2794,2795,2797,2800],{"class":1323,"line":57},[1321,2796,2708],{"class":2707},[1321,2798,2799],{"class":1343}," ir",[1321,2801,2714],{"class":1343},[1321,2803,2804],{"class":1323,"line":102},[1321,2805,1384],{"emptyLinePlaceholder":1383},[1321,2807,2808],{"class":1323,"line":108},[1321,2809,2810],{"class":1326},"# Step 3: Generate module YAML files from IR\n",[1321,2812,2813,2815,2818,2821],{"class":1323,"line":114},[1321,2814,2708],{"class":2707},[1321,2816,2817],{"class":1343}," generate",[1321,2819,2820],{"class":1343}," webhook-alert.ir.json",[1321,2822,2823],{"class":1343}," .\u002Fmodules\u002F\n",[1321,2825,2826],{"class":1323,"line":120},[1321,2827,1384],{"emptyLinePlaceholder":1383},[1321,2829,2830],{"class":1323,"line":181},[1321,2831,2832],{"class":1326},"# Step 4: Export to AsyncAPI (for API docs and schema contracts)\n",[1321,2834,2835,2837,2839,2841,2844,2846,2848],{"class":1323,"line":186},[1321,2836,2708],{"class":2707},[1321,2838,2768],{"class":1343},[1321,2840,2771],{"class":1343},[1321,2842,2843],{"class":1343}," asyncapi",[1321,2845,2777],{"class":1343},[1321,2847,2780],{"class":1336},[1321,2849,2850],{"class":1343}," asyncapi.yaml\n",[1321,2852,2853],{"class":1323,"line":191},[1321,2854,1384],{"emptyLinePlaceholder":1383},[1321,2856,2857],{"class":1323,"line":197},[1321,2858,2859],{"class":1326},"# Step 5: Export to OpenAPI (for HTTP action docs)\n",[1321,2861,2862,2864,2866,2868,2871,2873,2875],{"class":1323,"line":203},[1321,2863,2708],{"class":2707},[1321,2865,2768],{"class":1343},[1321,2867,2771],{"class":1343},[1321,2869,2870],{"class":1343}," openapi",[1321,2872,2777],{"class":1343},[1321,2874,2780],{"class":1336},[1321,2876,2877],{"class":1343}," openapi.yaml\n",[1321,2879,2880],{"class":1323,"line":214},[1321,2881,1384],{"emptyLinePlaceholder":1383},[1321,2883,2884],{"class":1323,"line":223},[1321,2885,2886],{"class":1326},"# Step 6: Generate SVG diagram for each module\n",[1321,2888,2889,2891,2894,2897],{"class":1323,"line":229},[1321,2890,2708],{"class":2707},[1321,2892,2893],{"class":1343}," diagram",[1321,2895,2896],{"class":1343}," .\u002Fmodules\u002Falerts\u002Fmodule.yaml",[1321,2898,2899],{"class":1343}," diagram.svg\n",[2901,2902,2903],"blockquote",{},[974,2904,2905,2908],{},[989,2906,2907],{},"Tip:"," Add the export steps to your CI pipeline. The AsyncAPI and OpenAPI outputs stay in sync with your flow automatically.",[994,2910],{},[997,2912,2914],{"id":2913},"_5-translate-nodes-to-go-handlers","5. Translate nodes to Go handlers",[974,2916,2917,2918,2921],{},"Each node ",[1029,2919,2920],{},"operationId"," maps to a function or struct in Go. The pattern is the same for every kind:",[1056,2923,2925],{"id":2924},"project-layout","Project layout",[1312,2927,2932],{"className":2928,"code":2930,"language":2931},[2929],"language-text","modules\u002Falerts\u002F\n├── module.go        # Module wrapper + init() factory registration\n├── config.go        # Env-based configuration\n├── packets.go       # Go structs matching components.packets\n├── nodes\u002F\n│   ├── source.go    # ingest_webhook — HTTP handler → event publish\n│   ├── router.go    # filter_by_severity\n│   ├── transform.go # enrich_webhook\n│   ├── action_pd.go # dispatch_pagerduty\n│   ├── action_sl.go # dispatch_slack\n│   └── terminal.go  # archive_low_severity\n└── events.go        # typed.EventDefinition declarations\n","text",[1029,2933,2930],{"__ignoreMap":1317},[1056,2935,2937],{"id":2936},"packetsgo-typed-packet-structs","packets.go — typed packet structs",[1312,2939,2943],{"className":2940,"code":2941,"language":2942,"meta":1317,"style":1317},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","package alerts\n\nimport \"time\"\n\n\u002F\u002F RawWebhook matches components.packets.RawWebhook in the FlowDSL document.\ntype RawWebhook struct {\n    WebhookID  string         `json:\"webhookId\"`\n    Source     string         `json:\"source\"`\n    Severity   string         `json:\"severity\"` \u002F\u002F critical | high | medium | low\n    Payload    map[string]any `json:\"payload\"`\n    ReceivedAt time.Time      `json:\"receivedAt\"`\n}\n\n\u002F\u002F EnrichedWebhook is the output of the enrich_webhook transform node.\ntype EnrichedWebhook struct {\n    WebhookID   string         `json:\"webhookId\"`\n    Source      string         `json:\"source\"`\n    Severity    string         `json:\"severity\"`\n    Payload     map[string]any `json:\"payload\"`\n    TeamOwner   string         `json:\"teamOwner\"`\n    ServiceSlug string         `json:\"serviceSlug\"`\n    ReceivedAt  time.Time      `json:\"receivedAt\"`\n}\n\n\u002F\u002F IncidentResult is the output of the dispatch_pagerduty action node.\ntype IncidentResult struct {\n    IncidentID string `json:\"incidentId\"`\n    URL        string `json:\"url\"`\n    Status     string `json:\"status\"`\n}\n","go",[1029,2944,2945,2950,2954,2959,2963,2968,2973,2978,2983,2988,2993,2998,3003,3007,3012,3017,3022,3027,3032,3037,3042,3047,3052,3056,3060,3065,3070,3075,3080,3085],{"__ignoreMap":1317},[1321,2946,2947],{"class":1323,"line":10},[1321,2948,2949],{},"package alerts\n",[1321,2951,2952],{"class":1323,"line":16},[1321,2953,1384],{"emptyLinePlaceholder":1383},[1321,2955,2956],{"class":1323,"line":22},[1321,2957,2958],{},"import \"time\"\n",[1321,2960,2961],{"class":1323,"line":51},[1321,2962,1384],{"emptyLinePlaceholder":1383},[1321,2964,2965],{"class":1323,"line":57},[1321,2966,2967],{},"\u002F\u002F RawWebhook matches components.packets.RawWebhook in the FlowDSL document.\n",[1321,2969,2970],{"class":1323,"line":102},[1321,2971,2972],{},"type RawWebhook struct {\n",[1321,2974,2975],{"class":1323,"line":108},[1321,2976,2977],{},"    WebhookID  string         `json:\"webhookId\"`\n",[1321,2979,2980],{"class":1323,"line":114},[1321,2981,2982],{},"    Source     string         `json:\"source\"`\n",[1321,2984,2985],{"class":1323,"line":120},[1321,2986,2987],{},"    Severity   string         `json:\"severity\"` \u002F\u002F critical | high | medium | low\n",[1321,2989,2990],{"class":1323,"line":181},[1321,2991,2992],{},"    Payload    map[string]any `json:\"payload\"`\n",[1321,2994,2995],{"class":1323,"line":186},[1321,2996,2997],{},"    ReceivedAt time.Time      `json:\"receivedAt\"`\n",[1321,2999,3000],{"class":1323,"line":191},[1321,3001,3002],{},"}\n",[1321,3004,3005],{"class":1323,"line":197},[1321,3006,1384],{"emptyLinePlaceholder":1383},[1321,3008,3009],{"class":1323,"line":203},[1321,3010,3011],{},"\u002F\u002F EnrichedWebhook is the output of the enrich_webhook transform node.\n",[1321,3013,3014],{"class":1323,"line":214},[1321,3015,3016],{},"type EnrichedWebhook struct {\n",[1321,3018,3019],{"class":1323,"line":223},[1321,3020,3021],{},"    WebhookID   string         `json:\"webhookId\"`\n",[1321,3023,3024],{"class":1323,"line":229},[1321,3025,3026],{},"    Source      string         `json:\"source\"`\n",[1321,3028,3029],{"class":1323,"line":234},[1321,3030,3031],{},"    Severity    string         `json:\"severity\"`\n",[1321,3033,3034],{"class":1323,"line":240},[1321,3035,3036],{},"    Payload     map[string]any `json:\"payload\"`\n",[1321,3038,3039],{"class":1323,"line":246},[1321,3040,3041],{},"    TeamOwner   string         `json:\"teamOwner\"`\n",[1321,3043,3044],{"class":1323,"line":251},[1321,3045,3046],{},"    ServiceSlug string         `json:\"serviceSlug\"`\n",[1321,3048,3049],{"class":1323,"line":257},[1321,3050,3051],{},"    ReceivedAt  time.Time      `json:\"receivedAt\"`\n",[1321,3053,3054],{"class":1323,"line":1538},[1321,3055,3002],{},[1321,3057,3058],{"class":1323,"line":1543},[1321,3059,1384],{"emptyLinePlaceholder":1383},[1321,3061,3062],{"class":1323,"line":271},[1321,3063,3064],{},"\u002F\u002F IncidentResult is the output of the dispatch_pagerduty action node.\n",[1321,3066,3067],{"class":1323,"line":277},[1321,3068,3069],{},"type IncidentResult struct {\n",[1321,3071,3072],{"class":1323,"line":282},[1321,3073,3074],{},"    IncidentID string `json:\"incidentId\"`\n",[1321,3076,3077],{"class":1323,"line":1578},[1321,3078,3079],{},"    URL        string `json:\"url\"`\n",[1321,3081,3082],{"class":1323,"line":1585},[1321,3083,3084],{},"    Status     string `json:\"status\"`\n",[1321,3086,3087],{"class":1323,"line":1602},[1321,3088,3002],{},[1056,3090,3092],{"id":3091},"eventsgo-event-definitions","events.go — event definitions",[1312,3094,3096],{"className":2940,"code":3095,"language":2942,"meta":1317,"style":1317},"package alerts\n\nimport \"github.com\u002Fredelay\u002Fgo-events\u002Ftyped\"\n\n\u002F\u002F WebhookReceivedEvent corresponds to the IngestWebhook source node.\n\u002F\u002F Publishing this event is what \"triggers\" the flow.\nvar WebhookReceivedEvent = typed.EventDefinition[RawWebhook]{\n    Name:       \"webhook.received\",\n    EntityType: \"webhook\",\n    Action:     \"received\",\n    Topic:      \"webhooks.received\",\n}\n\n\u002F\u002F WebhookAlertedEvent marks the end of a successful alert dispatch.\nvar WebhookAlertedEvent = typed.EventDefinition[EnrichedWebhook]{\n    Name:       \"webhook.alerted\",\n    EntityType: \"webhook\",\n    Action:     \"alerted\",\n    Topic:      \"webhooks.alerted\",\n}\n",[1029,3097,3098,3102,3106,3111,3115,3120,3125,3130,3135,3140,3145,3150,3154,3158,3163,3168,3173,3177,3182,3187],{"__ignoreMap":1317},[1321,3099,3100],{"class":1323,"line":10},[1321,3101,2949],{},[1321,3103,3104],{"class":1323,"line":16},[1321,3105,1384],{"emptyLinePlaceholder":1383},[1321,3107,3108],{"class":1323,"line":22},[1321,3109,3110],{},"import \"github.com\u002Fredelay\u002Fgo-events\u002Ftyped\"\n",[1321,3112,3113],{"class":1323,"line":51},[1321,3114,1384],{"emptyLinePlaceholder":1383},[1321,3116,3117],{"class":1323,"line":57},[1321,3118,3119],{},"\u002F\u002F WebhookReceivedEvent corresponds to the IngestWebhook source node.\n",[1321,3121,3122],{"class":1323,"line":102},[1321,3123,3124],{},"\u002F\u002F Publishing this event is what \"triggers\" the flow.\n",[1321,3126,3127],{"class":1323,"line":108},[1321,3128,3129],{},"var WebhookReceivedEvent = typed.EventDefinition[RawWebhook]{\n",[1321,3131,3132],{"class":1323,"line":114},[1321,3133,3134],{},"    Name:       \"webhook.received\",\n",[1321,3136,3137],{"class":1323,"line":120},[1321,3138,3139],{},"    EntityType: \"webhook\",\n",[1321,3141,3142],{"class":1323,"line":181},[1321,3143,3144],{},"    Action:     \"received\",\n",[1321,3146,3147],{"class":1323,"line":186},[1321,3148,3149],{},"    Topic:      \"webhooks.received\",\n",[1321,3151,3152],{"class":1323,"line":191},[1321,3153,3002],{},[1321,3155,3156],{"class":1323,"line":197},[1321,3157,1384],{"emptyLinePlaceholder":1383},[1321,3159,3160],{"class":1323,"line":203},[1321,3161,3162],{},"\u002F\u002F WebhookAlertedEvent marks the end of a successful alert dispatch.\n",[1321,3164,3165],{"class":1323,"line":214},[1321,3166,3167],{},"var WebhookAlertedEvent = typed.EventDefinition[EnrichedWebhook]{\n",[1321,3169,3170],{"class":1323,"line":223},[1321,3171,3172],{},"    Name:       \"webhook.alerted\",\n",[1321,3174,3175],{"class":1323,"line":229},[1321,3176,3139],{},[1321,3178,3179],{"class":1323,"line":234},[1321,3180,3181],{},"    Action:     \"alerted\",\n",[1321,3183,3184],{"class":1323,"line":240},[1321,3185,3186],{},"    Topic:      \"webhooks.alerted\",\n",[1321,3188,3189],{"class":1323,"line":246},[1321,3190,3002],{},[1056,3192,3194,3195,3198],{"id":3193},"nodessourcego-ingest_webhook-kind-source","nodes\u002Fsource.go — ",[1029,3196,3197],{},"ingest_webhook"," (kind: source)",[974,3200,3201,3202,3204],{},"A ",[1029,3203,1083],{}," node is an entry point. In Redelay it's an HTTP handler that validates input and publishes the typed event.",[1312,3206,3208],{"className":2940,"code":3207,"language":2942,"meta":1317,"style":1317},"package nodes\n\nimport (\n    \"net\u002Fhttp\"\n    \"time\"\n\n    \"github.com\u002Fredelay\u002Fgo-events\u002Ftyped\"\n    \"github.com\u002Fredelay\u002Fgo-framework\u002Fserver\u002Fhttputil\"\n    \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\"\n    \"github.com\u002Fmyorg\u002Fmyapp\u002Fmodules\u002Falerts\"\n)\n\ntype IngestWebhookHandler struct {\n    eventBus modules.EventBus\n}\n\nfunc NewIngestWebhookHandler(bus modules.EventBus) *IngestWebhookHandler {\n    return &IngestWebhookHandler{eventBus: bus}\n}\n\n\u002F\u002F Handle is the HTTP POST \u002Fwebhooks endpoint — the source node entry.\nfunc (h *IngestWebhookHandler) Handle(w http.ResponseWriter, r *http.Request) {\n    var raw alerts.RawWebhook\n    if err := httputil.ReadJSON(r, &raw); err != nil {\n        httputil.WriteError(w, http.StatusBadRequest, \"invalid payload\", err)\n        return\n    }\n    if raw.WebhookID == \"\" || raw.Source == \"\" || raw.Severity == \"\" {\n        httputil.WriteError(w, http.StatusUnprocessableEntity, \"missing required fields\", nil)\n        return\n    }\n    raw.ReceivedAt = time.Now().UTC()\n\n    \u002F\u002F Publishing the event is all this node does — Redelay routes it.\n    msg, err := alerts.WebhookReceivedEvent.NewMessage(\n        raw.WebhookID,\n        typed.Actor{Type: typed.ActorTypeSystem, ID: \"alerts\"},\n        raw,\n    )\n    if err != nil {\n        httputil.WriteError(w, http.StatusInternalServerError, \"event creation failed\", err)\n        return\n    }\n    if err := h.eventBus.Publish(r.Context(), msg); err != nil {\n        httputil.WriteError(w, http.StatusInternalServerError, \"publish failed\", err)\n        return\n    }\n\n    httputil.WriteJSON(w, http.StatusAccepted, map[string]string{\n        \"webhookId\": raw.WebhookID,\n        \"status\":    \"accepted\",\n    })\n}\n",[1029,3209,3210,3215,3219,3224,3229,3234,3238,3243,3248,3253,3258,3263,3267,3272,3277,3281,3285,3290,3295,3299,3303,3308,3313,3318,3323,3328,3333,3338,3343,3348,3352,3356,3361,3365,3370,3375,3380,3385,3390,3395,3400,3405,3409,3413,3418,3423,3427,3431,3435,3440,3445,3450,3455],{"__ignoreMap":1317},[1321,3211,3212],{"class":1323,"line":10},[1321,3213,3214],{},"package nodes\n",[1321,3216,3217],{"class":1323,"line":16},[1321,3218,1384],{"emptyLinePlaceholder":1383},[1321,3220,3221],{"class":1323,"line":22},[1321,3222,3223],{},"import (\n",[1321,3225,3226],{"class":1323,"line":51},[1321,3227,3228],{},"    \"net\u002Fhttp\"\n",[1321,3230,3231],{"class":1323,"line":57},[1321,3232,3233],{},"    \"time\"\n",[1321,3235,3236],{"class":1323,"line":102},[1321,3237,1384],{"emptyLinePlaceholder":1383},[1321,3239,3240],{"class":1323,"line":108},[1321,3241,3242],{},"    \"github.com\u002Fredelay\u002Fgo-events\u002Ftyped\"\n",[1321,3244,3245],{"class":1323,"line":114},[1321,3246,3247],{},"    \"github.com\u002Fredelay\u002Fgo-framework\u002Fserver\u002Fhttputil\"\n",[1321,3249,3250],{"class":1323,"line":120},[1321,3251,3252],{},"    \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\"\n",[1321,3254,3255],{"class":1323,"line":181},[1321,3256,3257],{},"    \"github.com\u002Fmyorg\u002Fmyapp\u002Fmodules\u002Falerts\"\n",[1321,3259,3260],{"class":1323,"line":186},[1321,3261,3262],{},")\n",[1321,3264,3265],{"class":1323,"line":191},[1321,3266,1384],{"emptyLinePlaceholder":1383},[1321,3268,3269],{"class":1323,"line":197},[1321,3270,3271],{},"type IngestWebhookHandler struct {\n",[1321,3273,3274],{"class":1323,"line":203},[1321,3275,3276],{},"    eventBus modules.EventBus\n",[1321,3278,3279],{"class":1323,"line":214},[1321,3280,3002],{},[1321,3282,3283],{"class":1323,"line":223},[1321,3284,1384],{"emptyLinePlaceholder":1383},[1321,3286,3287],{"class":1323,"line":229},[1321,3288,3289],{},"func NewIngestWebhookHandler(bus modules.EventBus) *IngestWebhookHandler {\n",[1321,3291,3292],{"class":1323,"line":234},[1321,3293,3294],{},"    return &IngestWebhookHandler{eventBus: bus}\n",[1321,3296,3297],{"class":1323,"line":240},[1321,3298,3002],{},[1321,3300,3301],{"class":1323,"line":246},[1321,3302,1384],{"emptyLinePlaceholder":1383},[1321,3304,3305],{"class":1323,"line":251},[1321,3306,3307],{},"\u002F\u002F Handle is the HTTP POST \u002Fwebhooks endpoint — the source node entry.\n",[1321,3309,3310],{"class":1323,"line":257},[1321,3311,3312],{},"func (h *IngestWebhookHandler) Handle(w http.ResponseWriter, r *http.Request) {\n",[1321,3314,3315],{"class":1323,"line":1538},[1321,3316,3317],{},"    var raw alerts.RawWebhook\n",[1321,3319,3320],{"class":1323,"line":1543},[1321,3321,3322],{},"    if err := httputil.ReadJSON(r, &raw); err != nil {\n",[1321,3324,3325],{"class":1323,"line":271},[1321,3326,3327],{},"        httputil.WriteError(w, http.StatusBadRequest, \"invalid payload\", err)\n",[1321,3329,3330],{"class":1323,"line":277},[1321,3331,3332],{},"        return\n",[1321,3334,3335],{"class":1323,"line":282},[1321,3336,3337],{},"    }\n",[1321,3339,3340],{"class":1323,"line":1578},[1321,3341,3342],{},"    if raw.WebhookID == \"\" || raw.Source == \"\" || raw.Severity == \"\" {\n",[1321,3344,3345],{"class":1323,"line":1585},[1321,3346,3347],{},"        httputil.WriteError(w, http.StatusUnprocessableEntity, \"missing required fields\", nil)\n",[1321,3349,3350],{"class":1323,"line":1602},[1321,3351,3332],{},[1321,3353,3354],{"class":1323,"line":1609},[1321,3355,3337],{},[1321,3357,3358],{"class":1323,"line":1628},[1321,3359,3360],{},"    raw.ReceivedAt = time.Now().UTC()\n",[1321,3362,3363],{"class":1323,"line":1633},[1321,3364,1384],{"emptyLinePlaceholder":1383},[1321,3366,3367],{"class":1323,"line":1641},[1321,3368,3369],{},"    \u002F\u002F Publishing the event is all this node does — Redelay routes it.\n",[1321,3371,3372],{"class":1323,"line":1651},[1321,3373,3374],{},"    msg, err := alerts.WebhookReceivedEvent.NewMessage(\n",[1321,3376,3377],{"class":1323,"line":1661},[1321,3378,3379],{},"        raw.WebhookID,\n",[1321,3381,3382],{"class":1323,"line":1671},[1321,3383,3384],{},"        typed.Actor{Type: typed.ActorTypeSystem, ID: \"alerts\"},\n",[1321,3386,3387],{"class":1323,"line":1678},[1321,3388,3389],{},"        raw,\n",[1321,3391,3392],{"class":1323,"line":1695},[1321,3393,3394],{},"    )\n",[1321,3396,3397],{"class":1323,"line":1702},[1321,3398,3399],{},"    if err != nil {\n",[1321,3401,3402],{"class":1323,"line":1720},[1321,3403,3404],{},"        httputil.WriteError(w, http.StatusInternalServerError, \"event creation failed\", err)\n",[1321,3406,3407],{"class":1323,"line":1725},[1321,3408,3332],{},[1321,3410,3411],{"class":1323,"line":1733},[1321,3412,3337],{},[1321,3414,3415],{"class":1323,"line":1743},[1321,3416,3417],{},"    if err := h.eventBus.Publish(r.Context(), msg); err != nil {\n",[1321,3419,3420],{"class":1323,"line":1752},[1321,3421,3422],{},"        httputil.WriteError(w, http.StatusInternalServerError, \"publish failed\", err)\n",[1321,3424,3425],{"class":1323,"line":1762},[1321,3426,3332],{},[1321,3428,3429],{"class":1323,"line":1769},[1321,3430,3337],{},[1321,3432,3433],{"class":1323,"line":1786},[1321,3434,1384],{"emptyLinePlaceholder":1383},[1321,3436,3437],{"class":1323,"line":1791},[1321,3438,3439],{},"    httputil.WriteJSON(w, http.StatusAccepted, map[string]string{\n",[1321,3441,3442],{"class":1323,"line":1799},[1321,3443,3444],{},"        \"webhookId\": raw.WebhookID,\n",[1321,3446,3447],{"class":1323,"line":1809},[1321,3448,3449],{},"        \"status\":    \"accepted\",\n",[1321,3451,3452],{"class":1323,"line":1819},[1321,3453,3454],{},"    })\n",[1321,3456,3457],{"class":1323,"line":1829},[1321,3458,3002],{},[1056,3460,3462,3463,3466],{"id":3461},"nodesroutergo-filter_by_severity-kind-router","nodes\u002Frouter.go — ",[1029,3464,3465],{},"filter_by_severity"," (kind: router)",[974,3468,3201,3469,3471],{},[1029,3470,1116],{}," node decides which downstream path receives the packet. No side effects.",[1312,3473,3475],{"className":2940,"code":3474,"language":2942,"meta":1317,"style":1317},"package nodes\n\nimport (\n    \"context\"\n\n    \"github.com\u002Fredelay\u002Fgo-events\u002Feventbus\"\n    \"github.com\u002Fredelay\u002Fgo-events\u002Ftyped\"\n    \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\"\n    \"github.com\u002Fmyorg\u002Fmyapp\u002Fmodules\u002Falerts\"\n)\n\n\u002F\u002F FilterBySeverityNode subscribes to WebhookReceivedEvent and re-publishes\n\u002F\u002F to AlertPath or ArchivePath based on severity.\ntype FilterBySeverityNode struct {\n    alertEvent   typed.EventDefinition[alerts.RawWebhook]\n    archiveEvent typed.EventDefinition[alerts.RawWebhook]\n    eventBus     modules.EventBus\n}\n\n\u002F\u002F Route returns the output port name — \"alert\" or \"archive\".\n\u002F\u002F This is the pure routing logic; publishing happens in the subscriber wrapper.\nfunc (n *FilterBySeverityNode) Route(ctx context.Context, webhook *alerts.RawWebhook) string {\n    switch webhook.Severity {\n    case \"critical\", \"high\":\n        return \"alert\"\n    default:\n        return \"archive\"\n    }\n}\n",[1029,3476,3477,3481,3485,3489,3494,3498,3503,3507,3511,3515,3519,3523,3528,3533,3538,3543,3548,3553,3557,3561,3566,3571,3576,3581,3586,3591,3596,3601,3605],{"__ignoreMap":1317},[1321,3478,3479],{"class":1323,"line":10},[1321,3480,3214],{},[1321,3482,3483],{"class":1323,"line":16},[1321,3484,1384],{"emptyLinePlaceholder":1383},[1321,3486,3487],{"class":1323,"line":22},[1321,3488,3223],{},[1321,3490,3491],{"class":1323,"line":51},[1321,3492,3493],{},"    \"context\"\n",[1321,3495,3496],{"class":1323,"line":57},[1321,3497,1384],{"emptyLinePlaceholder":1383},[1321,3499,3500],{"class":1323,"line":102},[1321,3501,3502],{},"    \"github.com\u002Fredelay\u002Fgo-events\u002Feventbus\"\n",[1321,3504,3505],{"class":1323,"line":108},[1321,3506,3242],{},[1321,3508,3509],{"class":1323,"line":114},[1321,3510,3252],{},[1321,3512,3513],{"class":1323,"line":120},[1321,3514,3257],{},[1321,3516,3517],{"class":1323,"line":181},[1321,3518,3262],{},[1321,3520,3521],{"class":1323,"line":186},[1321,3522,1384],{"emptyLinePlaceholder":1383},[1321,3524,3525],{"class":1323,"line":191},[1321,3526,3527],{},"\u002F\u002F FilterBySeverityNode subscribes to WebhookReceivedEvent and re-publishes\n",[1321,3529,3530],{"class":1323,"line":197},[1321,3531,3532],{},"\u002F\u002F to AlertPath or ArchivePath based on severity.\n",[1321,3534,3535],{"class":1323,"line":203},[1321,3536,3537],{},"type FilterBySeverityNode struct {\n",[1321,3539,3540],{"class":1323,"line":214},[1321,3541,3542],{},"    alertEvent   typed.EventDefinition[alerts.RawWebhook]\n",[1321,3544,3545],{"class":1323,"line":223},[1321,3546,3547],{},"    archiveEvent typed.EventDefinition[alerts.RawWebhook]\n",[1321,3549,3550],{"class":1323,"line":229},[1321,3551,3552],{},"    eventBus     modules.EventBus\n",[1321,3554,3555],{"class":1323,"line":234},[1321,3556,3002],{},[1321,3558,3559],{"class":1323,"line":240},[1321,3560,1384],{"emptyLinePlaceholder":1383},[1321,3562,3563],{"class":1323,"line":246},[1321,3564,3565],{},"\u002F\u002F Route returns the output port name — \"alert\" or \"archive\".\n",[1321,3567,3568],{"class":1323,"line":251},[1321,3569,3570],{},"\u002F\u002F This is the pure routing logic; publishing happens in the subscriber wrapper.\n",[1321,3572,3573],{"class":1323,"line":257},[1321,3574,3575],{},"func (n *FilterBySeverityNode) Route(ctx context.Context, webhook *alerts.RawWebhook) string {\n",[1321,3577,3578],{"class":1323,"line":1538},[1321,3579,3580],{},"    switch webhook.Severity {\n",[1321,3582,3583],{"class":1323,"line":1543},[1321,3584,3585],{},"    case \"critical\", \"high\":\n",[1321,3587,3588],{"class":1323,"line":271},[1321,3589,3590],{},"        return \"alert\"\n",[1321,3592,3593],{"class":1323,"line":277},[1321,3594,3595],{},"    default:\n",[1321,3597,3598],{"class":1323,"line":282},[1321,3599,3600],{},"        return \"archive\"\n",[1321,3602,3603],{"class":1323,"line":1578},[1321,3604,3337],{},[1321,3606,3607],{"class":1323,"line":1585},[1321,3608,3002],{},[1056,3610,3612,3613,3616],{"id":3611},"nodestransformgo-enrich_webhook-kind-transform","nodes\u002Ftransform.go — ",[1029,3614,3615],{},"enrich_webhook"," (kind: transform)",[974,3618,3201,3619,3621],{},[1029,3620,1099],{}," node is a pure mapping function — takes one packet type, returns another.",[1312,3623,3625],{"className":2940,"code":3624,"language":2942,"meta":1317,"style":1317},"package nodes\n\nimport (\n    \"context\"\n\n    \"github.com\u002Fmyorg\u002Fmyapp\u002Fmodules\u002Falerts\"\n)\n\n\u002F\u002F EnrichWebhookNode enriches a RawWebhook with team ownership metadata.\n\u002F\u002F It is a pure transform — no side effects, no external calls beyond the\n\u002F\u002F config service lookup (which should be in-process or low-latency Redis).\ntype EnrichWebhookNode struct {\n    ownershipMap map[string]string \u002F\u002F source → teamOwner\n    slugMap      map[string]string \u002F\u002F source → serviceSlug\n}\n\n\u002F\u002F Transform is the core function: RawWebhook → EnrichedWebhook.\n\u002F\u002F If called via the event bus, wrap this in a typed subscriber.\nfunc (n *EnrichWebhookNode) Transform(ctx context.Context, raw *alerts.RawWebhook) (*alerts.EnrichedWebhook, error) {\n    teamOwner, ok := n.ownershipMap[raw.Source]\n    if !ok {\n        teamOwner = \"platform\"\n    }\n    serviceSlug, ok := n.slugMap[raw.Source]\n    if !ok {\n        serviceSlug = raw.Source\n    }\n\n    return &alerts.EnrichedWebhook{\n        WebhookID:   raw.WebhookID,\n        Source:      raw.Source,\n        Severity:    raw.Severity,\n        Payload:     raw.Payload,\n        TeamOwner:   teamOwner,\n        ServiceSlug: serviceSlug,\n        ReceivedAt:  raw.ReceivedAt,\n    }, nil\n}\n",[1029,3626,3627,3631,3635,3639,3643,3647,3651,3655,3659,3664,3669,3674,3679,3684,3689,3693,3697,3702,3707,3712,3717,3722,3727,3731,3736,3740,3745,3749,3753,3758,3763,3768,3773,3778,3783,3788,3793,3798],{"__ignoreMap":1317},[1321,3628,3629],{"class":1323,"line":10},[1321,3630,3214],{},[1321,3632,3633],{"class":1323,"line":16},[1321,3634,1384],{"emptyLinePlaceholder":1383},[1321,3636,3637],{"class":1323,"line":22},[1321,3638,3223],{},[1321,3640,3641],{"class":1323,"line":51},[1321,3642,3493],{},[1321,3644,3645],{"class":1323,"line":57},[1321,3646,1384],{"emptyLinePlaceholder":1383},[1321,3648,3649],{"class":1323,"line":102},[1321,3650,3257],{},[1321,3652,3653],{"class":1323,"line":108},[1321,3654,3262],{},[1321,3656,3657],{"class":1323,"line":114},[1321,3658,1384],{"emptyLinePlaceholder":1383},[1321,3660,3661],{"class":1323,"line":120},[1321,3662,3663],{},"\u002F\u002F EnrichWebhookNode enriches a RawWebhook with team ownership metadata.\n",[1321,3665,3666],{"class":1323,"line":181},[1321,3667,3668],{},"\u002F\u002F It is a pure transform — no side effects, no external calls beyond the\n",[1321,3670,3671],{"class":1323,"line":186},[1321,3672,3673],{},"\u002F\u002F config service lookup (which should be in-process or low-latency Redis).\n",[1321,3675,3676],{"class":1323,"line":191},[1321,3677,3678],{},"type EnrichWebhookNode struct {\n",[1321,3680,3681],{"class":1323,"line":197},[1321,3682,3683],{},"    ownershipMap map[string]string \u002F\u002F source → teamOwner\n",[1321,3685,3686],{"class":1323,"line":203},[1321,3687,3688],{},"    slugMap      map[string]string \u002F\u002F source → serviceSlug\n",[1321,3690,3691],{"class":1323,"line":214},[1321,3692,3002],{},[1321,3694,3695],{"class":1323,"line":223},[1321,3696,1384],{"emptyLinePlaceholder":1383},[1321,3698,3699],{"class":1323,"line":229},[1321,3700,3701],{},"\u002F\u002F Transform is the core function: RawWebhook → EnrichedWebhook.\n",[1321,3703,3704],{"class":1323,"line":234},[1321,3705,3706],{},"\u002F\u002F If called via the event bus, wrap this in a typed subscriber.\n",[1321,3708,3709],{"class":1323,"line":240},[1321,3710,3711],{},"func (n *EnrichWebhookNode) Transform(ctx context.Context, raw *alerts.RawWebhook) (*alerts.EnrichedWebhook, error) {\n",[1321,3713,3714],{"class":1323,"line":246},[1321,3715,3716],{},"    teamOwner, ok := n.ownershipMap[raw.Source]\n",[1321,3718,3719],{"class":1323,"line":251},[1321,3720,3721],{},"    if !ok {\n",[1321,3723,3724],{"class":1323,"line":257},[1321,3725,3726],{},"        teamOwner = \"platform\"\n",[1321,3728,3729],{"class":1323,"line":1538},[1321,3730,3337],{},[1321,3732,3733],{"class":1323,"line":1543},[1321,3734,3735],{},"    serviceSlug, ok := n.slugMap[raw.Source]\n",[1321,3737,3738],{"class":1323,"line":271},[1321,3739,3721],{},[1321,3741,3742],{"class":1323,"line":277},[1321,3743,3744],{},"        serviceSlug = raw.Source\n",[1321,3746,3747],{"class":1323,"line":282},[1321,3748,3337],{},[1321,3750,3751],{"class":1323,"line":1578},[1321,3752,1384],{"emptyLinePlaceholder":1383},[1321,3754,3755],{"class":1323,"line":1585},[1321,3756,3757],{},"    return &alerts.EnrichedWebhook{\n",[1321,3759,3760],{"class":1323,"line":1602},[1321,3761,3762],{},"        WebhookID:   raw.WebhookID,\n",[1321,3764,3765],{"class":1323,"line":1609},[1321,3766,3767],{},"        Source:      raw.Source,\n",[1321,3769,3770],{"class":1323,"line":1628},[1321,3771,3772],{},"        Severity:    raw.Severity,\n",[1321,3774,3775],{"class":1323,"line":1633},[1321,3776,3777],{},"        Payload:     raw.Payload,\n",[1321,3779,3780],{"class":1323,"line":1641},[1321,3781,3782],{},"        TeamOwner:   teamOwner,\n",[1321,3784,3785],{"class":1323,"line":1651},[1321,3786,3787],{},"        ServiceSlug: serviceSlug,\n",[1321,3789,3790],{"class":1323,"line":1661},[1321,3791,3792],{},"        ReceivedAt:  raw.ReceivedAt,\n",[1321,3794,3795],{"class":1323,"line":1671},[1321,3796,3797],{},"    }, nil\n",[1321,3799,3800],{"class":1323,"line":1678},[1321,3801,3002],{},[1056,3803,3805,3806,3809],{"id":3804},"nodesaction_pdgo-dispatch_pagerduty-kind-action","nodes\u002Faction_pd.go — ",[1029,3807,3808],{},"dispatch_pagerduty"," (kind: action)",[974,3811,3812,3813,3815],{},"An ",[1029,3814,1142],{}," node performs an external side effect. It must be idempotent — Redelay can retry on failure.",[1312,3817,3819],{"className":2940,"code":3818,"language":2942,"meta":1317,"style":1317},"package nodes\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"net\u002Fhttp\"\n\n    \"github.com\u002Fmyorg\u002Fmyapp\u002Fmodules\u002Falerts\"\n)\n\n\u002F\u002F DispatchPagerDutyNode opens a PagerDuty incident.\n\u002F\u002F The edge declares idempotencyKey = \"{{payload.webhookId}}-pagerduty\",\n\u002F\u002F so duplicate calls are safe — PagerDuty deduplicates by dedup_key.\ntype DispatchPagerDutyNode struct {\n    routingKey string\n    httpClient *http.Client\n}\n\nfunc (n *DispatchPagerDutyNode) Handle(ctx context.Context, enriched *alerts.EnrichedWebhook) (*alerts.IncidentResult, error) {\n    dedupKey := fmt.Sprintf(\"%s-pagerduty\", enriched.WebhookID)\n\n    body := map[string]any{\n        \"routing_key\":  n.routingKey,\n        \"dedup_key\":    dedupKey,\n        \"event_action\": \"trigger\",\n        \"payload\": map[string]any{\n            \"summary\":  fmt.Sprintf(\"[%s] %s — %s\", enriched.Severity, enriched.TeamOwner, enriched.Source),\n            \"severity\": enriched.Severity,\n            \"source\":   enriched.ServiceSlug,\n            \"custom_details\": map[string]any{\n                \"webhookId\":   enriched.WebhookID,\n                \"teamOwner\":   enriched.TeamOwner,\n                \"serviceSlug\": enriched.ServiceSlug,\n            },\n        },\n    }\n    _ = body \u002F\u002F call PagerDuty Events API v2 here\n\n    return &alerts.IncidentResult{\n        IncidentID: dedupKey,\n        Status:     \"triggered\",\n    }, nil\n}\n",[1029,3820,3821,3825,3829,3833,3837,3842,3846,3850,3854,3858,3862,3867,3872,3877,3882,3887,3892,3896,3900,3905,3910,3914,3919,3924,3929,3934,3939,3944,3949,3954,3959,3964,3969,3974,3979,3984,3988,3993,3997,4002,4007,4012,4016],{"__ignoreMap":1317},[1321,3822,3823],{"class":1323,"line":10},[1321,3824,3214],{},[1321,3826,3827],{"class":1323,"line":16},[1321,3828,1384],{"emptyLinePlaceholder":1383},[1321,3830,3831],{"class":1323,"line":22},[1321,3832,3223],{},[1321,3834,3835],{"class":1323,"line":51},[1321,3836,3493],{},[1321,3838,3839],{"class":1323,"line":57},[1321,3840,3841],{},"    \"fmt\"\n",[1321,3843,3844],{"class":1323,"line":102},[1321,3845,3228],{},[1321,3847,3848],{"class":1323,"line":108},[1321,3849,1384],{"emptyLinePlaceholder":1383},[1321,3851,3852],{"class":1323,"line":114},[1321,3853,3257],{},[1321,3855,3856],{"class":1323,"line":120},[1321,3857,3262],{},[1321,3859,3860],{"class":1323,"line":181},[1321,3861,1384],{"emptyLinePlaceholder":1383},[1321,3863,3864],{"class":1323,"line":186},[1321,3865,3866],{},"\u002F\u002F DispatchPagerDutyNode opens a PagerDuty incident.\n",[1321,3868,3869],{"class":1323,"line":191},[1321,3870,3871],{},"\u002F\u002F The edge declares idempotencyKey = \"{{payload.webhookId}}-pagerduty\",\n",[1321,3873,3874],{"class":1323,"line":197},[1321,3875,3876],{},"\u002F\u002F so duplicate calls are safe — PagerDuty deduplicates by dedup_key.\n",[1321,3878,3879],{"class":1323,"line":203},[1321,3880,3881],{},"type DispatchPagerDutyNode struct {\n",[1321,3883,3884],{"class":1323,"line":214},[1321,3885,3886],{},"    routingKey string\n",[1321,3888,3889],{"class":1323,"line":223},[1321,3890,3891],{},"    httpClient *http.Client\n",[1321,3893,3894],{"class":1323,"line":229},[1321,3895,3002],{},[1321,3897,3898],{"class":1323,"line":234},[1321,3899,1384],{"emptyLinePlaceholder":1383},[1321,3901,3902],{"class":1323,"line":240},[1321,3903,3904],{},"func (n *DispatchPagerDutyNode) Handle(ctx context.Context, enriched *alerts.EnrichedWebhook) (*alerts.IncidentResult, error) {\n",[1321,3906,3907],{"class":1323,"line":246},[1321,3908,3909],{},"    dedupKey := fmt.Sprintf(\"%s-pagerduty\", enriched.WebhookID)\n",[1321,3911,3912],{"class":1323,"line":251},[1321,3913,1384],{"emptyLinePlaceholder":1383},[1321,3915,3916],{"class":1323,"line":257},[1321,3917,3918],{},"    body := map[string]any{\n",[1321,3920,3921],{"class":1323,"line":1538},[1321,3922,3923],{},"        \"routing_key\":  n.routingKey,\n",[1321,3925,3926],{"class":1323,"line":1543},[1321,3927,3928],{},"        \"dedup_key\":    dedupKey,\n",[1321,3930,3931],{"class":1323,"line":271},[1321,3932,3933],{},"        \"event_action\": \"trigger\",\n",[1321,3935,3936],{"class":1323,"line":277},[1321,3937,3938],{},"        \"payload\": map[string]any{\n",[1321,3940,3941],{"class":1323,"line":282},[1321,3942,3943],{},"            \"summary\":  fmt.Sprintf(\"[%s] %s — %s\", enriched.Severity, enriched.TeamOwner, enriched.Source),\n",[1321,3945,3946],{"class":1323,"line":1578},[1321,3947,3948],{},"            \"severity\": enriched.Severity,\n",[1321,3950,3951],{"class":1323,"line":1585},[1321,3952,3953],{},"            \"source\":   enriched.ServiceSlug,\n",[1321,3955,3956],{"class":1323,"line":1602},[1321,3957,3958],{},"            \"custom_details\": map[string]any{\n",[1321,3960,3961],{"class":1323,"line":1609},[1321,3962,3963],{},"                \"webhookId\":   enriched.WebhookID,\n",[1321,3965,3966],{"class":1323,"line":1628},[1321,3967,3968],{},"                \"teamOwner\":   enriched.TeamOwner,\n",[1321,3970,3971],{"class":1323,"line":1633},[1321,3972,3973],{},"                \"serviceSlug\": enriched.ServiceSlug,\n",[1321,3975,3976],{"class":1323,"line":1641},[1321,3977,3978],{},"            },\n",[1321,3980,3981],{"class":1323,"line":1651},[1321,3982,3983],{},"        },\n",[1321,3985,3986],{"class":1323,"line":1661},[1321,3987,3337],{},[1321,3989,3990],{"class":1323,"line":1671},[1321,3991,3992],{},"    _ = body \u002F\u002F call PagerDuty Events API v2 here\n",[1321,3994,3995],{"class":1323,"line":1678},[1321,3996,1384],{"emptyLinePlaceholder":1383},[1321,3998,3999],{"class":1323,"line":1695},[1321,4000,4001],{},"    return &alerts.IncidentResult{\n",[1321,4003,4004],{"class":1323,"line":1702},[1321,4005,4006],{},"        IncidentID: dedupKey,\n",[1321,4008,4009],{"class":1323,"line":1720},[1321,4010,4011],{},"        Status:     \"triggered\",\n",[1321,4013,4014],{"class":1323,"line":1725},[1321,4015,3797],{},[1321,4017,4018],{"class":1323,"line":1733},[1321,4019,3002],{},[1056,4021,4023,4024,4027],{"id":4022},"nodesterminalgo-archive_low_severity-kind-terminal","nodes\u002Fterminal.go — ",[1029,4025,4026],{},"archive_low_severity"," (kind: terminal)",[974,4029,3201,4030,4032],{},[1029,4031,1155],{}," node has no outputs. It logs, archives, or discards — and returns.",[1312,4034,4036],{"className":2940,"code":4035,"language":2942,"meta":1317,"style":1317},"package nodes\n\nimport (\n    \"context\"\n\n    \"go.mongodb.org\u002Fmongo-driver\u002Fmongo\"\n    \"github.com\u002Fmyorg\u002Fmyapp\u002Fmodules\u002Falerts\"\n)\n\n\u002F\u002F ArchiveLowSeverityNode writes the webhook to the archive collection.\n\u002F\u002F It is a terminal — nothing is published after this.\ntype ArchiveLowSeverityNode struct {\n    coll *mongo.Collection\n}\n\nfunc (n *ArchiveLowSeverityNode) Handle(ctx context.Context, webhook *alerts.RawWebhook) error {\n    _, err := n.coll.InsertOne(ctx, map[string]any{\n        \"webhookId\":  webhook.WebhookID,\n        \"source\":     webhook.Source,\n        \"severity\":   webhook.Severity,\n        \"payload\":    webhook.Payload,\n        \"receivedAt\": webhook.ReceivedAt,\n        \"archivedBy\": \"archive_low_severity\",\n    })\n    return err\n}\n",[1029,4037,4038,4042,4046,4050,4054,4058,4063,4067,4071,4075,4080,4085,4090,4095,4099,4103,4108,4113,4118,4123,4128,4133,4138,4143,4147,4152],{"__ignoreMap":1317},[1321,4039,4040],{"class":1323,"line":10},[1321,4041,3214],{},[1321,4043,4044],{"class":1323,"line":16},[1321,4045,1384],{"emptyLinePlaceholder":1383},[1321,4047,4048],{"class":1323,"line":22},[1321,4049,3223],{},[1321,4051,4052],{"class":1323,"line":51},[1321,4053,3493],{},[1321,4055,4056],{"class":1323,"line":57},[1321,4057,1384],{"emptyLinePlaceholder":1383},[1321,4059,4060],{"class":1323,"line":102},[1321,4061,4062],{},"    \"go.mongodb.org\u002Fmongo-driver\u002Fmongo\"\n",[1321,4064,4065],{"class":1323,"line":108},[1321,4066,3257],{},[1321,4068,4069],{"class":1323,"line":114},[1321,4070,3262],{},[1321,4072,4073],{"class":1323,"line":120},[1321,4074,1384],{"emptyLinePlaceholder":1383},[1321,4076,4077],{"class":1323,"line":181},[1321,4078,4079],{},"\u002F\u002F ArchiveLowSeverityNode writes the webhook to the archive collection.\n",[1321,4081,4082],{"class":1323,"line":186},[1321,4083,4084],{},"\u002F\u002F It is a terminal — nothing is published after this.\n",[1321,4086,4087],{"class":1323,"line":191},[1321,4088,4089],{},"type ArchiveLowSeverityNode struct {\n",[1321,4091,4092],{"class":1323,"line":197},[1321,4093,4094],{},"    coll *mongo.Collection\n",[1321,4096,4097],{"class":1323,"line":203},[1321,4098,3002],{},[1321,4100,4101],{"class":1323,"line":214},[1321,4102,1384],{"emptyLinePlaceholder":1383},[1321,4104,4105],{"class":1323,"line":223},[1321,4106,4107],{},"func (n *ArchiveLowSeverityNode) Handle(ctx context.Context, webhook *alerts.RawWebhook) error {\n",[1321,4109,4110],{"class":1323,"line":229},[1321,4111,4112],{},"    _, err := n.coll.InsertOne(ctx, map[string]any{\n",[1321,4114,4115],{"class":1323,"line":234},[1321,4116,4117],{},"        \"webhookId\":  webhook.WebhookID,\n",[1321,4119,4120],{"class":1323,"line":240},[1321,4121,4122],{},"        \"source\":     webhook.Source,\n",[1321,4124,4125],{"class":1323,"line":246},[1321,4126,4127],{},"        \"severity\":   webhook.Severity,\n",[1321,4129,4130],{"class":1323,"line":251},[1321,4131,4132],{},"        \"payload\":    webhook.Payload,\n",[1321,4134,4135],{"class":1323,"line":257},[1321,4136,4137],{},"        \"receivedAt\": webhook.ReceivedAt,\n",[1321,4139,4140],{"class":1323,"line":1538},[1321,4141,4142],{},"        \"archivedBy\": \"archive_low_severity\",\n",[1321,4144,4145],{"class":1323,"line":1543},[1321,4146,3454],{},[1321,4148,4149],{"class":1323,"line":271},[1321,4150,4151],{},"    return err\n",[1321,4153,4154],{"class":1323,"line":277},[1321,4155,3002],{},[994,4157],{},[997,4159,4161],{"id":4160},"_6-wire-everything-into-the-module","6. Wire everything into the module",[1312,4163,4165],{"className":2940,"code":4164,"language":2942,"meta":1317,"style":1317},"\u002F\u002F modules\u002Falerts\u002Fmodule.go\npackage alerts\n\nimport (\n    \"context\"\n\n    \"github.com\u002Fgo-chi\u002Fchi\u002Fv5\"\n    \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\"\n    \"github.com\u002Fmyorg\u002Fmyapp\u002Fmodules\u002Falerts\u002Fnodes\"\n)\n\nfunc init() {\n    modules.RegisterFactory(\"alerts\", func(deps modules.ModuleDeps) modules.Module {\n        return &Module{deps: deps}\n    })\n}\n\ntype Module struct {\n    deps modules.ModuleDeps\n\n    \u002F\u002F nodes\n    source   *nodes.IngestWebhookHandler\n    enricher *nodes.EnrichWebhookNode\n    pdNode   *nodes.DispatchPagerDutyNode\n    slNode   *nodes.DispatchSlackNode\n    archive  *nodes.ArchiveLowSeverityNode\n}\n\nfunc (m *Module) Manifest() modules.Manifest {\n    return modules.Manifest{\n        ID:          \"alerts\",\n        Name:        \"Webhook Alert Pipeline\",\n        Description: \"FlowDSL-defined webhook ingest → filter → enrich → dispatch.\",\n        Version:     \"1.0.0\",\n    }\n}\n\nfunc (m *Module) Configure(r *modules.Registry) {}\n\nfunc (m *Module) Startup(ctx context.Context) error {\n    cfg := DefaultConfig()\n\n    \u002F\u002F Instantiate nodes\n    m.source = nodes.NewIngestWebhookHandler(m.deps.EventBus)\n    m.enricher = nodes.NewEnrichWebhookNode(cfg.OwnershipMapPath)\n    m.pdNode = nodes.NewDispatchPagerDutyNode(cfg.PagerDutyRoutingKey)\n    m.slNode = nodes.NewDispatchSlackNode(cfg.SlackWebhookURL)\n\n    db := m.deps.MongoDB.Database(m.deps.Config.MongoDatabase)\n    m.archive = nodes.NewArchiveLowSeverityNode(db.Collection(cfg.ArchiveCollection))\n\n    \u002F\u002F Wire event subscriptions (the \"edges\" in Go)\n    \u002F\u002F edge: IngestWebhook → FilterBySeverity (direct — handled inline by the router)\n    \u002F\u002F edge: FilterBySeverity.archive → ArchiveLow (ephemeral via Redis stream)\n    \u002F\u002F edge: FilterBySeverity.alert → EnrichPayload → PagerDuty + Slack (durable via EventBus)\n\n    return nil\n}\n\nfunc (m *Module) Shutdown(ctx context.Context) error { return nil }\n\nfunc (m *Module) Routes(prefix string) func(chi.Router) {\n    return func(r chi.Router) {\n        r.Post(\"\u002Fwebhooks\", m.source.Handle)\n    }\n}\n",[1029,4166,4167,4172,4176,4180,4184,4188,4192,4197,4201,4206,4210,4214,4219,4224,4229,4233,4237,4241,4246,4251,4255,4260,4265,4270,4275,4280,4285,4289,4293,4298,4303,4308,4313,4318,4323,4327,4331,4335,4340,4344,4349,4354,4358,4363,4368,4373,4378,4383,4387,4392,4397,4401,4406,4411,4416,4421,4425,4430,4434,4438,4443,4447,4452,4457,4462,4466],{"__ignoreMap":1317},[1321,4168,4169],{"class":1323,"line":10},[1321,4170,4171],{},"\u002F\u002F modules\u002Falerts\u002Fmodule.go\n",[1321,4173,4174],{"class":1323,"line":16},[1321,4175,2949],{},[1321,4177,4178],{"class":1323,"line":22},[1321,4179,1384],{"emptyLinePlaceholder":1383},[1321,4181,4182],{"class":1323,"line":51},[1321,4183,3223],{},[1321,4185,4186],{"class":1323,"line":57},[1321,4187,3493],{},[1321,4189,4190],{"class":1323,"line":102},[1321,4191,1384],{"emptyLinePlaceholder":1383},[1321,4193,4194],{"class":1323,"line":108},[1321,4195,4196],{},"    \"github.com\u002Fgo-chi\u002Fchi\u002Fv5\"\n",[1321,4198,4199],{"class":1323,"line":114},[1321,4200,3252],{},[1321,4202,4203],{"class":1323,"line":120},[1321,4204,4205],{},"    \"github.com\u002Fmyorg\u002Fmyapp\u002Fmodules\u002Falerts\u002Fnodes\"\n",[1321,4207,4208],{"class":1323,"line":181},[1321,4209,3262],{},[1321,4211,4212],{"class":1323,"line":186},[1321,4213,1384],{"emptyLinePlaceholder":1383},[1321,4215,4216],{"class":1323,"line":191},[1321,4217,4218],{},"func init() {\n",[1321,4220,4221],{"class":1323,"line":197},[1321,4222,4223],{},"    modules.RegisterFactory(\"alerts\", func(deps modules.ModuleDeps) modules.Module {\n",[1321,4225,4226],{"class":1323,"line":203},[1321,4227,4228],{},"        return &Module{deps: deps}\n",[1321,4230,4231],{"class":1323,"line":214},[1321,4232,3454],{},[1321,4234,4235],{"class":1323,"line":223},[1321,4236,3002],{},[1321,4238,4239],{"class":1323,"line":229},[1321,4240,1384],{"emptyLinePlaceholder":1383},[1321,4242,4243],{"class":1323,"line":234},[1321,4244,4245],{},"type Module struct {\n",[1321,4247,4248],{"class":1323,"line":240},[1321,4249,4250],{},"    deps modules.ModuleDeps\n",[1321,4252,4253],{"class":1323,"line":246},[1321,4254,1384],{"emptyLinePlaceholder":1383},[1321,4256,4257],{"class":1323,"line":251},[1321,4258,4259],{},"    \u002F\u002F nodes\n",[1321,4261,4262],{"class":1323,"line":257},[1321,4263,4264],{},"    source   *nodes.IngestWebhookHandler\n",[1321,4266,4267],{"class":1323,"line":1538},[1321,4268,4269],{},"    enricher *nodes.EnrichWebhookNode\n",[1321,4271,4272],{"class":1323,"line":1543},[1321,4273,4274],{},"    pdNode   *nodes.DispatchPagerDutyNode\n",[1321,4276,4277],{"class":1323,"line":271},[1321,4278,4279],{},"    slNode   *nodes.DispatchSlackNode\n",[1321,4281,4282],{"class":1323,"line":277},[1321,4283,4284],{},"    archive  *nodes.ArchiveLowSeverityNode\n",[1321,4286,4287],{"class":1323,"line":282},[1321,4288,3002],{},[1321,4290,4291],{"class":1323,"line":1578},[1321,4292,1384],{"emptyLinePlaceholder":1383},[1321,4294,4295],{"class":1323,"line":1585},[1321,4296,4297],{},"func (m *Module) Manifest() modules.Manifest {\n",[1321,4299,4300],{"class":1323,"line":1602},[1321,4301,4302],{},"    return modules.Manifest{\n",[1321,4304,4305],{"class":1323,"line":1609},[1321,4306,4307],{},"        ID:          \"alerts\",\n",[1321,4309,4310],{"class":1323,"line":1628},[1321,4311,4312],{},"        Name:        \"Webhook Alert Pipeline\",\n",[1321,4314,4315],{"class":1323,"line":1633},[1321,4316,4317],{},"        Description: \"FlowDSL-defined webhook ingest → filter → enrich → dispatch.\",\n",[1321,4319,4320],{"class":1323,"line":1641},[1321,4321,4322],{},"        Version:     \"1.0.0\",\n",[1321,4324,4325],{"class":1323,"line":1651},[1321,4326,3337],{},[1321,4328,4329],{"class":1323,"line":1661},[1321,4330,3002],{},[1321,4332,4333],{"class":1323,"line":1671},[1321,4334,1384],{"emptyLinePlaceholder":1383},[1321,4336,4337],{"class":1323,"line":1678},[1321,4338,4339],{},"func (m *Module) Configure(r *modules.Registry) {}\n",[1321,4341,4342],{"class":1323,"line":1695},[1321,4343,1384],{"emptyLinePlaceholder":1383},[1321,4345,4346],{"class":1323,"line":1702},[1321,4347,4348],{},"func (m *Module) Startup(ctx context.Context) error {\n",[1321,4350,4351],{"class":1323,"line":1720},[1321,4352,4353],{},"    cfg := DefaultConfig()\n",[1321,4355,4356],{"class":1323,"line":1725},[1321,4357,1384],{"emptyLinePlaceholder":1383},[1321,4359,4360],{"class":1323,"line":1733},[1321,4361,4362],{},"    \u002F\u002F Instantiate nodes\n",[1321,4364,4365],{"class":1323,"line":1743},[1321,4366,4367],{},"    m.source = nodes.NewIngestWebhookHandler(m.deps.EventBus)\n",[1321,4369,4370],{"class":1323,"line":1752},[1321,4371,4372],{},"    m.enricher = nodes.NewEnrichWebhookNode(cfg.OwnershipMapPath)\n",[1321,4374,4375],{"class":1323,"line":1762},[1321,4376,4377],{},"    m.pdNode = nodes.NewDispatchPagerDutyNode(cfg.PagerDutyRoutingKey)\n",[1321,4379,4380],{"class":1323,"line":1769},[1321,4381,4382],{},"    m.slNode = nodes.NewDispatchSlackNode(cfg.SlackWebhookURL)\n",[1321,4384,4385],{"class":1323,"line":1786},[1321,4386,1384],{"emptyLinePlaceholder":1383},[1321,4388,4389],{"class":1323,"line":1791},[1321,4390,4391],{},"    db := m.deps.MongoDB.Database(m.deps.Config.MongoDatabase)\n",[1321,4393,4394],{"class":1323,"line":1799},[1321,4395,4396],{},"    m.archive = nodes.NewArchiveLowSeverityNode(db.Collection(cfg.ArchiveCollection))\n",[1321,4398,4399],{"class":1323,"line":1809},[1321,4400,1384],{"emptyLinePlaceholder":1383},[1321,4402,4403],{"class":1323,"line":1819},[1321,4404,4405],{},"    \u002F\u002F Wire event subscriptions (the \"edges\" in Go)\n",[1321,4407,4408],{"class":1323,"line":1829},[1321,4409,4410],{},"    \u002F\u002F edge: IngestWebhook → FilterBySeverity (direct — handled inline by the router)\n",[1321,4412,4413],{"class":1323,"line":1836},[1321,4414,4415],{},"    \u002F\u002F edge: FilterBySeverity.archive → ArchiveLow (ephemeral via Redis stream)\n",[1321,4417,4418],{"class":1323,"line":1853},[1321,4419,4420],{},"    \u002F\u002F edge: FilterBySeverity.alert → EnrichPayload → PagerDuty + Slack (durable via EventBus)\n",[1321,4422,4423],{"class":1323,"line":1858},[1321,4424,1384],{"emptyLinePlaceholder":1383},[1321,4426,4427],{"class":1323,"line":1865},[1321,4428,4429],{},"    return nil\n",[1321,4431,4432],{"class":1323,"line":1879},[1321,4433,3002],{},[1321,4435,4436],{"class":1323,"line":1890},[1321,4437,1384],{"emptyLinePlaceholder":1383},[1321,4439,4440],{"class":1323,"line":1898},[1321,4441,4442],{},"func (m *Module) Shutdown(ctx context.Context) error { return nil }\n",[1321,4444,4445],{"class":1323,"line":1909},[1321,4446,1384],{"emptyLinePlaceholder":1383},[1321,4448,4449],{"class":1323,"line":1920},[1321,4450,4451],{},"func (m *Module) Routes(prefix string) func(chi.Router) {\n",[1321,4453,4454],{"class":1323,"line":1925},[1321,4455,4456],{},"    return func(r chi.Router) {\n",[1321,4458,4459],{"class":1323,"line":1937},[1321,4460,4461],{},"        r.Post(\"\u002Fwebhooks\", m.source.Handle)\n",[1321,4463,4464],{"class":1323,"line":1947},[1321,4465,3337],{},[1321,4467,4468],{"class":1323,"line":1954},[1321,4469,3002],{},[994,4471],{},[997,4473,4475],{"id":4474},"_7-bootstrap-and-run","7. Bootstrap and run",[1312,4477,4479],{"className":2940,"code":4478,"language":2942,"meta":1317,"style":1317},"\u002F\u002F cmd\u002Fapi\u002Fmain.go\npackage main\n\nimport (\n    \"github.com\u002Fredelay\u002Fgo-framework\u002Fapp\"\n    \"github.com\u002Fredelay\u002Fgo-framework\u002Fserver\"\n    \"github.com\u002Fredelay\u002Fgo-events\u002Feventbus\"\n    \"github.com\u002Fredelay\u002Fgo-events\u002Ftransport\u002Fkafka\"\n\n    _ \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\u002Fhealth\"\n    _ \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\u002Fopenapi\"\n    _ \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\u002Fasyncapi\" \u002F\u002F serves \u002Fasyncapi.json\n    _ \"github.com\u002Fmyorg\u002Fmyapp\u002Fmodules\u002Falerts\"\n)\n\nfunc main() {\n    \u002F\u002F Wire Kafka transport\n    t, err := kafka.NewFromEnv()\n    if err != nil {\n        panic(err)\n    }\n    bus := eventbus.New(t)\n\n    \u002F\u002F Bootstrap Redelay\n    inst, err := app.Bootstrap(app.WithEventBus(bus))\n    if err != nil {\n        panic(err)\n    }\n\n    \u002F\u002F Run HTTP server\n    app.Run(inst, server.Default(inst))\n}\n",[1029,4480,4481,4486,4491,4495,4499,4504,4509,4513,4518,4522,4527,4532,4537,4542,4546,4550,4555,4560,4565,4569,4574,4578,4583,4587,4592,4597,4601,4605,4609,4613,4618,4623],{"__ignoreMap":1317},[1321,4482,4483],{"class":1323,"line":10},[1321,4484,4485],{},"\u002F\u002F cmd\u002Fapi\u002Fmain.go\n",[1321,4487,4488],{"class":1323,"line":16},[1321,4489,4490],{},"package main\n",[1321,4492,4493],{"class":1323,"line":22},[1321,4494,1384],{"emptyLinePlaceholder":1383},[1321,4496,4497],{"class":1323,"line":51},[1321,4498,3223],{},[1321,4500,4501],{"class":1323,"line":57},[1321,4502,4503],{},"    \"github.com\u002Fredelay\u002Fgo-framework\u002Fapp\"\n",[1321,4505,4506],{"class":1323,"line":102},[1321,4507,4508],{},"    \"github.com\u002Fredelay\u002Fgo-framework\u002Fserver\"\n",[1321,4510,4511],{"class":1323,"line":108},[1321,4512,3502],{},[1321,4514,4515],{"class":1323,"line":114},[1321,4516,4517],{},"    \"github.com\u002Fredelay\u002Fgo-events\u002Ftransport\u002Fkafka\"\n",[1321,4519,4520],{"class":1323,"line":120},[1321,4521,1384],{"emptyLinePlaceholder":1383},[1321,4523,4524],{"class":1323,"line":181},[1321,4525,4526],{},"    _ \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\u002Fhealth\"\n",[1321,4528,4529],{"class":1323,"line":186},[1321,4530,4531],{},"    _ \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\u002Fopenapi\"\n",[1321,4533,4534],{"class":1323,"line":191},[1321,4535,4536],{},"    _ \"github.com\u002Fredelay\u002Fgo-framework\u002Fmodules\u002Fasyncapi\" \u002F\u002F serves \u002Fasyncapi.json\n",[1321,4538,4539],{"class":1323,"line":197},[1321,4540,4541],{},"    _ \"github.com\u002Fmyorg\u002Fmyapp\u002Fmodules\u002Falerts\"\n",[1321,4543,4544],{"class":1323,"line":203},[1321,4545,3262],{},[1321,4547,4548],{"class":1323,"line":214},[1321,4549,1384],{"emptyLinePlaceholder":1383},[1321,4551,4552],{"class":1323,"line":223},[1321,4553,4554],{},"func main() {\n",[1321,4556,4557],{"class":1323,"line":229},[1321,4558,4559],{},"    \u002F\u002F Wire Kafka transport\n",[1321,4561,4562],{"class":1323,"line":234},[1321,4563,4564],{},"    t, err := kafka.NewFromEnv()\n",[1321,4566,4567],{"class":1323,"line":240},[1321,4568,3399],{},[1321,4570,4571],{"class":1323,"line":246},[1321,4572,4573],{},"        panic(err)\n",[1321,4575,4576],{"class":1323,"line":251},[1321,4577,3337],{},[1321,4579,4580],{"class":1323,"line":257},[1321,4581,4582],{},"    bus := eventbus.New(t)\n",[1321,4584,4585],{"class":1323,"line":1538},[1321,4586,1384],{"emptyLinePlaceholder":1383},[1321,4588,4589],{"class":1323,"line":1543},[1321,4590,4591],{},"    \u002F\u002F Bootstrap Redelay\n",[1321,4593,4594],{"class":1323,"line":271},[1321,4595,4596],{},"    inst, err := app.Bootstrap(app.WithEventBus(bus))\n",[1321,4598,4599],{"class":1323,"line":277},[1321,4600,3399],{},[1321,4602,4603],{"class":1323,"line":282},[1321,4604,4573],{},[1321,4606,4607],{"class":1323,"line":1578},[1321,4608,3337],{},[1321,4610,4611],{"class":1323,"line":1585},[1321,4612,1384],{"emptyLinePlaceholder":1383},[1321,4614,4615],{"class":1323,"line":1602},[1321,4616,4617],{},"    \u002F\u002F Run HTTP server\n",[1321,4619,4620],{"class":1323,"line":1609},[1321,4621,4622],{},"    app.Run(inst, server.Default(inst))\n",[1321,4624,4625],{"class":1323,"line":1628},[1321,4626,3002],{},[1312,4628,4630],{"className":2693,"code":4629,"language":2695,"meta":1317,"style":1317},"# Start infrastructure\ncd infra && make up\n\n# Run the service\ncd backend && go run .\u002Fcmd\u002Fapi\u002F\n",[1029,4631,4632,4637,4655,4659,4664],{"__ignoreMap":1317},[1321,4633,4634],{"class":1323,"line":10},[1321,4635,4636],{"class":1326},"# Start infrastructure\n",[1321,4638,4639,4643,4646,4649,4652],{"class":1323,"line":16},[1321,4640,4642],{"class":4641},"s2Zo4","cd",[1321,4644,4645],{"class":1343}," infra",[1321,4647,4648],{"class":1336}," &&",[1321,4650,4651],{"class":2707}," make",[1321,4653,4654],{"class":1343}," up\n",[1321,4656,4657],{"class":1323,"line":22},[1321,4658,1384],{"emptyLinePlaceholder":1383},[1321,4660,4661],{"class":1323,"line":51},[1321,4662,4663],{"class":1326},"# Run the service\n",[1321,4665,4666,4668,4671,4673,4676,4679],{"class":1323,"line":57},[1321,4667,4642],{"class":4641},[1321,4669,4670],{"class":1343}," backend",[1321,4672,4648],{"class":1336},[1321,4674,4675],{"class":2707}," go",[1321,4677,4678],{"class":1343}," run",[1321,4680,4681],{"class":1343}," .\u002Fcmd\u002Fapi\u002F\n",[974,4683,4684],{},"The service exposes:",[1005,4686,4687,4697],{},[1008,4688,4689],{},[1011,4690,4691,4694],{},[1014,4692,4693],{},"Endpoint",[1014,4695,4696],{},"What it is",[1021,4698,4699,4709,4719,4729],{},[1011,4700,4701,4706],{},[1026,4702,4703],{},[1029,4704,4705],{},"POST \u002Fapi\u002Fv1\u002Fwebhooks",[1026,4707,4708],{},"Source node — webhook ingest",[1011,4710,4711,4716],{},[1026,4712,4713],{},[1029,4714,4715],{},"GET \u002Fasyncapi.json",[1026,4717,4718],{},"Auto-generated AsyncAPI spec from your events",[1011,4720,4721,4726],{},[1026,4722,4723],{},[1029,4724,4725],{},"GET \u002Freference",[1026,4727,4728],{},"OpenAPI docs for the HTTP actions",[1011,4730,4731,4736],{},[1026,4732,4733],{},[1029,4734,4735],{},"GET \u002Fhealth",[1026,4737,4738],{},"Health check",[994,4740],{},[997,4742,4744],{"id":4743},"_8-test-without-kafka","8. Test without Kafka",[974,4746,4747],{},"Use the in-memory transport to test the full node chain in unit tests — no Docker required.",[1312,4749,4751],{"className":2940,"code":4750,"language":2942,"meta":1317,"style":1317},"package alerts_test\n\nimport (\n    \"context\"\n    \"testing\"\n\n    \"github.com\u002Fredelay\u002Fgo-events\u002Feventbus\"\n    \"github.com\u002Fredelay\u002Fgo-events\u002Ftransport\u002Fmemory\"\n    \"github.com\u002Fmyorg\u002Fmyapp\u002Fmodules\u002Falerts\"\n    \"github.com\u002Fmyorg\u002Fmyapp\u002Fmodules\u002Falerts\u002Fnodes\"\n)\n\nfunc TestFilterBySeverity_RoutesCorrectly(t *testing.T) {\n    tr := memory.New()\n    bus := eventbus.New(tr)\n    _ = bus\n\n    router := &nodes.FilterBySeverityNode{}\n\n    critical := &alerts.RawWebhook{WebhookID: \"w1\", Source: \"api\", Severity: \"critical\"}\n    low := &alerts.RawWebhook{WebhookID: \"w2\", Source: \"api\", Severity: \"low\"}\n\n    if got := router.Route(context.Background(), critical); got != \"alert\" {\n        t.Errorf(\"expected alert, got %q\", got)\n    }\n    if got := router.Route(context.Background(), low); got != \"archive\" {\n        t.Errorf(\"expected archive, got %q\", got)\n    }\n}\n\nfunc TestEnrichWebhook_AddsOwnership(t *testing.T) {\n    enricher := nodes.NewEnrichWebhookNode(\"\")\n    \u002F\u002F inject test ownership map\n    raw := &alerts.RawWebhook{WebhookID: \"w1\", Source: \"checkout-api\", Severity: \"high\"}\n\n    enriched, err := enricher.Transform(context.Background(), raw)\n    if err != nil {\n        t.Fatal(err)\n    }\n    if enriched.WebhookID != raw.WebhookID {\n        t.Errorf(\"webhookId not preserved\")\n    }\n}\n",[1029,4752,4753,4758,4762,4766,4770,4775,4779,4783,4788,4792,4796,4800,4804,4809,4814,4819,4824,4828,4833,4837,4842,4847,4851,4856,4861,4865,4870,4875,4879,4883,4887,4892,4897,4902,4907,4911,4916,4920,4925,4929,4934,4939,4943],{"__ignoreMap":1317},[1321,4754,4755],{"class":1323,"line":10},[1321,4756,4757],{},"package alerts_test\n",[1321,4759,4760],{"class":1323,"line":16},[1321,4761,1384],{"emptyLinePlaceholder":1383},[1321,4763,4764],{"class":1323,"line":22},[1321,4765,3223],{},[1321,4767,4768],{"class":1323,"line":51},[1321,4769,3493],{},[1321,4771,4772],{"class":1323,"line":57},[1321,4773,4774],{},"    \"testing\"\n",[1321,4776,4777],{"class":1323,"line":102},[1321,4778,1384],{"emptyLinePlaceholder":1383},[1321,4780,4781],{"class":1323,"line":108},[1321,4782,3502],{},[1321,4784,4785],{"class":1323,"line":114},[1321,4786,4787],{},"    \"github.com\u002Fredelay\u002Fgo-events\u002Ftransport\u002Fmemory\"\n",[1321,4789,4790],{"class":1323,"line":120},[1321,4791,3257],{},[1321,4793,4794],{"class":1323,"line":181},[1321,4795,4205],{},[1321,4797,4798],{"class":1323,"line":186},[1321,4799,3262],{},[1321,4801,4802],{"class":1323,"line":191},[1321,4803,1384],{"emptyLinePlaceholder":1383},[1321,4805,4806],{"class":1323,"line":197},[1321,4807,4808],{},"func TestFilterBySeverity_RoutesCorrectly(t *testing.T) {\n",[1321,4810,4811],{"class":1323,"line":203},[1321,4812,4813],{},"    tr := memory.New()\n",[1321,4815,4816],{"class":1323,"line":214},[1321,4817,4818],{},"    bus := eventbus.New(tr)\n",[1321,4820,4821],{"class":1323,"line":223},[1321,4822,4823],{},"    _ = bus\n",[1321,4825,4826],{"class":1323,"line":229},[1321,4827,1384],{"emptyLinePlaceholder":1383},[1321,4829,4830],{"class":1323,"line":234},[1321,4831,4832],{},"    router := &nodes.FilterBySeverityNode{}\n",[1321,4834,4835],{"class":1323,"line":240},[1321,4836,1384],{"emptyLinePlaceholder":1383},[1321,4838,4839],{"class":1323,"line":246},[1321,4840,4841],{},"    critical := &alerts.RawWebhook{WebhookID: \"w1\", Source: \"api\", Severity: \"critical\"}\n",[1321,4843,4844],{"class":1323,"line":251},[1321,4845,4846],{},"    low := &alerts.RawWebhook{WebhookID: \"w2\", Source: \"api\", Severity: \"low\"}\n",[1321,4848,4849],{"class":1323,"line":257},[1321,4850,1384],{"emptyLinePlaceholder":1383},[1321,4852,4853],{"class":1323,"line":1538},[1321,4854,4855],{},"    if got := router.Route(context.Background(), critical); got != \"alert\" {\n",[1321,4857,4858],{"class":1323,"line":1543},[1321,4859,4860],{},"        t.Errorf(\"expected alert, got %q\", got)\n",[1321,4862,4863],{"class":1323,"line":271},[1321,4864,3337],{},[1321,4866,4867],{"class":1323,"line":277},[1321,4868,4869],{},"    if got := router.Route(context.Background(), low); got != \"archive\" {\n",[1321,4871,4872],{"class":1323,"line":282},[1321,4873,4874],{},"        t.Errorf(\"expected archive, got %q\", got)\n",[1321,4876,4877],{"class":1323,"line":1578},[1321,4878,3337],{},[1321,4880,4881],{"class":1323,"line":1585},[1321,4882,3002],{},[1321,4884,4885],{"class":1323,"line":1602},[1321,4886,1384],{"emptyLinePlaceholder":1383},[1321,4888,4889],{"class":1323,"line":1609},[1321,4890,4891],{},"func TestEnrichWebhook_AddsOwnership(t *testing.T) {\n",[1321,4893,4894],{"class":1323,"line":1628},[1321,4895,4896],{},"    enricher := nodes.NewEnrichWebhookNode(\"\")\n",[1321,4898,4899],{"class":1323,"line":1633},[1321,4900,4901],{},"    \u002F\u002F inject test ownership map\n",[1321,4903,4904],{"class":1323,"line":1641},[1321,4905,4906],{},"    raw := &alerts.RawWebhook{WebhookID: \"w1\", Source: \"checkout-api\", Severity: \"high\"}\n",[1321,4908,4909],{"class":1323,"line":1651},[1321,4910,1384],{"emptyLinePlaceholder":1383},[1321,4912,4913],{"class":1323,"line":1661},[1321,4914,4915],{},"    enriched, err := enricher.Transform(context.Background(), raw)\n",[1321,4917,4918],{"class":1323,"line":1671},[1321,4919,3399],{},[1321,4921,4922],{"class":1323,"line":1678},[1321,4923,4924],{},"        t.Fatal(err)\n",[1321,4926,4927],{"class":1323,"line":1695},[1321,4928,3337],{},[1321,4930,4931],{"class":1323,"line":1702},[1321,4932,4933],{},"    if enriched.WebhookID != raw.WebhookID {\n",[1321,4935,4936],{"class":1323,"line":1720},[1321,4937,4938],{},"        t.Errorf(\"webhookId not preserved\")\n",[1321,4940,4941],{"class":1323,"line":1725},[1321,4942,3337],{},[1321,4944,4945],{"class":1323,"line":1733},[1321,4946,3002],{},[994,4948],{},[997,4950,4952],{"id":4951},"next-steps","Next steps",[4954,4955,4956,4965,4973,4981,4987,4993,4998],"ul",{},[4957,4958,4959,4964],"li",{},[978,4960,4963],{"href":4961,"rel":4962},"https:\u002F\u002Fflowdsl.com\u002Fdocs\u002Fconcepts\u002Fnodes",[982],"FlowDSL concepts — Nodes"," — all nine node kinds in depth",[4957,4966,4967,4972],{},[978,4968,4971],{"href":4969,"rel":4970},"https:\u002F\u002Fflowdsl.com\u002Fdocs\u002Fconcepts\u002Fdelivery-modes",[982],"FlowDSL concepts — Delivery Modes"," — choose the right mode for each edge",[4957,4974,4975,4980],{},[978,4976,4979],{"href":4977,"rel":4978},"https:\u002F\u002Fflowdsl.com\u002Fstudio",[982],"FlowDSL Studio"," — visual canvas to draw and validate flows before writing YAML",[4957,4982,4983,4986],{},[978,4984,188],{"href":980,"rel":4985},[982]," — copy real flow patterns (smart email triage, DB anomaly detection, sales pipeline)",[4957,4988,4989,4992],{},[978,4990,4991],{"href":87},"Adding Events to a Go Module"," — full guide to typed event publishing in Redelay",[4957,4994,4995,4997],{},[978,4996,94],{"href":95}," — Kafka vs NATS vs Redis Streams for your edges",[4957,4999,5000,5004],{},[978,5001,5003],{"href":5002},"\u002Fflowdsl","FlowDSL Examples (this site)"," — interactive examples with YAML viewer and export commands",[5006,5007,5008],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":1317,"searchDepth":16,"depth":16,"links":5010},[5011,5016,5017,5018,5019,5034,5035,5036,5037],{"id":999,"depth":16,"text":1000,"children":5012},[5013,5014],{"id":1058,"depth":22,"text":1059},{"id":1207,"depth":22,"text":5015},"Delivery modes (edge mode)",{"id":1298,"depth":16,"text":1299},{"id":2689,"depth":16,"text":2690},{"id":2747,"depth":16,"text":2748},{"id":2913,"depth":16,"text":2914,"children":5020},[5021,5022,5023,5024,5026,5028,5030,5032],{"id":2924,"depth":22,"text":2925},{"id":2936,"depth":22,"text":2937},{"id":3091,"depth":22,"text":3092},{"id":3193,"depth":22,"text":5025},"nodes\u002Fsource.go — ingest_webhook (kind: source)",{"id":3461,"depth":22,"text":5027},"nodes\u002Frouter.go — filter_by_severity (kind: router)",{"id":3611,"depth":22,"text":5029},"nodes\u002Ftransform.go — enrich_webhook (kind: transform)",{"id":3804,"depth":22,"text":5031},"nodes\u002Faction_pd.go — dispatch_pagerduty (kind: action)",{"id":4022,"depth":22,"text":5033},"nodes\u002Fterminal.go — archive_low_severity (kind: terminal)",{"id":4160,"depth":16,"text":4161},{"id":4474,"depth":16,"text":4475},{"id":4743,"depth":16,"text":4744},{"id":4951,"depth":16,"text":4952},"Take a FlowDSL diagram, validate it, generate Go models, and run it with Redelay.","md",null,{},{"order":51,"icon":52},{"title":90,"description":5038},"gDPv_dK9qI3GeH0gfMESiNcHCpaNkIyqgaodtEOP9NY",[5046,5048],{"title":86,"path":87,"stem":88,"description":5047,"order":22,"icon":36,"children":-1},"How to publish events, consume events, and test event-driven behaviour in a Redelay Go module.",{"title":94,"path":95,"stem":96,"description":5049,"order":57,"icon":97,"children":-1},"How to choose the right event transport — Memory, NATS, Redis Streams, or Kafka.",1790332226878]