Skip to content
Communicating with Microsoft Foundry via REST API
Albert-Jan Schot
Albert-Jan Schot

· 7 min read

Post

Communicating with Microsoft Foundry via REST API

Most of the time, when you build on Microsoft Foundry, you reach for the SDK. And that’s fine, it’s well-documented, it handles auth for you, it gives you nice typed objects. But every now and then you end up in a situation where you just want to write a few lines of script to poke around an see things.

In a recent project I spent some time doing exactly that, listing agents, inspecting their tools, walking through conversations so I ended up documenting the samples for later use, the whole thing uses nothing more than PowerShell and Invoke-RestMethod.

First: which door are you knocking on?

You can use the az login to sign in and then swap out the audience to get a token for the data plane. There’s the control plane that’s ARM, management.azure.com. It’s where you create the resource, manage role assignments, look at deployments. And there’s the data plane in this case *.services.ai.azure.com which is where the actual agents, conversations and messages live.

The practical consequence: you cannot reuse your ARM token. The audience is different. So the very first call is about getting a token scoped to the right place, and setting a few variables to keep the rest of the calls tidy:

# Get a token — note the audience: https://ai.azure.com/, NOT ARM
$token = (az account get-access-token `
  --resource "https://ai.azure.com/" | ConvertFrom-Json).accessToken


$resourceName = "my-ai-resource"
$projectName  = "my-demo-project"
$agentId      = "my-agent-id"
$apiVersion   = "2025-11-15-preview" # or "2025-05-15-preview" for preview tools

Listing agents, and reading the shape of one

With a token in hand, the rest follows a fairly predictable pattern. The data plane is organised as resource - project - agents, and everything hangs off that. Documentation is pretty good, but some of the stuff is still in beta and the best way to understand it is to just try it out and see what comes back. So let’s start with listing agents, and then getting one specific agent to see what its definition looks like.:

$listUrl = "https://$resourceName.services.ai.azure.com/api/projects/$projectName/agents?api-version=$apiVersion"
$agents = Invoke-RestMethod -Method GET -Uri $listUrl `
  -Headers @{ Authorization = "Bearer $token" }
$agents.data | ConvertTo-Json -Depth 10

With that you can then list a single agent by its ID, and see the full shape of the object that defines it. The Agent ID is the internal name (so not the Display Name you see in the portal, but the one in the URL when you click into it). The full object is pretty big, so I won’t paste it here, but the key part is the versions.latest.definition node, which is where you find out what kind of agent it is, and what tools it has attached.

$getUrl = "https://$resourceName.services.ai.azure.com/api/projects/$projectName/agents/$agentId`?api-version=$apiVersion"
$agent = Invoke-RestMethod -Method GET -Uri $getUrl `
  -Headers @{ Authorization = "Bearer $token" }
$agent | ConvertTo-Json -Depth 10

Two small things worth noticing here, because they’ll save you a confusing ten minutes:

  1. The list endpoint wraps its results in a data array — that’s why it’s $agents.data, not $agents. The single-agent endpoint hands you the object directly.
  2. The backtick before the ? in the single-agent URL ($agentId?api-version) is not a typo. In PowerShell, $agentId?would be read as part of the variable name, so you escape the?` to keep it as a literal.

For an agent, most of the details that matter live in versions.latest.definition. That’s where you find out what kind of agent you’re looking at, and what it can do.

Filtering?

This is a nice example of a question that doesn’t have one answer: how do you narrow down a list? It depends on whether the API does it for you, or whether you do it yourself.

Take “give me only the workflow agents.” There’s no server-side filter for that, so you pull everything and filter client-side on the definition:

$listUrl = "https://$resourceName.services.ai.azure.com/api/projects/$projectName/agents?api-version=$apiVersion"
$agents = Invoke-RestMethod -Method GET -Uri $listUrl `
  -Headers @{ Authorization = "Bearer $token" }
$workflows = $agents.data | Where-Object { $_.versions.latest.definition.kind -eq "workflow" }
$workflows | ConvertTo-Json -Depth 10

Now compare that with conversations, where the API does let you filter on the server with a query parameter (more on that below). Same goal, a shorter list, but one is a Where-Object in your own code, the other is a parameter on the URL. Worth being deliberate about which one you’re using: client-side filtering means you’ve already paid the cost of fetching everything, which is fine for a demo project and less fine when you’re walking thousands of records.

Tools are not a separate endpoint

I went looking for a /tools endpoint and didn’t find one. Turns out you don’t need it. The tools attached to an agent are embedded right in that same latest-version definition:

$getUrl = "https://$resourceName.services.ai.azure.com/api/projects/$projectName/agents/$agentId`?api-version=$apiVersion"
$agent = Invoke-RestMethod -Method GET -Uri $getUrl `
  -Headers @{ Authorization = "Bearer $token" }
$agentTools = $agent.versions.latest.definition.tools
$agentTools | ConvertTo-Json -Depth 10

So once you’ve got the agent, you’ve already got its tools. One call, not two.

Following a conversation end to end

Conversations are where you see the agent actually doing something. And here’s the server-side filter I mentioned: you pass the agentId straight on the query string, and the API only gives you the conversations owned by that agent.

# List conversations for a specific agent — filtered server-side
$convListUrl = "https://$resourceName.services.ai.azure.com/api/projects/$projectName/conversations?api-version=$apiVersion&agentId=$agentId"
$conversations = Invoke-RestMethod -Method GET -Uri $convListUrl `
  -Headers @{ Authorization = "Bearer $token" }
$conversations | ConvertTo-Json -Depth 10

# Get one specific conversation
$conversationId = $conversations.data[0].id   # or hard-code one
$convUrl = "https://$resourceName.services.ai.azure.com/api/projects/$projectName/conversations/$conversationId`?api-version=$apiVersion"
$conversation = Invoke-RestMethod -Method GET -Uri $convUrl `
  -Headers @{ Authorization = "Bearer $token" }
$conversation | ConvertTo-Json -Depth 10

# And the messages inside it
$msgUrl = "https://$resourceName.services.ai.azure.com/api/projects/$projectName/conversations/$conversationId/messages?api-version=$apiVersion"
$messages = Invoke-RestMethod -Method GET -Uri $msgUrl `
  -Headers @{ Authorization = "Bearer $token" }
$messages | ConvertTo-Json -Depth 10

That’s the full drill-down: from which agent, to which conversation, to what was actually said. For debugging “why did the agent do that?”, being able to walk this path by hand is useful (or at least I thought so).

So, that api-version?

Notice that every single call carries api-version, and notice the value: 2025-11-15-preview. That -preview suffix is doing you a big favour.

This is the part I’d flag hardest. Foundry is moving fast, and I mean really fast. Preview API versions are exactly the kind of thing that shifts shape between releases: a field gets renamed, a response wrapper changes, an endpoint moves. So a couple of habits pay off:

  • Pin the version explicitly (which this code already does — good). Don’t rely on a default.
  • Treat preview as preview. Great for R&D and experimentation, but don’t quietly let it become load-bearing in production without a plan for when it changes.
  • When something breaks, check the version first. Half the “it stopped working” reports I’ve seen come down to a preview API that moved underneath the code.

Wrapping up

None of this replaces the SDK for day-to-day building. But knowing how to talk to the data plane directly gives you a few things the SDK doesn’t always make easy: a clear view of what’s actually stored, a fast way to inspect agents and their tools during R&D, and a no-magic path for debugging a conversation that went sideways.

If you want to try this yourself, three practical next steps:

  1. Get a token against https://ai.azure.com/ — confirm you can list agents at all before anything else.
  2. Pick one agent and dump its versions.latest.definition. Read the kind and the tools. That one object tells you most of what you need to know.
  3. Follow one conversation all the way to its messages. Once you’ve done it once, the pattern sticks.

Sometimes the fastest way to understand a platform is to knock on the door yourself and see what answers.

Albert-Jan Schot

Albert-Jan Schot

CTO, Microsoft MVP & FastTrack Recognized Solution Architect

I am Albert-Jan Schot, CTO at Blis Digital, Microsoft MVP, and FastTrack Recognized Solution Architect focused on Microsoft 365, Azure, and AI agents. I help teams turn complex Microsoft Cloud challenges into practical architecture decisions and shipped outcomes.

Copilot Studio Microsoft 365 Agent Flows

Zuid Holland, Netherlands

Related Posts