Most agents are still waiting in a chat window.
They may be capable of classifying an incident, finding the right documentation, or recommending an owner - but nothing happens until someone remembers to ask. The hard part is no longer always the reasoning. It is noticing that work has arrived, invoking the agent securely, and keeping enough history to understand what happened.
Routines in Microsoft Foundry close that gap. A routine connects a trigger - such as a schedule, timer, GitHub issue, or Microsoft Teams message - to an agent action. Microsoft Foundry queues the invocation, runs the agent, and stores a run record for later inspection.
In this post, we will build an event-driven routine for a familiar developer workflow:
When someone opens a GitHub issue, invoke a triage agent immediately - without waiting for a person to copy the issue into a chat.
Along the way, we will look at the architecture, create the routine with the Azure Developer CLI (azd), test it, inspect its run history, and make an explicit identity decision before putting it into production.
From conversational agents to event-driven agents
A chat-first agent follows a request-response pattern:
- A user opens an interface.
- The user provides a prompt.
- The agent performs work.
- The interaction ends or waits for another prompt.
An event-driven agent starts differently. Work in an external system becomes the prompt.
A newly opened issue, for example, already contains useful context: its title, description, author, repository, labels, and timestamps. A routine can receive that event through an authorized connection and invoke an agent while the context is still fresh.
That changes the agent's role. It is no longer only a place people go for answers. It becomes a participant in an operational workflow.
The routine does not replace the agent. It supplies the managed automation around it:
- Trigger: Defines when work starts.
- Connection: Authenticates the event source.
- Action: Identifies the agent and invocation protocol.
- Dispatch identity: Determines whose permissions are used when the agent and its tools run.
- Run history: Records executions so operators can inspect outcomes.
This separation is useful. The agent owns the reasoning; the routine owns when and how that reasoning begins.
The scenario: triage every new GitHub issue
Our example assumes that a triage agent is already deployed in a Microsoft Foundry project. When an issue opens, the agent should:
- Summarize the issue in two or three sentences.
- Classify it as a bug, feature request, documentation issue, or support question.
- Estimate severity and explain the evidence.
- Recommend an owner or team.
- Identify missing reproduction details.
- Produce a proposed response for a maintainer to review.
Keeping a human review step is intentional. Event-driven does not have to mean unrestricted autonomy. A routine can automate the expensive first pass while a maintainer remains responsible for labels, assignments, and public responses.
Prerequisites
You need:
- An active Microsoft Foundry project.
- The Foundry User role or higher on the project.
- A deployed prompt agent or hosted agent. Workflow agents are not currently supported by routines.
- The Azure Developer CLI.
- A GitHub connection authorized in the Foundry project.
- The routines extension for azd.
Install the extension and confirm that the routine commands are available:
azd extension install azure.ai.routines
azd ai routine --help
Set the project endpoint through your active azd environment or pass it explicitly to each command:
azd env set AZURE_AI_PROJECT_ENDPOINT \
"https://<account>.services.ai.azure.com/api/projects/<project>"
The agent must exist before you attach a routine to it. A routine references an agent; it does not deploy one.
Create the GitHub issue routine
The following command creates an enabled routine named triage-on-open. Replace the placeholders with the connection and repository details from your environment:
azd ai routine create triage-on-open \
--trigger github-issue \
--connection-id "<workspace-connection-id>" \
--owner "<github-owner>" \
--repository "<github-repository>" \
--issue-event opened \
--action agent-invoke \
--agent-name "triage-agent" \
--description "Triage every newly opened GitHub issue"
There are two important type translations in this command:
- The CLI alias github-issue represents the routine trigger type github_issue.
- The CLI alias agent-invoke invokes the agent through the Invocations API.
If your agent uses the Responses API instead, use --action agent-response. Choose the protocol that matches the deployed agent rather than treating the two actions as interchangeable.
For automation that belongs in source control, define the routine as an azure.ai.routine service in azure.yaml. This makes the relationship between the agent and its trigger reproducible across environments:
services:
triage-agent:
host: azure.ai.agent
project: ./agent
triage-on-open:
host: azure.ai.routine
uses:
- triage-agent
description: Triage every newly opened GitHub issue
enabled: true
triggers:
issue-opened:
type: github_issue
connection_id: ${GITHUB_CONNECTION_ID}
owner: ${GITHUB_OWNER}
repository: ${GITHUB_REPOSITORY}
issue_event: opened
action:
type: invoke_agent_invocations_api
agent_name: triage-agent
Deploy the routine after the target agent:
azd deploy triage-on-open --no-prompt
The uses relationship tells azd to order the agent before the routine. Deployment is idempotent: redeploying updates the named routine instead of creating duplicates.
Give the agent a clear triage contract
Automation magnifies ambiguity. A vague instruction that is merely inconvenient in a chat can produce inconsistent work every time an event fires.
Give the triage agent a bounded contract such as:
You are the first-pass triage agent for this repository.
For each newly opened issue:
1. Summarize the reported behavior without adding facts.
2. Classify it as bug, feature, documentation, or support.
3. Assign severity only when the issue contains supporting evidence.
4. List missing information needed to reproduce or route the issue.
5. Recommend an owner from the approved ownership map.
6. Draft a response, but do not publish, close, label, or assign the issue.
Return structured JSON that matches the triage schema.
A useful output contract might look like this:
{
"summary": "The CLI exits when a project endpoint contains an explicit port.",
"category": "bug",
"severity": {
"level": "medium",
"reason": "The issue blocks routine creation but has a documented workaround."
},
"missing_information": [
"Azure Developer CLI version",
"Redacted project endpoint shape",
"Full error output"
],
"recommended_owner": "developer-experience",
"proposed_response": "Thanks for the report. Could you share..."
}
Structured output gives downstream systems something predictable to validate. It also makes evaluation easier: you can test category accuracy, required-field completeness, unsupported severity claims, and whether the agent attempted a prohibited action.
Test before waiting for a real event
Start by checking that Foundry stored the routine you intended:
azd ai routine show triage-on-open --output json
Confirm:
- The routine is enabled.
- The trigger watches the correct owner and repository.
- issue_event is opened.
- The action references the intended agent.
- The connection ID belongs to the expected Foundry project.
You can manually dispatch a routine while testing:
azd ai routine dispatch triage-on-open \
--input '{"test":true,"issue":{"number":123,"title":"Test triage event"}}'
The manual input is a one-time override for that dispatch. It does not replace the event payload or modify the routine's stored configuration.
Inspect recent executions:
azd ai routine run list triage-on-open --top 20
Then open a test issue in the watched repository and inspect the run list again. A production test should verify more than "the agent ran." Check that the correct event started the run, the agent received enough context, tool calls used the intended identity, the output matched the schema, and prohibited actions did not occur.
Choose the dispatch identity deliberately
Every routine uses the agent identity by default. This is usually the better fit for unattended automation because access belongs to the agent rather than to an employee's account.
Use agent identity when the agent's tools authenticate with managed identity, workload identity, or keys and the agent has been granted only the permissions required for the task.
Some tools require delegated user access. In that case, you can create the routine with creator identity. Creator identity means the Microsoft Entra identity of the person or service principal that creates the routine—not the agent publisher, connection creator, latest editor, or user who caused an event.
This distinction has operational consequences:
- If the creator loses access or consent, delegated tool calls can fail.
- Recreating the routine as another principal changes the delegated creator identity.
- The event connection identity is separate from the identity used to dispatch the agent.
- Dispatch identity is a creation-time decision. To switch an existing routine between agent and creator identity, delete and recreate it.
For a GitHub triage flow, a strong starting design is:
- Use a narrowly scoped project connection to receive issue events.
- Use agent identity for Foundry and Azure resources.
- Keep repository-changing actions disabled until evaluation demonstrates reliable behavior.
- Require human approval before posting, assigning, labeling, or closing.
Identity is not a deployment detail. It is part of the automation's behavior and should be reviewed with the same care as the prompt and tool list.
Make failure visible
An event-driven agent can fail even when its reasoning is sound. The connection might expire. The agent might receive an unexpected payload. A tool might lose permission. The output might violate its schema.
Monitor the workflow at four boundaries:
- Trigger: Did the expected event fire the routine exactly once?
- Invocation: Did Foundry invoke the intended agent and protocol?
- Tools: Did tool calls succeed with the intended identity and scope?
- Outcome: Did the output satisfy the triage contract?
Use azd ai routine run list for routine execution history and your agent's Foundry observability data for traces, tool calls, latency, and failures. Preserve representative failures as evaluation cases instead of fixing each incident only in the prompt.
Also design for duplicate delivery. Before taking a repository-changing action, check whether the issue and event have already been processed. Idempotency matters more once an agent can act without a person initiating each run.
Know the current boundaries
Before adopting routines for a regulated or business-critical workload, review the current service constraints:
- Routines support prompt agents and hosted agents, but not workflow agents.
- A recurring schedule has a minimum interval of five minutes.
- GitHub issue triggers support opened and closed issue events.
- Routines inherit the project's networking configuration and can work with virtual-network-secured projects.
- Routines do not currently support customer-managed key encryption.
- Regional availability has exceptions; verify your project's region in the current documentation.
These boundaries can change. Treat the routines documentation as the source of truth when moving from a tutorial to production.
What changes when agents stop waiting?
The most interesting part of this design is not the GitHub trigger. It is the change in operating model.
The agent begins work because the world changed, not because someone opened a chat. That makes trigger scope, identity, output contracts, run history, evaluation, and human approval part of the agent design—not infrastructure to consider later.
Once the triage routine is working, the same pattern can support:
- A Teams message that starts support classification.
- A nightly backlog review.
- A one-time release-readiness check.
- A scheduled compliance summary.
- A hosted agent that uses the reminder tool to resume the same conversation after a long-running task.
Start with one bounded event and one reversible outcome. Measure what the agent does, not merely whether it ran. Then expand its permissions only as evidence earns that autonomy.
Try it next
Choose the path that matches where you are:
- Build: Follow the Microsoft Foundry routines documentation and connect one existing agent to a schedule or event.
- Harden: Review dispatch identity, connection scope, idempotency, output validation, and human approval before enabling repository-changing tools.
- Extend: Add the reminder tool to a hosted agent that needs to continue work later.
- Explore: Open the Microsoft Foundry portal to inspect your project, agents, and routine runs.
Your agent already knows how to do useful work. The next step is teaching it when that work should begin.