> ## Documentation Index
> Fetch the complete documentation index at: https://hobbyist-e43fa225.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy a hosted agent

> Deploy your containerized agent code to Foundry Agent Service using the Python SDK or REST API.

export const ZonePivot = ({group, options = [], defaultValue, label = "Choose an experience"}) => {
  const values = options.map(option => option.id);
  const optionKey = options.map(option => `${option.id}:${option.title}`).join("|");
  const [activePivot, setActivePivot] = useState(defaultValue || values[0]);
  const slugify = value => value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
  const resolvePivot = () => {
    if (typeof window === "undefined") return defaultValue || values[0];
    const params = new URLSearchParams(window.location.search);
    const requested = params.get("pivots");
    if (requested) {
      const requestedIds = requested.split(",").map(value => value.trim()).filter(Boolean);
      const match = requestedIds.find(id => values.includes(id));
      if (match) return match;
    }
    const hash = window.location.hash.replace(/^#/, "");
    if (hash) {
      const match = options.find(option => option.id === hash || slugify(option.title) === hash);
      if (match) return match.id;
    }
    try {
      const stored = window.localStorage.getItem(`foundry-zone-pivot:${group}`);
      if (values.includes(stored)) return stored;
    } catch {
      return defaultValue || values[0];
    }
    return defaultValue || values[0];
  };
  const publishPivotChange = value => {
    if (typeof window === "undefined") return;
    window.dispatchEvent(new CustomEvent("foundry-zone-pivot-change", {
      detail: {
        group,
        value
      }
    }));
  };
  const syncTableOfContents = () => {
    if (typeof window === "undefined") return;
    window.requestAnimationFrame(() => {
      const toc = document.getElementById("table-of-contents-content");
      if (!toc) return;
      const links = Array.from(toc.querySelectorAll('a[href^="#"]'));
      for (const link of links) {
        const item = link.closest("li");
        const rawId = link.getAttribute("href")?.slice(1);
        if (!item || !rawId) continue;
        let id = rawId;
        try {
          id = decodeURIComponent(rawId);
        } catch {}
        item.style.display = document.getElementById(id) ? "" : "none";
      }
    });
  };
  useEffect(() => {
    const resolvedPivot = resolvePivot();
    setActivePivot(resolvedPivot);
    publishPivotChange(resolvedPivot);
    window.setTimeout(syncTableOfContents, 0);
  }, [group, defaultValue, values.join("|"), optionKey]);
  const selectPivot = value => {
    setActivePivot(value);
    if (typeof window !== "undefined") {
      try {
        window.localStorage.setItem(`foundry-zone-pivot:${group}`, value);
      } catch {}
      const url = new URL(window.location.href);
      const current = url.searchParams.get("pivots");
      const preserved = current ? current.split(",").map(id => id.trim()).filter(id => id && !values.includes(id)) : [];
      url.searchParams.set("pivots", [...preserved, value].join(","));
      window.history.replaceState(null, "", `${url.pathname}${url.search}${url.hash}`);
    }
    publishPivotChange(value);
    window.setTimeout(syncTableOfContents, 0);
  };
  if (options.length < 2) return null;
  return <div className="not-prose my-6 border-b border-slate-200 pb-3 dark:border-slate-800">
      <div className="mb-2 text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">
        {label}
      </div>
      <div className="flex flex-wrap gap-2" role="tablist" aria-label={label}>
        {options.map(option => {
    const selected = option.id === activePivot;
    return <button key={option.id} type="button" role="tab" aria-selected={selected} onClick={() => selectPivot(option.id)} className={`rounded-md border px-3 py-1.5 text-sm font-medium transition ${selected ? "border-slate-900 bg-slate-900 text-white shadow-sm dark:border-slate-100 dark:bg-slate-100 dark:text-slate-950" : "border-slate-200 bg-white text-slate-700 hover:border-slate-400 hover:text-slate-950 dark:border-slate-700 dark:bg-slate-950 dark:text-slate-200 dark:hover:border-slate-500"}`}>
              {option.title}
            </button>;
  })}
      </div>
    </div>;
};

export const ZoneContent = ({group, value, options = [], values = [], defaultValue, children}) => {
  const optionKey = options.map(option => `${option.id}:${option.title}`).join("|");
  const [activePivot, setActivePivot] = useState(defaultValue || values[0]);
  const slugify = value => value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
  const resolvePivot = () => {
    if (typeof window === "undefined") return defaultValue || values[0];
    const params = new URLSearchParams(window.location.search);
    const requested = params.get("pivots");
    if (requested) {
      const requestedIds = requested.split(",").map(value => value.trim()).filter(Boolean);
      const match = requestedIds.find(id => values.includes(id));
      if (match) return match;
    }
    const hash = window.location.hash.replace(/^#/, "");
    if (hash) {
      const match = options.find(option => option.id === hash || slugify(option.title) === hash);
      if (match) return match.id;
    }
    try {
      const stored = window.localStorage.getItem(`foundry-zone-pivot:${group}`);
      if (values.includes(stored)) return stored;
    } catch {
      return defaultValue || values[0];
    }
    return defaultValue || values[0];
  };
  useEffect(() => {
    setActivePivot(resolvePivot());
  }, [group, defaultValue, values.join("|"), optionKey]);
  useEffect(() => {
    const onPivotChange = event => {
      if (event.detail?.group === group && values.includes(event.detail.value)) {
        setActivePivot(event.detail.value);
      }
    };
    window.addEventListener("foundry-zone-pivot-change", onPivotChange);
    return () => window.removeEventListener("foundry-zone-pivot-change", onPivotChange);
  }, [group, values.join("|")]);
  if (activePivot !== value) return null;
  return <>{children}</>;
};

This article shows you how to deploy a containerized agent to Foundry Agent Service by using the Azure Developer CLI (`azd`), the Python SDK, or the REST API. Choose a deployment method by using the selector at the top of the article. Use the SDK or REST approaches when you want to manage agent deployments directly from your own applications or services.

If you're deploying for the first time or want a guided walkthrough, see the [Quickstart: Create and deploy a Hosted agent](/agents/quickstart-hosted-agent). The **Azure Developer CLI (azd)** and **VS Code extension** handle building, pushing, versioning, and RBAC configuration automatically.

<Tip>
  Prefer a Docker-less inner loop? You can also [deploy a hosted agent directly from source code](/get-started/deploy-hosted-agent-code) - upload a `.zip` of your Python or .NET code and the platform builds and hosts it for you.
</Tip>

## Deployment lifecycle

Every Hosted agent deployment follows this sequence:

1. **Build and push** - Package your agent code into a container image and push it to Azure Container Registry.
2. **Create an agent version** - Register the image with Foundry Agent Service. The platform provisions infrastructure and creates a dedicated Entra agent identity.
3. **Poll for status** - Wait for the version status to reach `active`.
4. **Invoke** - Send requests to the agent's dedicated endpoint.

## Prerequisites

* A [Microsoft Foundry project](../../how-to/create-projects).
* Agent code using a [supported framework](/agents/hosted-agents#language-support).
* [Docker Desktop](https://docs.docker.com/get-docker/) installed for local container development.
* [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) version 2.80 or later.

### Required permissions

You need the **Foundry Project Manager** role at the project scope to deploy a hosted agent. This role grants the data-plane permissions to create and update agents, plus the ability to create role assignments for the platform-created agent identity if needed. For a detailed breakdown of the permissions involved, see [Hosted agent permissions reference](/agents/hosted-agent-permissions).

<Info>
  The Foundry RBAC roles were recently renamed. **Foundry User**, **Foundry Owner**, **Foundry Account Owner**, and **Foundry Project Manager** were previously named Azure AI User, Azure AI Owner, Azure AI Account Owner, and Azure AI Project Manager. You might still see the previous names in some places while the rename rolls out. The role IDs and core permissions are unchanged by the rename.
</Info>

The platform creates a dedicated Microsoft Entra agent identity for each hosted agent at deploy time. This identity is a service principal that your running container uses to call models and tools. You don't need to configure managed identities manually. The agent identity can access model inferencing through the project endpoint and session storage by default. For external resources (for example, your own Azure Storage), assign RBAC roles manually to the agent's Microsoft Entra ID. For more information, see [Agent access beyond defaults](/agents/hosted-agent-permissions#agent-access-beyond-defaults).

If you use `azd` or the VS Code extension, the tooling handles most RBAC assignments automatically, including **Container Registry Repository Reader** for the project managed identity (image pulls).

For more information, see [Authentication and authorization](../../concepts/authentication-authorization-foundry).

<Info>
  Support for placing your Hosted agent's Azure Container Registry behind a private network (private endpoint with public network access disabled) depends on when the Foundry project was created. Projects created after June 25, 2026 support a private registry. Projects created before that date require the registry to be reachable over its public endpoint so the platform can pull the image. Existing projects aren't affected. For the full list of network constraints, see [Limitations](virtual-networks#limitations).
</Info>

## Container requirements

Your container image must meet the following requirements to run on the Hosted agent platform.

<Info>
  The hosting platform requires x86\_64 (linux/amd64) container images. If you build on Apple Silicon or other ARM-based machines, use `docker build --platform linux/amd64 .` to avoid producing an incompatible ARM image.
</Info>

### Protocol libraries

Hosted agents communicate with the Foundry gateway through protocol libraries. Choose the protocol that matches your agent's interaction pattern:

| Protocol                    | Python library                     | .NET library                       | Endpoint          | Best for                                                                     |
| --------------------------- | ---------------------------------- | ---------------------------------- | ----------------- | ---------------------------------------------------------------------------- |
| **Responses**               | `azure-ai-agentserver-responses`   | `Azure.AI.AgentServer.Responses`   | `/responses`      | Conversational chatbots, streaming, multi-turn with platform-managed history |
| **Invocations**             | `azure-ai-agentserver-invocations` | `Azure.AI.AgentServer.Invocations` | `/invocations`    | Webhook receivers, non-conversational processing, custom async workflows     |
| **Invocations (WebSocket)** | `azure-ai-agentserver-invocations` | `Azure.AI.AgentServer.Invocations` | `/invocations_ws` | Bidirectional streaming: real-time voice agents, interactive media           |

The WebSocket protocol uses the identifier `invocations_ws` and ships in the same `azure-ai-agentserver-invocations` package as the HTTP `/invocations` route, so one container can serve both. Use it when you need persistent, full-duplex streaming - for example, sending microphone PCM to the agent and receiving synthesized audio back. For voice scenarios, see [Build a voice agent with hosted agents](/agents/build-voice-agent).

A single container can expose **multiple protocols simultaneously** by declaring them when you create the agent - in the `protocols` field of the `azure.ai.agent` service in `azure.yaml`, an SDK call, or a REST API request - and importing the required libraries. Use the protocol libraries within your existing framework, whether that's Microsoft Agent Framework, LangChain, or custom code.

### Responses protocol library

The Python and .NET libraries for the Responses protocol implement the Azure AI Responses API. Import the package and implement the `IResponseHandler` interface. The library handles routing, streaming with server-sent events (SSE), background execution, cancellation, caching, and response lifecycle management.

#### IResponseHandler

`IResponseHandler` is the core abstraction you implement. The library calls `CreateAsync` for each incoming request and delivers the returned `IAsyncEnumerable<ResponseStreamEvent>` to clients through SSE:

```csharp theme={null}
public class EchoHandler : ResponseHandler
{
    public override IAsyncEnumerable<ResponseStreamEvent> CreateAsync(
        CreateResponse request,
        ResponseContext context,
        CancellationToken cancellationToken)
    {
        return new TextResponse(context, request,
            createText: async ct =>
            {
                var input = await context.GetInputTextAsync(cancellationToken: ct);
                return $"Echo: {input}";
            });
    }
}
```

#### ResponseEventStream

`ResponseEventStream` manages `sequenceNumber`, `outputIndex`, `contentIndex`, `itemId`, and the full `Response` lifecycle automatically. Each `yield return` maps one-to-one to an SSE event, so you don't need to track this state yourself.

#### Streaming and background modes

* **Streaming mode** (default): SSE events are delivered in real time to the connected client.
* **Background mode**: The handler runs to completion without a connected SSE client. Events are buffered and available for replay through `GET /responses/{id}`.

#### Response lifecycle

The library orchestrates the complete response lifecycle: `created` -> `in_progress` -> `completed` (or `failed` or `cancelled`). The library also manages cancellation, error handling, and terminal event guarantees automatically.

#### Thread safety

All service instances registered through `AddResponsesServer()` are thread-safe. Handler instances are scoped per-request.

For detailed handler implementation guidance, see the [handler implementation guide](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/agentserver/Azure.AI.AgentServer.Responses/docs/handler-implementation-guide.md). For runnable examples, see the [Responses protocol samples](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/agentserver/Azure.AI.AgentServer.Responses/samples).

### Health endpoints

The protocol libraries automatically expose a `/readiness` endpoint for platform health checks. You don't need to implement this yourself.

### Port

Containers serve traffic on port **8088** locally. In production, the Foundry gateway handles routing - your container doesn't need to expose a public port.

### Platform-injected environment variables

The Hosted agent platform automatically injects environment variables into your container at runtime. Your code can read these variables without declaring them in the `env` map of the `azure.ai.agent` service in `azure.yaml` or in SDK and REST environment variable settings. The `FOUNDRY_*` prefix is reserved for platform use.

| Variable                                | Purpose                                                     |
| --------------------------------------- | ----------------------------------------------------------- |
| `FOUNDRY_PROJECT_ENDPOINT`              | Foundry project endpoint URL                                |
| `FOUNDRY_PROJECT_ARM_ID`                | Foundry project ARM resource ID                             |
| `FOUNDRY_AGENT_NAME`                    | Name of the running agent                                   |
| `FOUNDRY_AGENT_VERSION`                 | Version of the running agent                                |
| `FOUNDRY_AGENT_SESSION_ID`              | Session ID for the current request (hosted containers only) |
| `APPLICATIONINSIGHTS_CONNECTION_STRING` | Application Insights connection string for telemetry        |

Don't redeclare platform-injected variables in `azure.yaml` - they're set automatically.

Variables that you declare yourself, such as `MODEL_DEPLOYMENT_NAME` or toolbox MCP endpoints, go in the `env` map of the `azure.ai.agent` service in `azure.yaml` or the SDK `create_version` call.

<Info>
  When you deploy your hosted agent to Foundry Agent Service, the platform automatically injects an Application Insights connection string into your agent container as an environment variable, enabling OpenTelemetry tracing by default. To view distributed traces, requests, and dependencies, open the Application Insights resource provisioned during setup in the Azure portal and navigate to Investigate > Transaction search or Performance. Use `azd ai agent monitor` for live console logs.  When AppInsights is enabled, this project logs traces to help monitor and evaluate user level interactions with agents. Project members provided with Log Analytics Reader role in AppInsights can view trace data, which might contain personal data and/or Customer Content. If the underlying Log Analytics tables are [protected](https://learn.microsoft.com/azure/azure-monitor/logs/protected-tables-configure), members instead need the [Privileged Monitoring Data Reader](https://learn.microsoft.com/azure/azure-monitor/logs/manage-access) role to view that trace data. Review what trace data is collected and who can view and use this data.  Additional Azure Monitor App Insights [pricing](https://azure.microsoft.com/pricing/details/monitor/) might apply. [Learn more](/observability/trace-data#disable-tracing).
</Info>

### Reference project connections in environment variables

Instead of hard-coding secrets (API keys, tokens, endpoints) into `azure.yaml` or your image, pull them from a Foundry project connection at sandbox start. Any value that you declare as an environment variable can be a placeholder expression that the platform resolves before your container starts.

#### Placeholder syntax

A placeholder has the form `${{connections.<name>.<path>}}`, where `<name>` is the connection's resource name (visible in the portal under **Project details** > **Connected resources**) and `<path>` is one of:

| Path                  | Resolves to                                                       |
| --------------------- | ----------------------------------------------------------------- |
| `credentials.<field>` | A secret field on the connection                                  |
| `target`              | The connection's `target` property (for example, an endpoint URL) |
| `metadata.<field>`    | A field under the connection's `metadata`                         |

The field name to use depends on the connection category:

| Connection category     | Field name in placeholder                                                                       |
| ----------------------- | ----------------------------------------------------------------------------------------------- |
| `ApiKey`, `AppInsights` | Always `key`--for example, `credentials.key`                                                    |
| `CustomKeys`            | The key name you supplied when creating the connection--for example, `credentials.github_token` |

#### Example

First, create a `CustomKeys` connection on the project that holds the secret. See [Add a new connection in Microsoft Foundry](../../how-to/connections-add). Then reference it from the `env` map in the `azure.ai.agent` service in `azure.yaml`:

```yaml theme={null}
services:
  my-agent:
    host: azure.ai.agent
    env:
      MODEL_DEPLOYMENT_NAME: gpt-5-mini
      GITHUB_TOKEN: ${{connections.agent-secrets.credentials.github_token}}
```

At sandbox start, Foundry resolves the placeholder and injects the resolved value as a plain environment variable. Your code reads it like any other env var:

```python theme={null}
import os
token = os.environ["GITHUB_TOKEN"]
```

A GET on the agent version returns the literal `${{...}}` text--the resolved secret is never echoed back through the management API.

#### Considerations

* **Create the connection before you deploy the version.** If the connection or the referenced field is missing at sandbox start, the placeholder doesn't resolve and the variable is empty.
* **Secrets are write-only.** GET on a connection returns `credentials: null`. Verify resolution by reading the env var from inside your running container, not by inspecting the connection.
* **Record `CustomKeys` field names yourself.** The management API never echoes them back after creation. Keep them next to your agent source (for example, in IaC templates or alongside `azure.yaml`) so you can construct placeholders later without guessing.
* **Foundry manages the backing secret name.** When you create the connection, Foundry stores the value in Key Vault under a name it chooses -- you can't reference a preexisting Key Vault secret by name. To attach your own Key Vault as the backing store, see [Set up a Key Vault connection](../../how-to/set-up-key-vault-connection).

## Package and test your agent locally

Before deploying to Foundry, validate your agent works locally using the protocol library. The container serves the same endpoints locally as it does in production.

### Test the Responses protocol

```http theme={null}
POST http://localhost:8088/responses
Content-Type: application/json

{
    "input": "Where is Seattle?",
    "stream": false
}
```

### Test the Invocations protocol

```http theme={null}
POST http://localhost:8088/invocations
Content-Type: application/json

{
    "message": "Hello!"
}
```

<ZonePivot group="azd__python__rest" options={[{"id": "azd", "title": "Azure Developer CLI"}, {"id": "python", "title": "Python"}, {"id": "rest", "title": "REST"}]} defaultValue="azd" />

<ZoneContent group="azd__python__rest" value="azd" options={[{"id": "azd", "title": "Azure Developer CLI"}, {"id": "python", "title": "Python"}, {"id": "rest", "title": "REST"}]} values={["azd", "python", "rest"]} defaultValue="azd">
  ## Deploy using the Azure Developer CLI or VS Code

  The Azure Developer CLI (`azd`) and the Microsoft Foundry Toolkit for Visual Studio Code automate the full deployment lifecycle: building the container, pushing it to Azure Container Registry, creating the agent version, and assigning RBAC roles. For a guided first-time walkthrough, see the [Quickstart: Create and deploy a Hosted agent](/agents/quickstart-hosted-agent).

  ### Deploy with one command

  From your agent project directory, provision infrastructure and deploy in a single step:

  ```bash theme={null}
  azd up
  ```

  `azd up` combines `azd provision`, which creates the Foundry project, model deployment, container registry, Application Insights, and managed identity, with `azd deploy`. Use it for first-time deployments or whenever you change both infrastructure and agent code.

  ### Deploy code changes only

  If you already provisioned your Azure resources and you only need to push a new agent version:

  ```bash theme={null}
  azd deploy
  ```

  During `azd deploy`, the CLI:

  1. Builds your container image remotely in Azure Container Registry, so you don't need local Docker.
  2. Pushes the image to the registry.
  3. Creates a hosted agent version on Foundry Agent Service.
  4. Creates a dedicated Microsoft Entra agent identity and assigns the RBAC roles the agent needs to access models and tools.

  ### Manage versions

  Each `azd deploy` creates a new version of the agent. The CLI preserves previous versions, and the latest version is active by default.

  ### Verify the deployment

  ```bash theme={null}
  azd ai agent show
  ```

  The output includes the agent name, version, protocols, container resources, environment variables, and creation timestamp. Use `--output table` for a summary view.

  ### Build images locally

  By default, `azd` builds container images remotely in Azure Container Registry. To build images locally, set `remoteBuild: false` in `azure.yaml`. Local builds require Docker Desktop.

  To screen prompts and responses against a content safety policy, [add a content safety guardrail to your agent](/agents/add-hosted-agent-guardrails).
</ZoneContent>

<ZoneContent group="azd__python__rest" value="python" options={[{"id": "azd", "title": "Azure Developer CLI"}, {"id": "python", "title": "Python"}, {"id": "rest", "title": "REST"}]} values={["azd", "python", "rest"]} defaultValue="azd">
  ## Deploy using the Python SDK

  Use the SDK when you want to manage agent deployments directly from Python code.

  ### Additional prerequisites

  * [Python 3.10 or later](https://www.python.org/downloads/)
  * A container image in [Azure Container Registry](https://learn.microsoft.com/azure/container-registry/container-registry-get-started-portal)
  * **Container Registry Repository Writer** or **AcrPush** role on the container registry (to push images)
  * Azure AI Projects SDK version 2.3.0 or later

    ```bash theme={null}
    pip install "azure-ai-projects>=2.3.0"
    ```

  ### Build and push your container image

  1. Build your Docker image:

     ```bash theme={null}
     docker build --platform linux/amd64 -t myagent:v1 .
     ```

     See sample Dockerfiles for [Python](https://github.com/microsoft-foundry/foundry-samples/tree/main/samples/python/hosted-agents/agent-framework) and [C#](https://github.com/microsoft-foundry/foundry-samples/blob/main/samples-classic/csharp/getting-started-agents/AgentFramework/AgentsInWorkflows/Dockerfile).

  2. Push to Azure Container Registry:

     ```bash theme={null}
     az acr login --name myregistry
     docker tag myagent:v1 myregistry.azurecr.io/myagent:v1
     docker push myregistry.azurecr.io/myagent:v1
     ```

  <Tip>
    Use unique image tags instead of `:latest` for reproducible deployments.
  </Tip>

  ### Configure container registry permissions

  Grant your project's managed identity access to pull images:

  1. In the [Azure portal](https://portal.azure.com), go to your Foundry project resource.

  2. Select **Identity** and copy the **Object (principal) ID** under **System assigned**.

  3. Assign the **Container Registry Repository Reader** role to this identity on your container registry. See [Azure Container Registry roles and permissions](https://learn.microsoft.com/azure/container-registry/container-registry-roles).

  ### Create a hosted agent version

  When you create a version, the platform automatically provisions the agent. There's no separate start step. The platform builds a container snapshot and makes the agent ready to serve requests.

  ```python theme={null}
  from azure.ai.projects import AIProjectClient
  from azure.ai.projects.models import HostedAgentDefinition, ProtocolVersionRecord, AgentEndpointProtocol, ContainerConfiguration
  from azure.identity import DefaultAzureCredential

  # Format: "https://resource_name.services.ai.azure.com/api/projects/project_name"
  PROJECT_ENDPOINT = "your_project_endpoint"

  # Create project client
  credential = DefaultAzureCredential()
  project = AIProjectClient(
      endpoint=PROJECT_ENDPOINT,
      credential=credential,
  )

  # Create a hosted agent version
  agent = project.agents.create_version(
      agent_name="my-agent",
      definition=HostedAgentDefinition(
          protocol_versions=[
              ProtocolVersionRecord(protocol=AgentEndpointProtocol.RESPONSES, version="1.0.0")
          ],
          cpu="1",
          memory="2Gi",
          container_configuration=ContainerConfiguration(
              image="your-registry.azurecr.io/your-image:tag"
          ),
          environment_variables={
              "MODEL_DEPLOYMENT_NAME": "gpt-5-mini"
          }
      )
  )

  print(f"Agent created: {agent.name}, version: {agent.version}")
  ```

  To expose both protocols, pass both in `protocol_versions`:

  ```python theme={null}
  protocol_versions=[
      ProtocolVersionRecord(protocol=AgentEndpointProtocol.RESPONSES, version="1.0.0"),
      ProtocolVersionRecord(protocol=AgentEndpointProtocol.INVOCATIONS, version="1.0.0"),
      ProtocolVersionRecord(protocol=AgentEndpointProtocol.INVOCATIONS_WS, version="1.0.0"),
  ],
  ```

  Key parameters:

  | Parameter                       | Description                                                           |
  | ------------------------------- | --------------------------------------------------------------------- |
  | `agent_name`                    | Unique name (alphanumeric with hyphens, max 63 characters)            |
  | `container_configuration.image` | Full Azure Container Registry image URL with tag                      |
  | `cpu`                           | CPU allocation (for example, `"1"`)                                   |
  | `memory`                        | Memory allocation (for example, `"2Gi"`)                              |
  | `protocol_versions`             | Protocols the container exposes (`responses`, `invocations`, or both) |

  ### Poll for version status

  After creating a version, poll until the status is `active` before invoking the agent. Provisioning typically takes less than one minute depending on image size.

  ```python theme={null}
  import time

  # Poll until the agent version is active
  while True:
      version_info = project.agents.get_version(
          agent_name="my-agent",
          agent_version=agent.version
      )
      status = version_info["status"]
      print(f"Status: {status}")

      if status == "active":
          print("Agent is ready!")
          break
      elif status == "failed":
          print(f"Provisioning failed: {version_info['error']}")
          break

      time.sleep(5)
  ```

  Version status values:

  | Status     | Description                                               |
  | ---------- | --------------------------------------------------------- |
  | `creating` | Infrastructure provisioning in progress                   |
  | `active`   | Agent is ready to serve requests                          |
  | `failed`   | Provisioning failed - check the `error` field for details |
  | `deleting` | Version is being cleaned up                               |
  | `deleted`  | Version has been fully removed                            |

  ### Invoke the agent

  After the version reaches `active` status, use `get_openai_client` to create an OpenAI client bound to the agent's endpoint.

  For the **Responses** protocol:

  ```python theme={null}
  # Create an OpenAI client bound to the agent endpoint
  openai_client = project.get_openai_client(agent_name="my-agent")

  response = openai_client.responses.create(
      input="Hello! What can you do?",
  )

  print(response.output_text)
  ```

  For the **Invocations** protocol, call the invocations endpoint directly:

  ```python theme={null}
  import requests

  token = credential.get_token("https://ai.azure.com/.default").token
  url = f"{PROJECT_ENDPOINT}/agents/my-agent/endpoint/protocols/invocations"

  response = requests.post(url, headers={
      "Authorization": f"Bearer {token}",
      "Content-Type": "application/json",
  }, params={"api-version": "v1"}, json={
      "message": "Process this task"
  })

  print(response.json())
  ```

  For more complete examples, see the [Hosted agent samples](https://github.com/microsoft-foundry/foundry-samples/tree/main/samples/python/hosted-agents).
</ZoneContent>

<ZoneContent group="azd__python__rest" value="rest" options={[{"id": "azd", "title": "Azure Developer CLI"}, {"id": "python", "title": "Python"}, {"id": "rest", "title": "REST"}]} values={["azd", "python", "rest"]} defaultValue="azd">
  ## Deploy using the REST API

  Use the REST API for direct HTTP-based deployments or when integrating with custom tooling.

  Before you begin, build and push your container image to Azure Container Registry, and grant the project managed identity the **Container Registry Repository Reader** role on the registry.

  ### Set up variables

  ```bash theme={null}
  BASE_URL="https://{account}.services.ai.azure.com/api/projects/{project}"
  API_VERSION="v1"
  TOKEN=$(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)
  ```

  ### Create an agent

  ```bash theme={null}
  curl -X POST "$BASE_URL/agents?api-version=$API_VERSION" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "my-agent",
      "definition": {
        "kind": "hosted",
        "container_configuration": {
          "image": "myacr.azurecr.io/my-agent:v1"
        },
        "cpu": "1",
        "memory": "2Gi",
        "protocol_versions": [
          {"protocol": "responses", "version": "1.0.0"}
        ],
        "environment_variables": {
          "MODEL_DEPLOYMENT_NAME": "gpt-5-mini"
        }
      }
    }'
  ```

  Creating an agent also creates version `1` and triggers provisioning.

  To screen prompts and responses against a content safety policy, include a `rai_config` object in the `definition`. See [Add a content safety guardrail to a hosted agent](/agents/add-hosted-agent-guardrails).

  ### Poll for version status

  Poll the version endpoint until `status` is `active`:

  ```bash theme={null}
  while true; do
    STATUS=$(curl -s -X GET "$BASE_URL/agents/my-agent/versions/1?api-version=$API_VERSION" \
      -H "Authorization: Bearer $TOKEN" | jq -r '.status')
    echo "Status: $STATUS"
    [ "$STATUS" = "active" ] && echo "Ready!" && break
    [ "$STATUS" = "failed" ] && echo "Provisioning failed." && exit 1
    sleep 5
  done
  ```

  ### Invoke the agent

  Use the agent's dedicated endpoint to send requests. Set `"stream": true` to receive server-sent events.

  **Responses protocol:**

  ```bash theme={null}
  curl -X POST "$BASE_URL/agents/my-agent/endpoint/protocols/openai/responses?api-version=$API_VERSION" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "input": "Hello! What can you do?",
      "store": true
    }'
  ```

  **Invocations protocol:**

  ```bash theme={null}
  curl -X POST "$BASE_URL/agents/my-agent/endpoint/protocols/invocations?api-version=$API_VERSION" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "message": "Process this task"
    }'
  ```

  ### Create a new version

  Deploy updated code or configuration by creating a new version:

  ```bash theme={null}
  curl -X POST "$BASE_URL/agents/my-agent/versions?api-version=$API_VERSION" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "definition": {
        "kind": "hosted",
        "container_configuration": {
          "image": "myacr.azurecr.io/my-agent:v2"
        },
        "cpu": "1",
        "memory": "2Gi",
        "protocol_versions": [
          {"protocol": "responses", "version": "1.0.0"}
        ],
        "environment_variables": {
          "MODEL_DEPLOYMENT_NAME": "gpt-5-mini"
        }
      }
    }'
  ```
</ZoneContent>

## Clean up resources

To prevent charges, clean up resources when finished. Agent compute is deprovisioned after 15 minutes of inactivity, so there's no cost when an agent isn't serving requests.

<ZoneContent group="azd__python__rest" value="azd" options={[{"id": "azd", "title": "Azure Developer CLI"}, {"id": "python", "title": "Python"}, {"id": "rest", "title": "REST"}]} values={["azd", "python", "rest"]} defaultValue="azd">
  ### Azure Developer CLI cleanup

  ```bash theme={null}
  azd down
  ```
</ZoneContent>

<ZoneContent group="azd__python__rest" value="python" options={[{"id": "azd", "title": "Azure Developer CLI"}, {"id": "python", "title": "Python"}, {"id": "rest", "title": "REST"}]} values={["azd", "python", "rest"]} defaultValue="azd">
  ### SDK cleanup

  Delete a single version:

  ```python theme={null}
  project.agents.delete_version(agent_name="my-agent", agent_version=agent.version)
  ```

  Or delete the entire agent and all its versions:

  ```python theme={null}
  project.agents.delete(agent_name="my-agent")
  ```
</ZoneContent>

<ZoneContent group="azd__python__rest" value="rest" options={[{"id": "azd", "title": "Azure Developer CLI"}, {"id": "python", "title": "Python"}, {"id": "rest", "title": "REST"}]} values={["azd", "python", "rest"]} defaultValue="azd">
  ### REST API cleanup

  Delete a single version:

  ```bash theme={null}
  curl -X DELETE "$BASE_URL/agents/my-agent/versions/1?api-version=$API_VERSION" \
    -H "Authorization: Bearer $TOKEN"
  ```

  Or delete the entire agent:

  ```bash theme={null}
  curl -X DELETE "$BASE_URL/agents/my-agent?api-version=$API_VERSION" \
    -H "Authorization: Bearer $TOKEN"
  ```

  <Warning>
    Deleting an agent removes all its versions and terminates active sessions. This action can't be undone.
  </Warning>
</ZoneContent>

## Troubleshooting

Provisioning errors surface on the version object's `error.code` and `error.message` fields. Check the version status after creation to identify issues.

| Error code                    | HTTP code | Solution                                                                                                                                                                                                      |
| ----------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `image_pull_failed`           | 400       | Verify the image URI. Confirm that the project managed identity has **Container Registry Repository Reader** on the ACR and that the registry's `azureADAuthenticationAsArmPolicy` policy status is `enabled` |
| `SubscriptionIsNotRegistered` | 400       | Register the subscription provider                                                                                                                                                                            |
| `InvalidAcrPullCredentials`   | 401       | Fix managed identity or registry RBAC                                                                                                                                                                         |
| `UnauthorizedAcrPull`         | 403       | Provide correct credentials or identity                                                                                                                                                                       |
| `AcrImageNotFound`            | 404       | Correct image name/tag or publish image                                                                                                                                                                       |
| `RegistryNotFound`            | 400/404   | Fix registry DNS or network reachability                                                                                                                                                                      |

For 5xx errors, contact Microsoft support.

For detailed RBAC requirements and permission troubleshooting, see [Hosted agent permissions reference](/agents/hosted-agent-permissions).

## Next steps

<Card title="Manage Hosted agent lifecycle" icon="arrow-right" href="manage-hosted-agent.md" />

## Related content

* [What are Hosted agents?](/agents/hosted-agents)
* [Add a content safety guardrail to a hosted agent](/agents/add-hosted-agent-guardrails)
* [Agent identity concepts](/agents/agent-identity)
* [Agent applications](/agents/agent-applications)

- [Azure Container Registry documentation](https://learn.microsoft.com/azure/container-registry/)
