# Microsoft gdlchat: Copilot Studio Chat Behind APIM With Fail-Closed Policies

> Source: <https://explainx.ai/blog/microsoft-gdlchat-apim-copilot-governed-chat-2026?utm_source=rss&utm_medium=feed&utm_campaign=blog>
> Published: 2026-11-12 00:00:00+00:00

**Microsoft's new [gdlchat](https://github.com/microsoft/gdlchat?ref=explainx) repository shows how to put **Microsoft Copilot Studio** chat behind **Azure API Management (APIM)** so every user turn passes **subscription auth**, **rate limits**, and **JSON validation** before an agent runs tools.** [Azure API Management's gateway](https://learn.microsoft.com/en-us/azure/api-management/api-management-key-concepts?ref=explainx) verifies API keys, enforces usage quotas and rate limits, and can transform requests through policy statements. The README targets teams that need **governed access** to a copilot — not anonymous Direct Line demos on the public internet.

Agents that act on your behalf need rules. [See how explainx helps secure your agents →](https://explainx.ai/agentbeam?src=blog_note)

## TL;DR

| Setting | Behavior | 
|---|---|
| Default path | Blazor UI → **APIM**`chat-api/messages` → backend → Copilot Studio | 
| `ChatGateway:Enabled=false` | Direct agent mode; **policies skipped** | 
| UI transport | Blazor **WebSockets** at`/chat/_blazor` when`useAPIM=true` | 
| Browser secrets | **No** agent tokens or subscription keys in browser | 
| Auth modes | Direct Line anonymous POC **or** Entra interactive dev sign-in | 
| Fail closed | Missing gateway config → chat errors, no silent fallback | 

## Problem: conversational UIs bypass your API edge

Enterprises already standardize **REST** traffic through APIM for **OAuth**, **quotas**, and **WAF-ish rules**. Copilot Studio samples often terminate **Direct Line** at the app server, leaving **model-chosen tool actions** outside the same envelope.

gdlchat's enforced path:

**Blazor server → APIM POST /chat-api/messages → protected backend POST /chat/api/chat/messages → Copilot Studio → buffered JSON reply → outbound APIM policies → UI**

The browser keeps **only** the Blazor circuit; **policy-enforced HTTP** rides server-side.

Agent permission boundary showing APIM filtering Copilot chat before the agent backend

## Connection modes in the README

**DirectLine default** — anonymous conversation tokens from configured endpoints; suitable for tightly networked demos.

**Sdk mode** — set `CopilotStudioAgent:ConnectionMode` to `Sdk` with environment and schema names; production guidance favors **DefaultAzureCredential** or service principal env vars, never secrets in `appsettings.json`.

**InteractiveBrowser** — development-only Entra delegated sign-in with `CopilotStudio.Copilots.Invoke` scope; first request may open a browser within the 90-second UI timeout.

Zero-trust access gate for governed Copilot Studio chat routed through APIM

## APIM hosting (`useAPIM`)

When `useAPIM=true`, the app uses path base `/chat/`, WebSocket API suffix `chat/_blazor` (not legacy `chat/socket`), and **no long-polling fallback**. APIM tier must support WebSockets (** Consumption does not**).

Operators import `apim/chat-api.openapi.json` as a separate API from the UI APIs, enable **subscription required** on `chat-api`, and keep UI APIs unsubscribed for anonymous POC.

## Policy and secret model

README instructs:

- Generate **64-hex backend secret** stored as APIM named value and Container App secret.
- Issue **dedicated subscription key** for the Blazor server (secret reference env vars).
- Apply `apim/apim-chat-policy.xml` at API scope.

Manual tests in `apim/chat-api.http` cover 401 without subscription, 403 on `policy-block-test`, 400 on schema violations, multi-turn `conversationId` continuity, and 401 on direct backend calls missing `X-Chat-Backend-Key`.

**Important limitation AWS also stresses elsewhere:** inbound blocking stops bad prompts **before** the agent runs; **outbound** blocking cannot undo tools already executed — same class of risk as [Bedrock KB interceptors](https://explainx.ai/blog/aws-bedrock-kb-acl-agentcore-gateway-interceptor-2026).

## POC capacity and logging

Single-replica backend holds **256** conversations, 30-minute idle TTL, **409** on overlapping turns, **410** on unknown handles. Completed replies cap at **128** messages / **32k** characters.

Conversation handles are **capabilities**, not user identities; rate limits apply per **server subscription**, not per anonymous visitor. README warns against logging credentials, Direct Line tokens, or unredacted resumes.

## How this compares to Microsoft agent posts we track

- [FoundryX Copilot skill](https://explainx.ai/blog/microsoft-foundryx-plugin-github-copilot-foundry-agent-skill-canvas-cli-guide-2026) — IDE-side skills.
- [Copilot managed runtime template](https://explainx.ai/blog/microsoft-copilot-managed-runtime-github-repo-vite8-template-claude-code-plugin-ms-cli-guide-2026) — app scaffolding.

gdlchat is **edge policy for published agents** — closest cousin to AWS's **ChatGateway** thinking, but for **Copilot Studio + APIM**.

## Try it safely

1. Deploy APIM **before** exposing Container Apps ingress publicly.
2. Keep `ChatGateway:Enabled=true` in any environment facing untrusted users.
3. Rotate **subscription** and**backend** secrets independently.
4. Add real **content classifiers** via APIM`send-request` policies; the sample's`policy-block-test` string is a**wiring test** , not safety ML.
5. Plan **multi-replica** conversation affinity before production — README says Blazor session affinity alone is insufficient.

## Direct Line transport details builders overlook

The README explains **WebSocket** lifecycle: connect before post, reconnect with **watermark**, filter user echoes by activity ID. **90-second UI cancel** versus **85-second backend** means operators must align timeouts across **APIM policies**, **Container Apps ingress**, and **Copilot Studio** agent settings.

**Plain text rendering** — agent output is not HTML — reduces XSS in chat UI but also means **no rich cards** unless Copilot Studio activities add them server-side.

## Entra application registration checklist (dev mode)

For `InteractiveBrowser` development:

- Redirect `http://localhost` on mobile/desktop platform.
- Delegate permission **CopilotStudio.Copilots.Invoke** with admin consent.
- Never commit **client secrets** for interactive mode.
- WSL users need **Windows browser ↔ WSL localhost** reachability — a recurring failure mode in hybrid dev setups.

## Scaling beyond POC

README is explicit: **single replica**, **in-memory conversation map**, **no durable conversation store**. Before production:

- Externalize conversation state (Redis, Cosmos) with **sticky routing** on APIM**or** partition by conversation ID hash.
- Replace anonymous Direct Line with **Entra-gated** users matching enterprise COPILOT policies.
- Add **WAF rules** on APIM front door separate from JSON schema validation.

## Comparison to Azure API Management patterns elsewhere

Microsoft's own ecosystem increasingly treats **LLM traffic as just another API product** — subscription keys, named values, policy fragments. gdlchat is the **Copilot Studio flavored** reference; [Foundry agents](https://explainx.ai/blog/microsoft-foundryx-plugin-github-copilot-foundry-agent-skill-canvas-cli-guide-2026) cover **GitHub-integrated** agents instead.

## Audit and compliance hooks

For regulated industries, extend policies with:

- **JWT validation** tying chat to corporate IdP, not only subscription keys.
- **PII scan** outbound via APIM`send-request` to a DLP service.
- **Immutable logging** to SIEM with**conversationId** correlation — README warns APIM diagnostics are**sampled** , not full transcripts.

## Licensing and support expectations

gdlchat shipped as **reference code** with **two commits** in the November 12 trending snapshot — not a supported Azure product SKU. Teams should fork with eyes open: you own **patch cadence**, **APIM version drift**, and **Copilot Studio API changes**. Microsoft’s README is unusually detailed; treat it as **spec** for your internal platform team.

## Failure injection tests

Beyond `apim/chat-api.http`, teams should simulate:

- **Expired subscription key** during long conversation.
- **Backend secret mismatch** after rotation half-deployed.
- **Copilot Studio outage** — UI should surface policy-blocked message, not hang until 90s cancel.

## Mapping README settings to operations

| Config key | Ops owner | Typical mistake | 
|---|---|---|
| `ChatGateway__Endpoint` | Platform | Points at prod APIM while UI still dev | 
| `useAPIM` | Frontend | Forgetting trailing slash on public URL | 
| `CopilotStudioAgent:TokenEndpoint` | Copilot admin | Anonymous mode left on in prod | 
| `Logging:LogLevel` for Direct Line | SRE | Verbose HTTP logs leak tokens | 

Run a **config drift scan** each release — gdlchat has many boolean toggles with security impact.

## Edge cases in policy-block-test

The sample blocks a literal substring. Real classifiers need **tokenization-aware rules** — attackers split strings. Plan to replace the test hook with **Azure Content Safety** or a bespoke model before any external pilot.

## Why Microsoft published this now

Copilot Studio adoption created **thousands of anonymous Direct Line endpoints**. Security reviewers asked for **APIM-shaped answers** similar to **API Management + Functions** patterns from the 2010s. gdlchat modernizes that story for **generative turns** instead of REST JSON.

## Procurement and tenant isolation

Multi-tenant SaaS vendors hosting Copilot for clients should **never** share one APIM subscription key across customers. The README's per-server subscription model is a stand-in for **per-tenant credentials** with separate rate limits and audit streams. Align **Azure AD B2B** guests with **per-tenant Copilot environments** before routing everything through one gdlchat deployment — otherwise policy logs commingle customer prompts.

## Disaster recovery

Document **fail-closed behavior** when APIM is unavailable: users should see an explicit maintenance message, not silent fallback to **ChatGateway disabled** mode. Runbooks must forbid toggling gateway bypass under pressure unless executive risk acceptance is recorded.

## Step-by-step hardening path

Week one: deploy README POC with **ChatGateway enabled** and run `apim/chat-api.http` tests. Week two: replace `policy-block-test` with **tenant-specific banned phrases** and PII regexes. Week three: integrate **Entra conditional access** so only managed devices hit `/chat/`. Week four: attach **SIEM forwarding** on APIM diagnostic settings with **conversationId** field preserved. Each week adds evidence for **security questionnaire** answers without requiring a Copilot rewrite.

## Blazor-specific operations notes

Because UI uses **Interactive Server**, scale plans must account for **circuit memory** per anonymous user plus **server-side Direct Line WebSockets**. Load tests should include **concurrent anonymous sessions**, not only APIM POST latency. Microsoft documents **single replica** for a reason — treat horizontal scale as a **design change**, not a slider.

Documentation readers should also inventory **which Copilot Studio environments** map to which APIM products before auditors ask for a diagram. A common gap is **dev Copilot** still pointing at **prod APIM** keys copied from a wiki page. Rotate keys when people leave the team that built the POC, because subscription secrets in Container Apps outlive hackathon weekends.

Keep a printed **network diagram** showing **browser → APIM → Container App → Copilot Studio → APIM return path** for onboarding engineers who have never debugged **split WebSocket and HTTP** chat stacks.

Security review boards sometimes ask whether **Copilot tool invocations** can exfiltrate data during an APIM outage. The README’s fail-closed stance is the right answer: degraded chat is preferable to **unmonitored agent execution**.

Add **change-control** on `apim-chat-policy.xml` the same way you gate production firewall rules. Version that policy XML in git with mandatory pull request review.

## Related on explainx.ai

*Configuration flags and policy file names follow the November 12, 2026 microsoft/gdlchat README.*
