🧭 How toService ManagementIntermediate✨ AI-assisted

How to Schedule Ticket Triage With Foundry Agent Routines

WittyTech Daily··5 min read
#microsoft foundry#agent routines#ticket triage#scheduling

Every weekday your service desk opens to a pile of tickets nobody has sorted. Foundry agent routines let you point an agent you already deployed at that pile on a cron schedule, with no scheduler, queue or Logic App in between. By the end you'll have a routine that runs a ticket triage agent at 07:00 on weekdays, a test run you can fire on demand, and a way to check that the agent really finished.

A routine is one trigger plus one action: when the trigger fires, Foundry invokes one prompt agent or hosted agent. Workflow agents aren't supported, and if you need branching or human approval you want a workflow instead. The routines concept page covers the model.

1. Check the role, region and SDK for Microsoft Foundry routines

You need:

  • A Foundry project with at least one deployed prompt or hosted agent that can already reach your ticket queue through its tools.
  • The Foundry User role (or higher) on the project. It was previously called Azure AI User.
  • A project outside UK West, Switzerland West, Japan West, UAE North and Norway East. Routines don't support customer-managed key encryption either.
pip install "azure-ai-projects>=2.4.0" azure-identity
az login

One honest caveat: the SDKs expose routines under a beta namespace (client.beta.routines), and the .NET route needs beta packages because stable 2.0.1 has no routines API. Treat the surface as one that can still change, and pin your version. The documented REST calls need only a bearer token and api-version=v1. I found no extra feature-flag header in the how-to article, so none is shown here.

2. Decide the identity and the prompt

By default a routine calls your agent with the agent identity, so the agent's own permissions decide which tickets it can read and change. If a tool needs delegated user access, you can opt into the creator identity at creation. You can't switch later: delete and recreate the routine.

The routine's input is the prompt sent on every run. Keep it to the task, and never put secrets or tokens in it.

3. Create the weekday schedule in the Foundry portal

  1. Open your project in Microsoft Foundry and select Routines, then + New routine.
  2. Enter a Name, pick your Agent and write the Prompt.
  3. Set Type to Recurring schedule, choose Daily or Weekly, and set the Time.
  4. Select Create & start.

The portal reads the time in your browser's time zone. If Routines isn't in the navigation, the feature isn't enabled for your region or subscription.

4. Schedule the AI agent on Azure with Python or REST

For a weekday-only cron and a pinned time zone, use code. Here is the Python SDK:

import os
from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient

client = AIProjectClient(
    endpoint=os.environ["PROJECT_ENDPOINT"],  # https://<account>.services.ai.azure.com/api/projects/<project>
    credential=DefaultAzureCredential(),
)

routine = client.beta.routines.create_or_update(
    routine_name="backlog-triage-weekdays",
    description="Triage the open ticket backlog before the desk opens.",
    enabled=True,
    triggers={
        "weekday-morning": {
            "type": "schedule",
            "cron_expression": "0 7 * * 1-5",
            "time_zone": "America/New_York",
        }
    },
    action={
        "type": "invoke_agent_responses_api",
        "agent_name": os.environ["AGENT_NAME"],
        "input": "Triage every untriaged ticket in the backlog: set category and priority, and flag possible major incidents. Skip tickets that already have a priority.",
    },
)
print(routine.name, routine.enabled)

The same call over REST:

PROJECT_ENDPOINT="https://<account>.services.ai.azure.com/api/projects/<project>"
AGENT_NAME="<your-agent-name>"
TOKEN=$(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)

curl -sS -X PUT "$PROJECT_ENDPOINT/routines/backlog-triage-weekdays?api-version=v1" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Triage the open ticket backlog before the desk opens.",
    "enabled": true,
    "triggers": {
      "weekday-morning": {
        "type": "schedule",
        "cron_expression": "0 7 * * 1-5",
        "time_zone": "America/New_York"
      }
    },
    "action": {
      "type": "invoke_agent_responses_api",
      "agent_name": "'"$AGENT_NAME"'",
      "input": "Triage every untriaged ticket in the backlog. Skip tickets that already have a priority."
    }
  }'

cron_expression is five fields, so 0 7 * * 1-5 means 07:00 Monday to Friday. time_zone is required and takes an IANA or Windows zone name. Calling create_or_update again with the same name edits the routine.

5. Fire a test run before the schedule does

result = client.beta.routines.dispatch(
    routine_name="backlog-triage-weekdays",
    payload={
        "type": "invoke_agent_responses_api",
        "input": "Dry run: list what you would change and change nothing.",
    },
)
print(result.dispatch_id, result.task_id)

The payload type must match the routine's action type. Leave payload out to run the saved input. In the portal, the Test run button does the same.

6. Read the run history without trusting "completed"

for run in client.beta.routines.list_runs("backlog-triage-weekdays"):
    print(run.id, run.phase, run.attempt_source, run.started_at, run.ended_at)
    if run.phase == "failed":
        print("  error:", run.error_type, run.error_message)

A completed phase means the agent endpoint returned a 2xx for the dispatch, nothing more. Responses API actions run in background mode, and the 30-second timeout covers acceptance of the request, not the agent finishing its work. Open the run's response ID in the portal (REST run records carry response_id too) to see the response and trace, and check the ticket system itself after 07:15 for tickets still untriaged.

Gotchas

  • Five-minute floor. A cron that resolves to a shorter interval, such as */2 * * * *, is rejected.
  • Retries. Delivery makes up to five attempts, retrying timeouts, 408, 429 and 5xx. Other 4xx errors fail the run immediately. Because a retry can repeat a request, make triage idempotent, as the prompt above does by skipping tickets that already have a priority.
  • One trigger, one action. Separate queues or schedules mean separate routines.
  • Pause fast. client.beta.routines.disable("backlog-triage-weekdays") stops the schedule without deleting anything.

A scheduled agent that reports success while its work sits half done is worse than no agent, so verify outcomes in the ticket system, not the run status.

← More in Service Management