PageSourceSearch

https://docs.mia-platform.eu/assets/js/3fd41c10.6fbc2ae4.js

js mia-platform.eu collected 2026-10-02 06:05:45 UTC 58,118 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkmia_platform_docs=self.webpackChunkmia_platform_docs||[]).push([["13676"],{609880(e,n,s){s.r(n),s.d(n,{metadata:()=>t,default:()=>c,frontMatter:()=>o,contentTitle:()=>d,toc:()=>l,assets:()=>a});var t=JSON.parse('{"id":"products/ai-foundry/orchestration/build-workflows","title":"Build, run, and test agentic workflows","description":"This guide shows you how to design an Agentic Workflow, build it in the workflow wizard, start it by hand, on a schedule, or from a webhook, and test and monitor its runs until it is ready for production.","source":"@site/versioned_docs/version-15.2.0/products/ai-foundry/orchestration/20_workflows.md","sourceDirName":"products/ai-foundry/orchestration","slug":"/products/ai-foundry/orchestration/build-workflows","permalink":"/docs/15.2.0/products/ai-foundry/orchestration/build-workflows","draft":false,"unlisted":false,"tags":[],"version":"15.2.0","sidebarPosition":20,"frontMatter":{"id":"build-workflows","title":"Build, run, and test agentic workflows","sidebar_label":"Agentic Workflows"},"sidebar":"aiFoundry","previous":{"title":"Playbooks","permalink":"/docs/15.2.0/products/ai-foundry/orchestration/build-playbooks"},"next":{"title":"AI Resources","permalink":"/docs/15.2.0/products/ai-foundry/observability/ai-resources"}}'),r=s(474848),i=s(28453);let o={id:"build-workflows",title:"Build, run, and test agentic workflows",sidebar_label:"Agentic Workflows"},d="Build, run, and test agentic workflows",a={},l=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Design the workflow",id:"design-the-workflow",level:2},{value:"Choose between a playbook and a workflow",id:"choose-between-a-playbook-and-a-workflow",level:3},{value:"Model the process as a graph",id:"model-the-process-as-a-graph",level:3},{value:"Declare typed inputs",id:"declare-typed-inputs",level:3},{value:"Pass data between steps",id:"pass-data-between-steps",level:3},{value:"Choose the step types",id:"choose-the-step-types",level:3},{value:"Make agent output usable by later steps",id:"make-agent-output-usable-by-later-steps",level:3},{value:"Plan timeouts, retries, and idempotency",id:"plan-timeouts-retries-and-idempotency",level:3},{value:"Add human approval",id:"add-human-approval",level:3},{value:"Secure the workflow",id:"secure-the-workflow",level:3},{value:"Example design: triage an issue and open a pull request",id:"example-design-triage-an-issue-and-open-a-pull-request",level:3},{value:"Example: build and test in a workspace",id:"example-build-and-test-in-a-workspace",level:3},{value:"Build the workflow in the wizard",id:"build-the-workflow-in-the-wizard",level:2},{value:"Edit the JSON directly",id:"edit-the-json-directly",level:3},{value:"How validation works",id:"how-validation-works",level:3},{value:"Run the workflow",id:"run-the-workflow",level:2},{value:"Permissions",id:"permissions",level:3},{value:"Run it by hand",id:"run-it-by-hand",level:3},{value:"Run it on a schedule",id:"run-it-on-a-schedule",level:3},{value:"Run it from a webhook",id:"run-it-from-a-webhook",level:3},{value:"Test and monitor runs",id:"test-and-monitor-runs",level:2},{value:"Validate and run with test inputs",id:"validate-and-run-with-test-inputs",level:3},{value:"Read the run page",id:"read-the-run-page",level:3},{value:"Approve, refuse, or cancel",id:"approve-refuse-or-cancel",level:3},{value:"Watch all runs",id:"watch-all-runs",level:3},{value:"Debug a failed step",id:"debug-a-failed-step",level:3},{value:"Understand versioning",id:"understand-versioning",level:3},{value:"Pre-production checklist",id:"pre-production-checklist",level:3},{value:"Troubleshooting",id:"troubleshooting",level:2},{value:"Related resources",id:"related-resources",level:2}];function h(e){let n={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",header:"header",input:"input",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"build-run-and-test-agentic-workflows",children:"Build, run, and test agentic workflows"})}),"\n",(0,r.jsxs)(n.p,{children:["This guide shows you how to design an ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/basic-concepts/workflow",children:"Agentic Workflow"}),", build it in the workflow wizard, start it by hand, on a schedule, or from a webhook, and test and monitor its runs until it is ready for production."]}),"\n",(0,r.jsxs)(n.p,{children:["It covers the tasks only. For the full list of spec f
1ields, step blocks, and trigger options, see the ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/basic-concepts/workflow",children:"Agentic Workflow"})," reference."]}),"\n",(0,r.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["You know the basic AI Foundry concepts: ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/basic-concepts/agent",children:"Agent"}),", ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/basic-concepts/playbook",children:"Playbook"}),", and ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/basic-concepts/workflow",children:"Agentic Workflow"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["The playbooks your workflow calls already exist, are enabled, and have been tested. See ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/orchestration/build-playbooks",children:"Build, run, and test playbooks"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["For steps that call external systems, an administrator has created the ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/administration/api-security",children:"API Credentials"})," you need."]}),"\n",(0,r.jsxs)(n.li,{children:["You have the permissions described in ",(0,r.jsx)(n.a,{href:"#permissions",children:"Permissions"}),"."]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"design-the-workflow",children:"Design the workflow"}),"\n",(0,r.jsx)(n.h3,{id:"choose-between-a-playbook-and-a-workflow",children:"Choose between a playbook and a workflow"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Use"}),(0,r.jsx)(n.th,{children:"When"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"A playbook"}),(0,r.jsx)(n.td,{children:"The work happens in a conversation, and the model decides at run time which agent does what."})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"An agentic workflow"}),(0,r.jsx)(n.td,{children:"The work runs outside a conversation: it starts from a schedule or an external system, must wait for a person, mixes agents with HTTP calls, Git operations, or commands, or must follow the same path every time."})]})]})]}),"\n",(0,r.jsxs)(n.p,{children:["A workflow does not replace a playbook: its ",(0,r.jsx)(n.strong,{children:"Playbook"})," steps run one turn of a playbook each. Design and test the playbook first, then call it from the workflow."]}),"\n",(0,r.jsx)(n.h3,{id:"model-the-process-as-a-graph",children:"Model the process as a graph"}),"\n",(0,r.jsx)(n.p,{children:"A workflow is a graph, not a list. Before you open the wizard, sketch the process:"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["Pick the ",(0,r.jsx)(n.strong,{children:"start"})," step, the one every run begins at."]}),"\n",(0,r.jsxs)(n.li,{children:["Connect each step to its successor with ",(0,r.jsx)(n.code,{children:"next"}),". A step without a successor ends the run."]}),"\n",(0,r.jsxs)(n.li,{children:["For a ",(0,r.jsx)(n.strong,{children:"Condition"}),", draw ",(0,r.jsx)(n.code,{children:"ifTrue"})," and ",(0,r.jsx)(n.code,{children:"ifFalse"}),". At least one of the two is required."]}),"\n",(0,r.jsxs)(n.li,{children:["For an ",(0,r.jsx)(n.strong,{children:"Approval"}),", ",(0,r.jsx)(n.code,{children:"ifTrue"})," is where the run goes when someone approves, and ",(0,r.jsx)(n.code,{children:"ifFalse"})," where it goes when someone refuses. A refusal with no ",(0,r.jsx)(n.code,{children:"ifFalse"}),' ends the run successfully: a person answering "no" is the workflow working as designed.']}),"\n",(0,r.jsxs)(n.li,{children:["For any step that can fail (Playbook, HTTP request, Integration, Clone a repository, Run a command), decide whether a failure ends the run or follows an ",(0,r.jsx)(n.code,{children:"onError"})," branch, for example to notify a channel."]}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"The graph may contain cycles, for example a step that loops back to retry a check. A run is stopped after 500 step executions, so every cycle needs an exit."}),"\n",(0,r.jsx)(n.h3,{id:"declare-typed-inputs",children:"Declare typed inputs"}),"\n",(0,r.jsxs)(n.p,{children:["Declare every value a run needs as an input, with a ",(0,r.jsx)(n.code,{children:"type"})," (",(0,r.jsx)(n.code,{children:"string"}),", ",(0,r.jsx)(n.code,{children:"number"}),", ",(0,r.jsx)(n.code,{children:"boolean"}),", or ",(0,r.jsx)(n.code,{children:"object"}),"), a ",(0,r.jsx)(n.code,{children:"description"})," for the person filling in the Run form, and either ",(0,r.jsx)(n.code,{children:"required: true"})," or a ",(0,r.jsx)(n.code,{children:"default"}),"."]}),"\n",(0,r.jsxs)(n.p,{children:["Inputs are an allowlist: whatever a trigger sends that is not declared is dropped before it reaches a step. A required input with no value and no default makes the run fail to start. See ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/basic-concepts/workflow#inputs",children:"Inputs"})," for the field reference."]}),"\n",(0,r.jsx)(n.h3,{id:"pass-data-between-steps",children:"Pass data between steps"}),"\n",(0,r.jsxs)(n.p,{children:["Step fields are templated with ",(0,r.jsx)(n.code,{children:"{{ ... }}"})," placeholders:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"{{ inputs.issueNumber }}"})," reads an input."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"{{ steps.<id>.output.<field> }}"})," reads the output of an earlier step. Nested fields and list indexes are separated by dots, for example ",(0,r.jsx)(n.code,{children:"{{ steps.fetch.output.body.items.0.id }}"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"{{ steps.<id>.error }}"})," reads the error message of an earlier step that failed, which is useful in a step on an ",(0,r.jsx)(n.code,{children:"onError"})," branch."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"{{ run.workflowId }}"})," reads the id of the current run."]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["Templating is substitution only. A placeholder that names something absent resolves to an empty value instead of failing the run, and a placeholder that fills an entire field keeps its type. See ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/basic-concepts/workflow#templating",children:"Templating"}),"."]}),"\n",(0,r.jsx)(n.p,{children:"Each step type exposes the following output fields:"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Step"}),(0,r.jsx)(n.th,{children:"Output fields"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.strong,{children:"Playbook"})}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.code,{children:"text"})," (the agent's answer), ",(0,r.jsx)(n.code,{children:"agent"}),", ",(0,r.jsx)(n.code,{children:"sessionId"})]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.strong,{children:"HTTP request"})}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.code,{children:"status"}),", ",(0,r.jsx)(n.code,{children:"headers"}),", ",(0,r.jsx)(n.code,{children:"body"}),", ",(0,r.jsx)(n.code,{children:"json"})," (",(0,r.jsx)(n.code,{children:"true"})," when the body was parsed as JSON)"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.strong,{children:"Integration"})}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.code,{children:"status"}),", ",(0,r.jsx)(n.code,{children:"headers"}),", ",(0,r.jsx)(n.code,{children:"body"}),", ",(0,r.jsx)(n.code,{children:"json"}),", ",(0,r.jsx)(n.code,{children:"method"}),", ",(0,r.jsx)(n.code,{children:"path"}),", plus the outputs the action declares, for example ",(0,r.jsx)(n.code,{children:"number"})," and ",(0,r.jsx)(n.code,{children:"url"})," for ",(0,r.jsx)(n.code,{children:"github/create-pull-request"})]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.strong,{children:"Clone a repository"})}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.code,{children:"path"}),", ",(0,r.jsx)(n.code,{children:"commit"}),", ",(0,r.jsx)(n.code,{children:"ref"}),", ",(0,r.jsx)(n.code,{children:"sealed"})]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.strong,{children:"Run a command"})}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.code,{children:"exitCode"}),", ",(0,r.jsx)(n.code,{children:"stdout"}),", ",(0,r.jsx)(n.code,{children:"stderr"}),", ",(0,r.jsx)(n.code,{children:"timedOut"}),", ",(0,r.jsx)(n.code,{children:"durationMs"}),", ",(0,r.jsx)(n.code,{children:"truncated"})]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.strong,{children:"Condition"})}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.code,{children:"result"}),", ",(0,r.jsx)(n.code,{children:"left"}),", ",(0,r.jsx)(n.code,{children:"right"})]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.strong,{children:"Approval"})}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.code,{children:"approved"}),", ",(0,r.jsx)(n.code,{children:"note"}),", ",(0,r.jsx)(n.code,{children:"actor"}),"; on a timeout, ",(0,r.jsx)(n.code,{children:"approved"})," and ",(0,r.jsx)(n.code,{children:"reason"})]})]})]})]}),"\n",(0,r.jsxs)(n.p,{children:["You can address fields of an HTTP response body only when the response is JSON (",(0,r.jsx)(n.code,{children:"json"})," is ",(0,r.jsx)(n.code,{children:"true"}),"). Bodies that are too long are truncated and are never parsed as JSON."]}),"\n",(0,r.jsx)(n.h3,{id:"choose-the-step-types",children:"Choose the step types"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"You need to"}),(0,r.jsx)(n.th,{children:"Use"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Ask an agent to reason, draft, classify, or review"}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.strong,{children:"Playbook"})," (",(0,r.jsx)(n.code,{children:"agent"}),"). By default the playbook's root agent answers. Turn on ",(0,r.jsx)(n.strong,{children:"Share the session with other agent steps"})," only when a later agent step must see what an earlier one said."]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Call GitHub or GitLab"}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.strong,{children:"Integration"}),". It knows the endpoint, the body shape, and the fields worth reading back, and it percent-encodes every value placed in the path."]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Call any other REST API"}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.strong,{children:"HTTP request"}),". Name an API Credential so that the secret stays out of the spec; the URL then becomes a path under the credential's base URL."]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Work on repository files, build, or test"}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.strong,{children:"Clone a repository"})," followed by ",(0,r.jsx)(n.strong,{children:"Run a command"}),", in a workflow that declares a workspace."]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Branch on a value"}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.strong,{children:"Condition"}),"."]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Wait for a person"}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.strong,{children:"Approval"}),"."]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Wait for a fixed time"}),(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.strong,{children:"Delay"}),", up to one year."]})]})]})]}),"\n",(0,r.jsx)(n.p,{children:"The built-in integration actions are:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"GitHub"}),": ",(0,r.jsx)(n.code,{children:"get-file"}),", ",(0,r.jsx)(n.code,{children:"get-file-info"}),", ",(0,r.jsx)(n.code,{children:"get-branch"}),", ",(0,r.jsx)(n.code,{children:"create-branch"}),", ",(0,r.jsx)(n.code,{children:"commit-file"}),", ",(0,r.jsx)(n.code,{children:"create-pull-request"}),", ",(0,r.jsx)(n.code,{children:"list-pull-requests"}),", ",(0,r.jsx)(n.code,{children:"comment-on-pull-request"}),", ",(0,r.jsx)(n.code,{children:"merge-pull-request"}),", ",(0,r.jsx)(n.code,{children:"create-tag"}),", ",(0,r.jsx)(n.code,{children:"create-release"}),", ",(0,r.jsx)(n.code,{children:"latest-release"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"GitLab"}),": ",(0,r.jsx)(n.code,{children:"get-file"}),", ",(0,r.jsx)(n.code,{children:"create-branch"}),", ",(0,r.jsx)(n.code,{children:"commit-file"}),", ",(0,r.jsx)(n.code,{children:"create-merge-request"}),", ",(0,r.jsx)(n.code,{children:"list-merge-requests"}),", ",(0,r.jsx)(n.code,{children:"comment-on-merge-request"}),", ",(0,r.jsx)(n.code,{children:"merge-merge-request"}),", ",(0,r.jsx)(n.code,{children:"create-tag"}),", ",(0,r.jsx)(n.code,{children:"create-release"}),", ",(0,r.jsx)(n.code,{children:"latest-release"}),"."]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["The step drawer lists each action's inputs under ",(0,r.jsx)(n.strong,{children:"What to send"})," and its outputs under ",(0,r.jsx)(n.strong,{children:"What this step produces"}),"."]}),"\n",(0,r.jsx)(n.h3,{id:"make-agent-output-usable-by-later-steps",children:"Make agent output usable by later steps"}
1),"\n",(0,r.jsxs)(n.p,{children:["A Playbook step exposes the agent's answer as plain text in ",(0,r.jsx)(n.code,{children:"output.text"}),": the text of all the agent's messages in the turn, joined together. The engine does not parse it as JSON, so you cannot address fields inside it."]}),"\n",(0,r.jsx)(n.p,{children:"To branch on what an agent decided:"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["In the agent's instructions (or in the step's ",(0,r.jsx)(n.strong,{children:"Prompt"}),"), ask for a fixed marker in the answer, for example a line ",(0,r.jsx)(n.code,{children:"VERDICT: ACTIONABLE"})," or ",(0,r.jsx)(n.code,{children:"VERDICT: NEEDS-INFO"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["Add a ",(0,r.jsx)(n.strong,{children:"Condition"})," with ",(0,r.jsx)(n.strong,{children:"Value"})," ",(0,r.jsx)(n.code,{children:"{{ steps.<id>.output.text }}"}),", the ",(0,r.jsx)(n.code,{children:"contains"})," comparison, and the marker as ",(0,r.jsx)(n.strong,{children:"Compared with"}),". The comparison is case-sensitive."]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["To use the whole answer, pass ",(0,r.jsx)(n.code,{children:"{{ steps.<id>.output.text }}"})," into a later field, such as the body of a commit, a pull request description, or an HTTP request body."]}),"\n",(0,r.jsx)(n.h3,{id:"plan-timeouts-retries-and-idempotency",children:"Plan timeouts, retries, and idempotency"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Timeout (seconds)"})," bounds a single attempt, not the sum of the retries. The default is 120 seconds, which is often too short for an agent that calls tools: raise it for Playbook steps."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Attempts"})," (",(0,r.jsx)(n.code,{children:"retry.maximumAttempts"}),") defaults to 3. Transport errors, ",(0,r.jsx)(n.code,{children:"429"}),", and ",(0,r.jsx)(n.code,{children:"5xx"})," responses are retried with exponential backoff; ",(0,r.jsx)(n.code,{children:"4xx"})," responses are not, because they would fail the same way every time. The other backoff fields are available in JSON mode. Condition, Delay, and Approval steps are never retried."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Retries repeat side effects."})," A retry is a second request, and after a timed-out attempt the engine cannot know whether the first one arrived. For an action that creates something, such as opening a pull request, set ",(0,r.jsx)(n.strong,{children:"Attempts"})," to ",(0,r.jsx)(n.code,{children:"1"})," and draw an ",(0,r.jsx)(n.code,{children:"onError"})," branch instead. The step drawer warns you when an integration action is not safe to repeat."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Every trigger starts a new run."})," A webhook caller that retries a delivery starts a second run, so design steps that tolerate running twice, for example by checking with ",(0,r.jsx)(n.code,{children:"list-pull-requests"})," before opening a new one."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"add-human-approval",children:"Add human approval"}),"\n",(0,r.jsxs)(n.p,{children:["Put an ",(0,r.jsx)(n.strong,{children:"Approval"})," step before any action you want a person to review. Set:"]}
1),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"What is being asked"}),": the question shown on the run page. You can use templates, for example to include the pull request title."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Give up after (seconds)"}),": how long to wait. The default is one day."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"If nobody answers"}),": ",(0,r.jsx)(n.strong,{children:"Fail the run"})," (default), ",(0,r.jsx)(n.strong,{children:"Treat as approved"}),", or ",(0,r.jsx)(n.strong,{children:"Treat as refused"}),". With ",(0,r.jsx)(n.strong,{children:"Treat as refused"}),", the run follows ",(0,r.jsx)(n.code,{children:"ifFalse"})," if present, otherwise ",(0,r.jsx)(n.code,{children:"next"}),"."]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["Place steps that need a workspace ",(0,r.jsx)(n.em,{children:"after"})," long approvals when you can: the workspace is created at the first step that needs it, and its lifetime counts from that moment."]}),"\n",(0,r.jsx)(n.h3,{id:"secure-the-workflow",children:"Secure the workflow"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"API Credentials"}),": never put secrets in headers or bodies, which are visible to everyone who can read the workflow. Name an API Credential instead. With a credential, the host comes from the credential's base URL and the step can only choose the path, so a workflow editor cannot send the credential elsewhere. The credential name itself cannot be templated."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Network"}),": HTTP request URLs that resolve to private, loopback, or link-local addresses are refused unless your installation allows them, and redirects are not followed."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Commands"}),": ",(0,r.jsx)(n.strong,{children:"Run a command"})," works only if the installation allows commands in workspaces. The runner is sealed before the first command, so no credential is available to code from a cloned repository: clone first, then build."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Webhook secret"}),": treat it like a password. It is shown only once, and rotating it invalidates the previous one immediately."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"example-design-triage-an-issue-and-open-a-pull-request",children:"Example design: triage an issue and open a pull request"}),"\n",(0,r.jsx)(n.p,{children:"The following workflow asks a triage playbook to analyze a GitHub issue. If the agent marks the issue as actionable, a person reviews the proposal, and the workflow then commits it to a new branch and opens a pull request. If opening the pull request fails, the workflow posts the error to a team chat API."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",children:'{\n  "description": "Triage a GitHub issue and propose a fix plan as a pull request",\n  "inputs": [\n    { "name": "owner", "type": "string", "required": true, "description": "Repository owner" },\n    { "name": "repo", "type": "string", "required": true, "description": "Repository name" },\n    { "name": "issueNumber", "type": "number", "required": true },\n    { "name": "issueTitle", "type": "string", "required": true },\n    { "name": "issueBody", "type": "string", "default": "" }\n  ],\n  "triggers": {\n    "manual": { "enabled": true },\n    "webhook": { "enabled": true }\n  },\n  "start": "triage",\n  "steps": [\n    {\n      "id": "triage",\n      "name": "Triage the issue",\n      "type": "agent",\n      "agent": {\n        "playbook": "issue-triage",\n        "prompt": "Triage issue #{{ inputs.issueNumber }}: {{ inputs.issueTitle }}\\n\\n{{ inputs.issueBody }}\\n\\nWrite a fix plan in Markdown. End with one line: VERDICT: ACTIONABLE or VERDICT: NEEDS-INFO."\n      },\n      "timeoutSeconds": 600,\n      "next": "is-actionable"\n    },\n    {\n      "id": "is-actionable",\n      "type": "condition",\n      "condition": {\n        "left": "{{ steps.triage.output.text }}",\n        "operator": "contains",\n        "right": "VERDICT: ACTIONABLE"\n      },\n      "ifTrue": "review"\n    },\n    {\n      "id": "review",\n      "type": "approval",\n      "approval": {\n        "message": "Open a pull request with the fix plan for issue #{{ inputs.issueNumber }}?",\n        "timeoutSeconds": 172800,\n        "onTimeout": "reject"\n      },\n      "ifTrue": "read-main"\n    },\n    {\n      "id": "read-main",\n      "type": "integration",\n      "integration": {\n        "provider": "github",\n        "action": "get-branch",\n        "apiCredential": "github-bot",\n        "with": { "owner": "{{ inputs.owner }}", "repo": "{{ inputs.repo }}", "branch": "main" }\n      },\n      "next": "create-branch"\n    },\n    {\n      "id": "create-branch",\n      "type": "integration",\n      "integration": {\n        "provider": "github",\n        "action": "create-branch",\n        "apiCredential": "github-bot",\n        "with": {\n          "owner": "{{ inputs.owner }}",\n          "repo": "{{ inputs.repo }}",\n          "branch": "triage/issue-{{ inputs.issueNumber }}",\n          "sha": "{{ steps.read-main.output.sha }}"\n        }\n      },\n      "retry": { "maximumAttempts": 1 },\n      "onError": "notify-failure",\n      "next": "commit-plan"\n    },\n    {\n      "id": "commit-plan",\n      "type": "integration",\n      "integration": {\n        "provider": "github",\n        "action": "commit-file",\n        "apiCredential": "github-bot",\n        "with": {\n          "owner": "{{ inputs.owner }}",\n          "repo": "{{ inputs.repo }}",\n          "branch": "triage/issue-{{ inputs.issueNumber }}",\n          "filePath": "docs/triage/issue-{{ inputs.issueNumber }}.md",\n          "message": "docs: fix plan for issue #{{ inputs.issueNumber }}",\n          "content": "{{ steps.triage.output.text }}"\n        }\n      },\n      "onError": "notify-failure",\n      "next": "open-pr"\n    },\n    {\n      "id": "open-pr",\n      "type": "integration",\n      "integration": {\n        "provider": "github",\n        "action": "create-pull-request",\n        "apiCredential": "github-bot",\n        "with": {\n          "owner": "{{ inputs.owner }}",\n          "repo": "{{ inputs.repo }}",\n          "head": "triage/issue-{{ inputs.issueNumber }}",\n          "base": "main",\n          "title": "Fix plan for #{{ inputs.issueNumber }}: {{ inputs.issueTitle }}",\n          "description": "Approved by {{ steps.review.output.actor }}. Note: {{ steps.review.output.note }}",\n          "draft": true\n        }\n      },\n      "retry": { "maximumAttempts": 1 },\n      "onError": "notify-failure"\n    },\n    {\n      "id": "notify-failure",\n      "type": "http",\n      "http": {\n        "method": "POST",\n        "apiCredential": "team-chat",\n        "url": "/messages",\n        "body": { "text": "Workflow run {{ run.workflowId }} failed: {{ steps.create-branch.error }} {{ steps.commit-plan.error }} {{ steps.open-pr.error }}" }\n      }\n    }\n  ]\n}\n'})}),"\n",(0,r.jsx)(n.p,{children:"In this example:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"issue-tr
1iage"})," is a playbook, and ",(0,r.jsx)(n.code,{children:"github-bot"})," and ",(0,r.jsx)(n.code,{children:"team-chat"})," are API Credentials of your tenant. Replace them with your own."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"create-branch"})," and ",(0,r.jsx)(n.code,{children:"open-pr"})," run once (",(0,r.jsx)(n.code,{children:"maximumAttempts: 1"}),") because repeating them would create duplicates; ",(0,r.jsx)(n.code,{children:"commit-plan"})," keeps the default retries because replacing a file is safe to repeat."]}),"\n",(0,r.jsx)(n.li,{children:"A run for an issue that needs more information ends after the condition, and a refused or unanswered approval ends the run without changes."}),"\n",(0,r.jsxs)(n.li,{children:["The run result is the output of the last successful step: the pull request ",(0,r.jsx)(n.code,{children:"number"})," and ",(0,r.jsx)(n.code,{children:"url"})," when everything succeeds."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"example-build-and-test-in-a-workspace",children:"Example: build and test in a workspace"}),"\n",(0,r.jsxs)(n.p,{children:["To work on the files of a repository, declare a ",(0,r.jsx)(n.code,{children:"workspace"})," and chain a clone, a command, and an agent step. Every agent step of a workflow with a workspace receives it, but it can read or change files only if the agent enables the workspace permissions (see the ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/basic-concepts/agent#agent-reference",children:"Agent reference"}),")."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",children:'{\n  "workspace": { "runner": "node", "ttlSeconds": 3600 },\n  "start": "checkout",\n  "steps": [\n    {\n      "id": "checkout",\n      "type": "clone",\n      "clone": { "apiCredential": "gitlab-bot", "repo": "acme/shop.git", "ref": "main" },\n      "next": "test"\n    },\n    {\n      "id": "test",\n      "type": "command",\n      "command": { "command": "npm ci && npm test", "workdir": "{{ steps.checkout.output.path }}" },\n      "timeoutSeconds": 900,\n      "retry": { "maximumAttempts": 1 },\n      "onError": "explain"\n    },\n    {\n      "id": "explain",\n      "type": "agent",\n      "agent": {\n        "playbook": "ci-helper",\n        "prompt": "The tests failed with exit code {{ steps.test.output.exitCode }}. Explain the failure:\\n\\n{{ steps.test.output.stderr }}"\n      },\n      "timeoutSeconds": 600\n    }\n  ]\n}\n'})}),"\n",(0,r.jsxs)(n.p,{children:["The available runner names depend on your installation: the ",(0,r.jsx)(n.strong,{children:"Runner"})," field of the wizard lists them. A failed command still exposes its output, so the step on the ",(0,r.jsx)(n.code,{children:"onError"})," branch can read ",(0,r.jsx)(n.code,{children:"stdout"})," and ",(0,r.jsx)(n.code,{children:"stderr"}),"."]}),"\n",(0,r.jsx)(n.h2,{id:"build-the-workflow-in-the-wizard",children:"Build the workflow in the wizard"}),"\n",(0,r.jsxs)(n.p,{children:["Open ",(0,r.jsx)(n.strong,{children:"Agentic Workflows"})," in the ",(0,r.jsx)(n.strong,{children:"Orchestration"})," section and create a workflow. The wizard has three steps."]}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Overview"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Title"}),", ",(0,r.jsx)(n.strong,{children:"Name"}),", ",(0,r.jsx)(n.strong,{children:"Description"}),", and tags. The ",(0,r.jsx)(n.strong,{children:"Name"})," is derived from the title, allows lowercase letters, digits, and hyphens, and cannot be changed after creation: it is the name the triggers and the webhook URL use."]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Workflow"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Click ",(0,r.jsx)(n.strong,{children:"Add step"})," and pick ",(0,r.jsx)(n.strong,{children:"HTTP request"}),", ",(0,r.jsx)(n.strong,{children:"Playbook"}),", ",(0,r.jsx)(n.strong,{children:"Integration"}),", ",(0,r.jsx)(n.strong,{children:"Clone a repository"}),", ",(0,r.jsx)(n.strong,{children:"Run a command"}),", ",(0,r.jsx)(n.strong,{children:"Condition"}),", ",(0,r.jsx)(n.strong,{children:"Approval"}),", or ",(0,r.jsx)(n.strong,{children:"Delay"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["Draw an edge from the ",(0,r.jsx)(n.strong,{children:"Start"})," node to the first step."]}),"\n",(0,r.jsxs)(n.li,{children:['Drag from a node\'s handles to connect steps: the green handle is the "yes" branch (',(0,r.jsx)(n.code,{children:"ifTrue"}),'), the red one the "no" branch (',(0,r.jsx)(n.code,{children:"ifFalse"}),"), and the one on the right is where a failure goes (",(0,r.jsx)(n.code,{children:"onError"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Click a node to configure it in the side drawer: its ",(0,r.jsx)(n.strong,{children:"Id"})," (the name templates use, as in ",(0,r.jsx)(n.code,{children:"{{ steps.<id>.output }}"}),"), ",(0,r.jsx)(n.strong,{children:"Name"}),", the fields of its type, and, under ",(0,r.jsx)(n.strong,{children:"Execution"}),", ",(0,r.jsx)(n.strong,{children:"Timeout (seconds)"})," and ",(0,r.jsx)(n.strong,{children:"Attempts"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["Click ",(0,r.jsx)(n.strong,{children:"Check"})," to validate the graph at any time."]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Settings"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Inputs"}),": click ",(0,r.jsx)(n.strong,{children:"Add input"})," and set ",(0,r.jsx)(n.strong,{children:"Name"}),", ",(0,r.jsx)(n.strong,{children:"Type"}),", ",(0,r.jsx)(n.strong,{children:"Required"}),", ",(0,r.jsx)(n.strong,{children:"Description"}),", and ",(0,r.jsx)(n.strong,{children:"Default"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Worksp
1ace"}),": turn on ",(0,r.jsx)(n.strong,{children:"Give runs a shared workspace"}),", then choose the ",(0,r.jsx)(n.strong,{children:"Runner"})," and ",(0,r.jsx)(n.strong,{children:"Reclaim after (seconds)"}),", between 60 and 86400."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Triggers"}),": ",(0,r.jsx)(n.strong,{children:"Run button in this website"}),", ",(0,r.jsx)(n.strong,{children:"REST webhook"}),", and ",(0,r.jsx)(n.strong,{children:"Schedule"}),", with ",(0,r.jsx)(n.strong,{children:"Cron"}),", ",(0,r.jsx)(n.strong,{children:"Time zone"}),", and the ",(0,r.jsx)(n.strong,{children:"Inputs for scheduled runs"}),"."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["Click ",(0,r.jsx)(n.strong,{children:"Create workflow"})," (or ",(0,r.jsx)(n.strong,{children:"Save changes"})," when editing) to save."]}),"\n",(0,r.jsx)(n.h3,{id:"edit-the-json-directly",children:"Edit the JSON directly"}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.strong,{children:"Workflow"})," and ",(0,r.jsx)(n.strong,{children:"Settings"})," steps have a ",(0,r.jsx)(n.strong,{children:"Form"}),"/",(0,r.jsx)(n.strong,{children:"JSON"})," toggle. In JSON mode on the ",(0,r.jsx)(n.strong,{children:"Workflow"})," step you edit ",(0,r.jsx)(n.code,{children:"start"})," and ",(0,r.jsx)(n.code,{children:"steps"}),"; node positions are optional, and steps written in JSON are laid out on the canvas for you. JSON mode is the fastest way to paste a workflow, copy one between environments, or set fields the form does not show, such as the full ",(0,r.jsx)(n.code,{children:"retry"})," policy. You cannot move to another step while the JSON is invali
1d."]}),"\n",(0,r.jsx)(n.h3,{id:"how-validation-works",children:"How validation works"}),"\n",(0,r.jsxs)(n.p,{children:["The wizard sends the spec to the workflow engine for validation when you click ",(0,r.jsx)(n.strong,{children:"Check"}),", when you leave the ",(0,r.jsx)(n.strong,{children:"Workflow"})," step with ",(0,r.jsx)(n.strong,{children:"Next"}),", and when you save. The engine checks, among others:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["that every edge points to an existing step, step ids are unique, and ",(0,r.jsx)(n.code,{children:"start"})," is one of the steps;"]}),"\n",(0,r.jsx)(n.li,{children:"that every step has the block matching its type, and no other block;"}),"\n",(0,r.jsx)(n.li,{children:"that Playbook steps name a playbook, and that steps naming an API Credential use a path, not an absolute URL;"}),"\n",(0,r.jsxs)(n.li,{children:["that ",(0,r.jsx)(n.code,{children:"clone"})," and ",(0,r.jsx)(n.code,{children:"command"})," steps are in a workflow with a workspace;"]}),"\n",(0,r.jsx)(n.li,{children:"that the cron expression of an enabled schedule is valid;"}),"\n",(0,r.jsx)(n.li,{children:"that integration providers and actions exist and that every required action input is filled."}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["The first problem appears as an error message in the form ",(0,r.jsx)(n.code,{children:"<field>: <message>"}),", for example ",(0,r.jsx)(n.code,{children:"steps.4.integration.with.title: 'title' is required by 'github/create-pull-request'"}),", and the wizard stays where it is. Steps that no edge reaches produce a warning that names them, but do not block you. If the engine cannot be reached, the wizard warns you and still lets you save."]}),"\n",(0,r.jsx)(n.h2,{id:"run-the-workflow",children:"Run the workflow"}),"\n",(0,r.jsx)(n.h3,{id:"permissions",children:"Permissions"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Permission"}),(0,r.jsx)(n.th,{children:"Lets you"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"workflows:read"})}),(0,r.jsxs)(n.td,{children:["View runs, schedules, and the ",(0,r.jsx)(n.strong,{children:"Workflow Runs"})," board."]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"workflows:trigger"})}),(0,r.jsx)(n.td,{children:"Start runs, cancel them, answer approvals, and pause or resume a schedule, without editing the workflow."})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"workflows:write"})}),(0,r.jsxs)(n.td,{children:["Everything ",(0,r.jsx)(n.code,{children:"workflows:trigger"})," allows."]})]})]})]}),"\n",(0,r.jsx)(n.p,{children:"Creating and editing the workflow definition, applying a schedule, and managing the webhook secret require permission to edit the workflow item."}),"\n",(0,r.jsx)(n.h3,{id:"run-it-by-hand",children:"Run it by hand"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["Open the workflow and click ",(0,r.jsx)(n.strong,{children:"Run"}),". The button is disabled when ",(0,r.jsx)(n.strong,{children:"Run button in this website"})," is off."]}),"\n",(0,r.jsxs)(n.li,{children:["Fill in the declared inputs. Booleans are switches, numbers are numeric fields, and ",(0,r.jsx)(n.code,{children:"object"})," inputs take JSON."]}),"\n",(0,r.jsxs)(n.li,{children:["Click ",(0,r.jsx)(n.strong,{children:"Start"}),". The run starts in the background and you are taken to its page."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"run-it-on-a-schedule",children:"Run it on a schedule"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["In ",(0,r.jsx)(n.strong,{children:"Settings"}),", turn on ",(0,r.jsx)(n.strong,{children:"Schedule"}),", write a five-field ",(0,r.jsx)(n.strong,{children:"Cron"})," expression (for example ",(0,r.jsx)(n.code,{children:"0 7 * * 1-5"}),"), and choose the ",(0,r.jsx)(n.strong,{children:"Time zone"}),". The schedule follows the zone's daylight saving time."]}),"\n",(0,r.jsxs)(n.li,{children:["Under ",(0,r.jsx)(n.strong,{children:"Inputs for scheduled runs"}),", give a value to every required input without a default: a scheduled run has nobody to fill in the Run form, and the engine refuses a schedule that cannot supply a required input."]}),"\n",(0,r.jsx)(n.li,{children:'Save. The schedule is applied to the engine on save;
1 if that fails, the save warns you with "Saved, but the schedule could not be applied".'}),"\n",(0,r.jsxs)(n.li,{children:["From the ",(0,r.jsx)(n.strong,{children:"Schedules"})," tab of the workflow, check ",(0,r.jsx)(n.strong,{children:"Cron"}),", ",(0,r.jsx)(n.strong,{children:"Next run"}),", ",(0,r.jsx)(n.strong,{children:"Last run"}),", and ",(0,r.jsx)(n.strong,{children:"State"}),", and pause or resume the schedule."]}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"If the previous scheduled run is still running when the next one is due, the new firing is skipped."}),"\n",(0,r.jsx)(n.h3,{id:"run-it-from-a-webhook",children:"Run it from a webhook"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["In ",(0,r.jsx)(n.strong,{children:"Settings"}),", turn on ",(0,r.jsx)(n.strong,{children:"REST webhook"})," and save."]}),"\n",(0,r.jsxs)(n.li,{children:["Open the ",(0,r.jsx)(n.strong,{children:"Webhook"})," tab of the workflow, copy the ",(0,r.jsx)(n.strong,{children:"URL"}),", and click ",(0,r.jsx)(n.strong,{children:"Create secret"}),". Copy the secret: it is shown only once."]}),"\n",(0,r.jsx)(n.li,{children:"Configure the calling system to send a JSON object whose keys are the workflow inputs, signed with the secret."}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["The request body ",(0,r.jsx)(n.em,{children:"is"})," the inputs, with no envelope. The signature is the hex-encoded HMAC-SHA256 of the raw body, sent as ",(0,r.jsx)(n.code,{children:"X-Hub-Signature-256: sha256=<signature>"}),", the scheme GitHub webhooks use:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:'WEBHOOK_URL=\'https://<ai-foundry-host>/api/workflow-webhooks/<organization>/<tenant>/issue-triage\'\nBODY=\'{"owner":"acme","repo":"shop","issueNumber":42,"issueTitle":"Checkout fails on Safari"}\'\nSIGNATURE="sha256=$(printf \'%s\' "$BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | awk \'{print $NF}\')"\n\ncurl -X POST "$WEBHOOK_URL" \\\n  -H \'Content-Type: application/json\' \\\n  -H "X-Hub-Signature-256: $SIGNATURE" \\\n  --data-raw "$BODY"\n'})}),"\n",(0,r.jsxs)(n.p,{children:["The endpoint answers ",(0,r.jsx)(n.code,{children:"202 Accepted"})," with the ",(0,r.jsx)(n.code,{children:"workflowId"}),", ",(0,r.jsx)(n.code,{children:"runId"}),", and ",(0,r.jsx)(n.code,{children:"workflow"})," of the new run, without waiting for it to finish. Sign exactly the bytes you send: reformatting the JSON after signing breaks the signature."]}),"\n",(0,r.jsxs)(n.p,{children:["To rotate the secret, click ",(0,r.jsx)(n.strong,{children:"Rotate secret"})," and confirm: the previous secret stops working immediately, so update the caller right away. ",(0,r.jsx)(n.strong,{children:"Delete"})," removes the secret, and the webhook then refuses every call until you create a new one."]}),"\n",(0,r.jsx)(n.h2,{id:"test-and-monitor-runs",children:"Test and monitor runs"}),"\n",(0,r.jsx)(n.h3,{id:"validate-and-run-with-test-inputs",children:"Validate and run with test inputs"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["Click ",(0,r.jsx)(n.strong,{children:"Check"})," on the ",(0,r.jsx)(n.strong,{children:"Workflow"})," step and fix every problem and unreachable step."]}),"\n",(0,r.jsx)(n.li,{children:"For the first runs, point the workflow at test resources: a sandbox repository, a test API Credential, a test channel. Inputs make this easy, because you can pass a test repository to a manual run without editing the workflow."}),"\n",(0,r.jsx)(n.li,{children:"Keep a manual trigger on while you test, even for a workflow meant to run on a schedule or webhook, so you can start runs with inputs you control."}),"\n",(0,r.jsxs)(n.li,{children:["Add an ",(0,r.jsx)(n.strong,{children:"Approval"})," step before irreversible actions while you test. You can remove it once the workflow is trusted."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"read-the-run-page",children:"Read the run page"}),"\n",(0,r.jsx)(n.p,{children:"The run page refreshes on its own while the run is in progress. It shows:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Started"}),", ",(0,r.jsx)(n.strong,{children:"Finished"}),", ",(0,r.jsx)(n.strong,{children:"Trigger"})," (manual, schedule, or webhook), ",(0,r.jsx)(n.strong,{children:"Started by"}),", and, when the workflow declares one, the ",(0,r.jsx)(n.strong,{children:"Worksp
1ace"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Input"}),": the inputs the run received, after defaults were applied and undeclared keys were dropped."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Steps"}),": the timeline, with each step's status, type, duration, number of attempts when it was retried, error message, and output. Agent answers are shown under ",(0,r.jsx)(n.strong,{children:"Answer"}),"; you can switch an output between ",(0,r.jsx)(n.strong,{children:"Preview"})," and ",(0,r.jsx)(n.strong,{children:"Data"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Result"}),": the output of the last successful step."]}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"A step reached more than once, in a cycle, shows its latest execution."}),"\n",(0,r.jsxs)(n.p,{children:["If the page shows ",(0,r.jsx)(n.strong,{children:"The step-by-step timeline is unavailable"}),", no workflow worker answered. The status, timings, and result still come from the run history; contact your administrator if it persists."]}),"\n",(0,r.jsx)(n.h3,{id:"approve-refuse-or-cancel",children:"Approve, refuse, or cancel"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["When the run reaches an ",(0,r.jsx)(n.strong,{children:"Approval"})," step, the step shows the question, an optional note, and the ",(0,r.jsx)(n.strong,{children:"Approve"})," and ",(0,r.jsx)(n.strong,{children:"Refuse"})," buttons. The decision, the note, and who decided are recorded in the step output and shown as ",(0,r.jsx)(n.strong,{children:"Decided by"}),". Runs waiting for an approval are also highlighted on the home page."]}),"\n",(0,r.jsxs)(n.li,{children:["To stop a running run, click ",(0,r.jsx)(n.strong,{children:"Cancel"})," and confirm. A step already in progress finishes first, and the timeline records where the run stopped."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"watch-all-runs",children:"Watch all runs"}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.strong,{children:"Workflow Runs"})," page in the ",(0,r.jsx)(n.strong,{children:"Observability"})," section is a live board of runs across all workflows, in three columns: ",(0,r.jsx)(n.strong,{children:"Running"}),", ",(0,r.jsx)(n.strong,{children:"Waiting"})," (stopped on an approval), and ",(0,r.jsx)(n.strong,{children:"Done"}),". Filter it by workflow, pause ",(0,r.jsx)(n.strong,{children:"Live"})," refresh, and open any card to reach its run page. The ",(0,r.jsx)(n.strong,{children:"Executions"})," tab of a workflow lists the runs of that workflow only."]}),"\n",(0,r.jsx)(n.h3,{id:"debug-a-failed-step",children:"Debug a failed step"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsx)(n.li,{children:"Open the run and expand the failed step: its error message is the innermost cause, for example the HTTP status and response of the called API."}),"\n",(0,r.jsxs)(n.li,{children:["Check the ",(0,r.jsx)(n.strong,{children:"Input"})," tab: an empty field in a later step often comes from a placeholder that named a missing input or field, which resolves to an empty value."]}),"\n",(0,r.jsxs)(n.li,{children:["For a failed command, read ",(0,r.jsx)(n.code,{children:"stdout"})," and ",(0,r.jsx)(n.code,{children:"stderr"})," in the step output."]}),"\n",(0,r.jsxs)(n.li,{children:["For a Playbook step, trace the agent turn in ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/observability/ai-sessions#platform-sessions",children:"Platform Sessions"}),": filter by the playbook and by ",(0,r.jsx)(n.strong,{children:"User ID"}),". A manual run is attributed to the person who started it; scheduled and webhook runs are attributed to ",(0,r.jsx)(n.code,{children:"workflow:<workflow-name>"}),". The session id starts with ",(0,r.jsx)(n.code,{children:"wf-"})," followed by the run id. For latency and errors across services, use ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/observability/ai-providers#ai-traces",children:"AI Traces"}),"."]}),"\n",(0,r.jsx)(n.li,{children:"Fix the workflow and start a new run. A run always executes the definition it started with."}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"understand-versioning",children:"Understand versioning"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"A run keeps executing the workflow definition it started with, even if you edit the workflow while it runs or waits for an approval."}),"\n",(0,r.jsxs)(n.li,{children:["A scheduled run executes the definition the schedule was last applied with. If the ",(0,r.jsx)(n.strong,{children:"Schedules"})," tab shows ",(0,r.jsx)(n.strong,{children:"The schedule running in the engine is not the one this workflow describes."}),", click ",(0,r.jsx)(n.strong,{children:"Apply"}),"."]}),"\n",(0,r.jsx)(n.li,{children:"A run stops after 500 step executions, which protects against cycles without an exit."}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"pre-production-checklist",children:"Pre-production checklist"}),"\n",(0,r.jsxs)(n.ul,{className:"contains-task-list",children:["\n",(0,r.jsxs)(n.li,{className:"task-list-item",children:[(0,r.jsx)(n.input,{type:"checkbox",disabled:!0})," ",(0,r.jsx)(n.strong,{children:"Check"})," reports the workflow as valid with all steps reachable from ",(0,r.jsx)(n.strong,{children:"Start"}),"."]}),"\n",(0,r.jsxs)(n.li,{className:"task-list-item",children:[(0,r.jsx)(n.input,{type:"checkbox",disabled:!0})," ","Every required input has a clear description; scheduled runs have values for all of them."]}),"\n",(0,r.jsxs)(n.li,{className:"task-list-item",children:[(0,r.jsx)(n.input,{type:"checkbox",disabled:!0}
1)," ","Every Playbook step names an enabled, tested playbook and has a timeout long enough for its tools."]}),"\n",(0,r.jsxs)(n.li,{className:"task-list-item",children:[(0,r.jsx)(n.input,{type:"checkbox",disabled:!0})," ","Agent answers used by a condition contain a fixed marker, and both branches have been tested."]}),"\n",(0,r.jsxs)(n.li,{className:"task-list-item",children:[(0,r.jsx)(n.input,{type:"checkbox",disabled:!0})," ","Steps that create resources run once (",(0,r.jsx)(n.code,{children:"Attempts"})," set to ",(0,r.jsx)(n.code,{children:"1"}),") and have an ",(0,r.jsx)(n.code,{children:"onError"})," branch."]}),"\n",(0,r.jsxs)(n.li,{className:"task-list-item",children:[(0,r.jsx)(n.input,{type:"checkbox",disabled:!0})," ","No secret is written in a header or body; every external call uses an API Credential."]}),"\n",(0,r.jsxs)(n.li,{className:"task-list-item",children:[(0,r.jsx)(n.input,{type:"checkbox",disabled:!0})," ","Approvals have a sensible ",(0,r.jsx)(n.strong,{children:"Give up after (seconds)"})," and ",(0,r.jsx)(n.strong,{children:"If nobody answers"})," policy."]}),"\n",(0,r.jsxs)(n.li,{className:"task-list-item",children:[(0,r.jsx)(n.input,{type:"checkbox",disabled:!0})," ","The workspace TTL covers the time between the first workspace step and the end of the run."]}),"\n",(0,r.jsxs)(n.li,{className:"task-list-item",children:[(0,r.jsx)(n.input,{type:"checkbox",disabled:!0})," ","The webhook secret is stored in the calling system's secret manager, and the team knows how to rotate it."]}),"\n",(0,r.jsxs)(n.li,{className:"task-list-item",children:[(0,r.jsx)(n.input,{type:"checkbox",disabled:!0})," ","Test runs have been run end to end with test resources, including a refused approval and a failing step."]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"troubleshooting",children:"Troubleshooting"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Symptom"}),(0,r.jsx)(n.th,{children:"Cause"}),(0,r.jsx)(n.th,{children:"Fix"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsxs)(n.td,{children:["Starting a run fails with ",(0,r.jsx)(n.code,{children:"Input '<name>' is required"})]}),(0,r.jsx)(n.td,{children:"A required input has no value and no default."}),(0,r.jsxs)(n.td,{children:["Supply the input, or give it a default. For schedules, fill in ",(0,r.jsx)(n.strong,{children:"Inputs for scheduled runs"}),"."]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsxs)(n.td,{children:["Starting a run fails because a playbook ",(0,r.jsx)(n.code,{children:"does not exist in this tenant"}),", ",(0,r.jsx)(n.code,{children:"is disabled"}),", or ",(0,r.jsx)(n.code,{children:"carries no agent"})]}),(0,r.jsx)(n.td,{children:"A Playbook step names a missing or disabled playbook, or one with an empty flow."}),(0,r.jsx)(n.td,{children:"Fix or enable the playbook, or point the step at another one."})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsxs)(n.td,{children:["A step fails with ",(0,r.jsx)(n.code,{children:"no API credential named '<name>' is visible to this tenant"})]}),(0,r.jsx)(n.td,{children:"The API Credential does not exist in your tenant."}),(0,r.jsx)(n.td,{children:"Create the credential, or fix the name in the step."})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsxs)(n.td,{children:["An HTTP request step fails with ",(0,r.jsx)(n.code,{children:"Refusing to connect to a non-public or unresolvable address."})]}),(0,r.jsx)(n.td,{children:"The URL resolves to a private or unknown address."}),(0,r.jsx)(n.td,{children:"Use a public address, or ask your administrator whether private addresses can be allowed."})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:'A condition always takes the "no" branch'}),(0,r.jsxs)(n.td,{children:["The value is missing, is not a number for ",(0,r.jsx)(n.code,{children:"gt"}),"/",(0,r.jsx)(n.code,{children:"lt"})," comparisons, or does not match exactly (",(0,r.jsx)(n.code,{children:"contains"})," is case-sensitive)."]}),(0,r.jsx)(n.td,{children:"Check the step output in the run page and fix the placeholder or the marker."})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"A pull request or other resource was created twice"}),(0,r.jsx)(n.td,{children:"The step was retried after a timeout."}),(0,r.jsxs)(n.td,{children:["Set ",(0,r.jsx)(n.strong,{children:"Attempts"})," to ",(0,r.jsx)(n.code,{children:"1"})," for non-repeatable actions and add an ",(0,r.jsx)(n.code,{children:"onError"})," branch."]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsxs)(n.td,{children:["A run fails with ",(0,r.jsx)(n.code,{children:"this run executed 500 steps without finishing"})]}),(0,r.jsx)(n.td,{children:"The graph has a cycle with no exit."}),(0,r.jsx)(n.td,{children:"Add a condition that leaves the cycle."})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsxs)(n.td,{children:["A webhook call answers ",(0,r.jsx)(n.code,{children:"401"})]}),(0,r.jsxs)(n.td,{children:["The ",(0,r.jsx)(n.code,{children:"X-Hub-Signature-256"})," header is missing, or it was computed with another secret or on different bytes."]}),(0,r.jsx)(n.td,{children:"Sign the exact raw body with the current secret."})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsxs)(n.td,{children:["A webhook call answers ",(0,r.jsx)(n.code,{children:"409"})]}),(0,r.jsxs)(n.td,{children:["The ",(0,r.jsx)(n.strong,{children:"REST webhook"})," trigger is off, or no secret has been created."]}),(0,r.jsxs)(n.td,{children:["Turn on the trigger and create a secret in the ",(0,r.jsx)(n.strong,{children:"Webhook"})," tab."]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsxs)(n.td,{children:["A webhook call answers ",(0,r.jsx)(n.code,{children:"400"})]}),(0,r.jsx)(n.td,{children:"The body is not a JSON object, or a required input is missing."}),(0,r.jsx)(n.td,{children:"Send a JSON object with all required inputs."})]})]})]}),"\n",(0,r.jsx)(n.h2,{id:"related-resources",children:"Related resources"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/basic-concepts/workflow",children:"Agentic Workflow"}
1),": the reference for spec fields, steps, triggers, and templating."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/orchestration/build-playbooks",children:"Build, run, and test playbooks"}),": design the playbooks that Playbook steps call."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/basic-concepts/agent",children:"Agent"}),": agent configuration, including workspace permissions."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/observability/ai-sessions",children:"AI Sessions"})," and ",(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/observability/ai-providers",children:"AI Providers"}),": trace agent steps and follow workflow runs."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"/docs/15.2.0/products/ai-foundry/overview#agentic-workflows",children:"AI Foundry Overview"}),": a summary of agentic workflows and API Credentials."]}),"\n"]})]})}function c(e={}){let{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(h,{...e})}):h(e)}},28453(e,n,s){s.d(n,{R:()=>o,x:()=>d});var t=s(296540);let r={},i=t.createContext(r);function o(e){let n=t.useContext(i);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function d(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),t.createElement(i.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.