Introduction
Working with Microsoft Copilot Studio often requires creating dynamic, interactive user experiences. One powerful feature that enables this is the integration of Adaptive Cards with dynamic JSON content generation. In this post, I'll walk you through a real-world implementation where we needed to generate radio buttons dynamically based on items returned from a RAG (Retrieval-Augmented Generation) system.
The Challenge
Our use case involved creating a conversational interface where users could select from a list of options that weren't static. The radio button choices needed to be generated dynamically based on the results returned from our RAG implementation. This meant we couldn't rely on pre-built, static Adaptive Cards.
The Solution: Dynamic Variable Integration
To solve this challenge, we leveraged Microsoft Copilot Studio's Dynamic Variable functionality combined with Adaptive Card Templates. Here's how we implemented it:
Step 1: Setting Up the Adaptive Card Template
In Copilot Studio, we configured an Adaptive Card Template using the `AdaptiveCardTemplate` object with placeholder expressions:
```yaml
- kind: SendActivity
id: sendActivity_dinNxr
displayName: Dynamic Radio Button
activity:
text:
- "{Text(Topic.KBAresponse.answer)}"
attachments:
- kind: AdaptiveCardTemplate
cardContent: =Text(Topic.Var1RadioButton)
```
The key here is the `cardContent: =Text(Topic.Var1RadioButton)` line, which references our dynamic variable that contains the JSON structure for our Adaptive Card.
Step 2: API Integration for Dynamic Content
The dynamic variable `Topic.Var1RadioButton` gets populated through an HTTP Node in Copilot Studio, which makes an API call to a Python-hosted FastAPI endpoint. This approach allows us to generate the Adaptive Card structure programmatically based on the current context and data.
Step 3: FastAPI Implementation
Here's the Python FastAPI implementation that generates our dynamic Adaptive Card content:
```python
from fastapi import APIRouter, Request
from fastapi.responses import PlainTextResponse
import json
router = APIRouter()
@router.post("/generate-adaptive-card")
async def generate_adaptive_card(request: Request):
payload = await request.json()
solution_ids_used = payload.get("SolutionIDsUsed", [])
used_ids = payload.get("UsedIDs", [])
# Generate your adaptive card JSON structure here
# adaptive_card = {...} # Your card logic
# Return escaped string (for plain text transport of JSON)
escaped_json = json.dumps(adaptive_card)
return PlainTextResponse(content=escaped_json)
```
Critical Implementation Details
Return Type Matters
One crucial aspect of this implementation is the return type from your API endpoint. The response **must be of type Plain Text**, not JSON. If you return a JSON response directly, Copilot Studio's workflow screen will throw rendering errors.
This is why we use:
```python
return PlainTextResponse(content=escaped_json)
```
Instead of returning the JSON object directly.
Dynamic Variable Population
The HTTP Node in Copilot Studio should be configured to:
1. Make a POST request to your FastAPI endpoint
2. Send the necessary context data (like `SolutionIDsUsed` and `UsedIDs`)
3. Store the response in your dynamic variable (`Topic.Var1RadioButton`)
Conclusion
Dynamic Adaptive Cards in Microsoft Copilot Studio open up powerful possibilities for creating responsive, data-driven conversational experiences. By combining Dynamic Variables with external API endpoints, you can create truly dynamic user interfaces that adapt to your users' needs and context.
Thursday, July 31, 2025
Microsoft Copilot Studio: How to Generate Dynamic Adaptive Cards Contents
Friday, July 11, 2025
An error has occurred. Error code: HttpRequestFailure Conversation Id: e3QGxxxxxx01NA-us Time (UTC): 2025-06-01T16:14:05.372Z.
Troubleshooting MS Copilot Studio HTTP Request Failures: A Practical Solution
The Problem Description
If you're working with MS Copilot Studio, you've likely encountered this frustrating error:
MS Copilot Studio --
An error has occurred. Error code: HttpRequestFailure Conversation Id: e3QGxxxxxx01NA-us Time (UTC): 2025-06-01T16:14:05.372Z.
This is a common issue that occurs frequently with MS Copilot Studio, though not consistently. Based on my experience, this isn't a code issue but rather a configuration issue related to the MS environment.
What We Discovered
When we enabled the Continue on Error feature in Copilot Studio, we encountered an HTTP 408 error (Request Timeout). This means the server didn't receive a complete request from the client within the expected timeframe.
After researching and contacting our support counterpart, they provided the following stacktrace:
CorrelationId: xxxx-b674-4c2a-xxxx-184197387367
Exception: Microsoft.IdentityModel.S2S.S2SAuthenticationException: S2xx2099:
An exception has been caught while validating the request.
Exception: [PII of type 'System.AggregateException' is hidden]
---> System.AggregateException: S2xx2096: Microsoft.IdentityModel.S2S.JwtAuthenticationHandler
caught exceptions when validating the token. See AuthenticationResult.InboundPolicyEvaluationResults
for additional details. (S2xx2086: An exception has been caught while validating the request
applying the policy with id : 'User'.
Exception: Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDXxx214:
Audience validation failed. Audiences: 'xxx-xxx-4af1-b9a8-09a648fb6699'.
Did not match: validationParameters.ValidAudience: 'null' or validationParameters.ValidAudiences:
Unfortunately, the issue remains unresolved as of when I write this post.
Our Temporary Solution: Retry Logic
As a quick fix, we implemented retry logic in the Copilot Studio workflow UI using the Goto Step feature for HTTP nodes, with a maximum retry count of 3.
Implementation Code
Here's the complete code snippet for our temporary fix:
1. Initialize Retry Counter
- kind: SetVariable
id: setVariable_btVmH4
displayName: retryCount
variable: Topic.retryCount
value: 0
2. HTTP Request with Error Handling
- kind: HttpRequestAction
id: JDNAtp
displayName: PF - HTTP Request
method: Post
url: https://pf-end-point-autoendpoint.eastus.inference.ml.azure.com/score
headers:
Authorization: Bearer 6mIiASFPiZKeaRxxUk4JQQJ99BGAAAAAAAAAAAAINFRAZML2PC6
azureml-model-deployment: auto-20250708-551705
Content-Type: application/json
body:
kind: JsonRequestContent
content: |
={
question:Topic.UserQuery,
chat_history:Global.VarHistory
}
errorHandling:
kind: ContinueOnErrorBehavior
statusCode: Topic.ErrorStatusCode
requestTimeoutInMilliseconds: 30000
response: Topic.KBAresponse
responseSchema: Any
responseHeaders: Topic.ResponseHeader
3. Error Status Logging
- kind: SendActivity
id: sendActivity_dxnBNR
activity: After HTTP request --- Error Code--- {Topic.ErrorStatusCode}
4. Retry Logic Implementation
- kind: ConditionGroup
id: conditionGroup_cN6dbL
conditions:
- id: conditionItem_xH0b5H
condition: =!IsBlank(Topic.ErrorStatusCode)
displayName: Condition to try Retry Logic
actions:
- kind: SetVariable
id: setVariable_9Lszte
variable: Topic.retryCount
value: =Topic.retryCount + 1
- kind: ConditionGroup
id: conditionGroup_UA3fsX
conditions:
- id: conditionItem_itQSEP
condition: =Topic.retryCount < 3
actions:
- kind: SendActivity
id: sendActivity_Y0iihp
activity: ---->{Topic.retryCount}---
- kind: GotoAction
id: S4S1LT
actionId: JDNAtp
- kind: SendActivity
id: sendActivity_vOX9b5
activity: Errorred --{Topic.retryCount}
How It Works
- Initialize a retry counter to 0
- Execute the HTTP request with error handling enabled
- Check if an error occurred (ErrorStatusCode is not blank)
- Increment the retry counter
- Retry up to 3 times using the GotoAction to jump back to the HTTP request
- Log the final error state if all retries fail
Key Takeaways
- This appears to be an authentication/token validation issue on Microsoft's end
- The Continue on Error feature is essential for implementing retry logic
- Retry logic provides a practical workaround while waiting for Microsoft to resolve the underlying issue
- The GotoAction feature in Copilot Studio makes implementing retry patterns straightforward
Conclusion
While this isn't a permanent solution, it significantly improves the reliability of HTTP requests in MS Copilot Studio workflows. If you're experiencing similar issues, consider implementing this retry pattern until Microsoft addresses the root cause.
Have you encountered similar issues with MS Copilot Studio? Share your experiences and solutions in the comments below!
Tags: #MSCopilotStudio #Azure #HTTPErrors #RetryLogic #Troubleshooting #Microsoft