Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Organizations can add a layer of security to their Copilot Studio agents by connecting them to a runtime threat detection system. Once connected, the agent calls this system at runtime. The agent provides the system with data so that the system can determine if a tool the agent plans to invoke is legitimate or not. The system then replies to Copilot Studio with a response of either "approve" or "block," causing the agent to invoke or skip the tool accordingly. For more information on how to connect agents to an existing external threat detection system, see Enable external threat detection and protection for Copilot Studio custom agents.
This article is targeted at developers, and describes how to integrate your own threat detection capabilities as a security provider for Copilot Studio agents.
The integration is based on an API consisting of two endpoints. The main endpoint you need to implement is the analyze-tool-execution endpoint. You need to expose this endpoint as an interface to your threat detection system. Once customers configure your system as their external threat detection system, the agent calls this API every time it intends to invoke a tool.
Aside from the analyze-tool-execution endpoint, you also need to expose a second endpoint, called validate. The validate endpoint is used to check the health and readiness of the endpoint as part of the system setup.
The following sections describe each endpoint in detail.
POST /validate
Purpose: Verifies that the threat detection endpoint is reachable and functioning. Used for initial setup and configuration testing.
Validate request
Method: POST
URL:
https://{threat detection endpoint}/validate?api-version=2025-05-01Headers:
Authorization: Bearer token for API authentication
x-ms-correlation-id: GUID for tracing
Body: Empty
Validate response
200 OK response example
{
"isSuccessful": true,
"status": "OK"
}
Error response example
If an error occurs (unsuccessful HTTP code), the endpoint returns an error code, a message, and optional diagnostics.
{
"errorCode": 5031,
"message": "Validation failed. Webhook service is temporarily unavailable.",
"httpStatus": 503,
"diagnostics": "{\\reason\\:\\Upstream dependency timeout\\}"
}
POST /analyze-tool-execution
Purpose: Submits tool execution context for risk evaluation. Evaluates the tool execution request and responds whether to allow or block the tool execution.
Analyze-tool-execution request
Method: POST
URL:
https://{threat detection endpoint}/analyze-tool-execution?api-version=2025-05-01Headers:
- Authorization: Bearer token for API authentication
- Content-Type: application/json
Body: JSON object
Example analyze-tool-execution request
POST https://security.contoso.com/api/agentSecurity/analyze-tool-execution?api-version=2025-05-01
Authorization: Bearer XXX……
x-ms-correlation-id: fbac57f1-3b19-4a2b-b69f-a1f2f2c5cc3c
Content-Type: application/json
{
"plannerContext": {
"userMessage": "Send an email to the customer",
"thought": "User wants to notify customer",
"chatHistory": [
{
"id": "m1",
"role": "user",
"content": "Send an email to the customer",
"timestamp": "2025-05-25T08:00:00Z"
},
{
"id": "m2",
"role": "assistant",
"content": "Which customer should I email?",
"timestamp": "2025-05-25T08:00:01Z"
},
{
"id": "m3",
"role": "user",
"content": "The customer is John Doe",
"timestamp": "2025-05-25T08:00:02Z"
}
],
"previousToolOutputs": [
{
"toolId": "tool-123",
"toolName": "Get customer email by name",
"outputs": {
"name": "email",
"description": "Customer's email address",
"type": {
"$kind": "String"
},
"value": "customer@foobar.com"
},
"timestamp": "2025-05-25T08:00:02Z"
}
]
},
"toolDefinition": {
"id": "tool-123",
"type": "PrebuiltToolDefinition",
"name": "Send email",
"description": "Sends an email to specified recipients.",
"inputParameters": [
{
"name": "to",
"description": "Receiver of the email",
"type": {
"$kind": "String"
}
},
{
"name": "bcc",
"description": "BCC of the email",
"type": {
"$kind": "String"
}
}
],
"outputParameters": [
{
"name": "result",
"description": "Result",
"type": {
"$kind": "String"
}
}
]
},
"inputValues": {
"to": "customer@foobar.com",
"bcc": "hacker@evil.com"
},
"conversationMetadata": {
"agent": {
"id": "agent-guid",
"tenantId": "tenant-guid",
"environmentId": "env-guid",
"isPublished": true
},
"user": {
"id": "user-guid",
"tenantId": "tenant-guid"
},
"trigger": {
"id": "trigger-guid",
"schemaName": "trigger-schema"
},
"conversationId": "conv-id",
"planId": "plan-guid",
"planStepId": "step-1"
}
}
Analyze-tool-execution response
200 OK
When the request is valid, the tool use specified in the request is evaluated and either allowed or blocked, based on the defined criteria. The response can include the following fields:
- blockAction (Boolean): Whether the action should be blocked
- reasonCode (integer, optional): Numeric code explaining the reason for the block
- reason (string, optional): Human-readable explanation
- diagnostics (object, optional): Other details for tracing or debugging
Example allow response
{
"blockAction": false
}
Example block response
{
"blockAction": true,
"reasonCode": 112,
"reason": "The action was blocked because there is a noncompliant email address in the BCC field.",
"diagnostics": "{\\flaggedField\\:\\bcc\\,\\flaggedValue\\:\\hacker@evil.com\\}"
}
Example error response
If the request isn't valid, an error response is returned with an error code, message, HTTP status, and optional diagnostics.
{
"errorCode": 4001,
"message": "Missing required field: toolDefinition",
"httpStatus": 400,
"diagnostics": "{\\missingField\\:\\toolDefinition\\,\\traceId\\:\\abc-123\\}"
}
Request and response body structures reference
The following tables describe the contents of various objects used within the request and response bodies for the endpoints.
ValidationResponse
| Name | Type | Required | Description |
|---|---|---|---|
| isSuccessful | Boolean | Yes | Indicates whether the validation passed. |
| status | string | Yes | Optional status message or partner-specific detail. |
AnalyzeToolExecutionResponse
| Name | Type | Required | Description |
|---|---|---|---|
| blockAction | Boolean | Yes | Indicates whether the action should be blocked. |
| reasonCode | integer | No | Optional numeric reason code, determined by partner. |
| reason | string | No | Optional human-readable explanation. |
| diagnostics | string | No | Optional freeform diagnostic info for debugging or telemetry. Must be preserialized. |
ErrorResponse
| Name | Type | Required | Description |
|---|---|---|---|
| errorCode | integer | Yes | Numeric identifier for the error (for example, 1001 = missing field, 2003 = auth failure). |
| message | string | Yes | Human-readable explanation of the error. |
| httpStatus | integer | Yes | HTTP status code returned by the partner. |
| diagnostics | string | No | Optional freeform diagnostic info for debugging or telemetry. Must be preserialized. |
EvaluationRequest
| Name | Type | Required | Description |
|---|---|---|---|
| plannerContext | PlannerContext | Yes | Planner context data. |
| toolDefinition | ToolDefinition | Yes | Tool definition details. |
| inputValues | JSON object | Yes | Dictionary of key-value pairs provided to the tool. |
| conversationMetadata | ConversationMetadata | Yes | Metadata about the conversation context, user, and plan tracking. |
PlannerContext
| Name | Type | Required | Description |
|---|---|---|---|
| userMessage | string | Yes | The original message sent by the agent. |
| thought | string | No | Planner explanation for why this tool was selected. |
| chatHistory | ChatMessage[] | No | List of recent chat messages exchanged with the user. |
| previousToolsOutputs | ToolExecutionOutput[] | No | List of recent tool outputs. |
ChatMessage
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique identifier for this message in the conversation. |
| role | string | Yes | Source of the message (for example, user, assistant). |
| content | string | Yes | The message text. |
| timestamp | string (date-time) | No | ISO 8601 time stamp indicating when the message was sent. |
ToolExecutionOutputs
| Name | Type | Required | Description |
|---|---|---|---|
| toolId | string | Yes | Unique identifier for this message in the conversation. |
| toolName | string | Yes | Name of the tool. |
| outputs | ExecutionOutput[] | Yes | List of the tool execution outputs. |
| timestamp | string (date-time) | No | ISO 8601 time stamp indicating when the tool execution was finished. |
ExecutionOutput
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Name of the output parameter. |
| description | string | No | Explanation for the output value. |
| type | object | No | Data type of the output. |
| value | JSON data value | Yes | The output value. |
ToolDefinition
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique identifier of the tool. |
| type | string | Yes | Specifies the kind of tool used in the planner. |
| name | string | Yes | Human-readable name of the tool. |
| description | string | Yes | Summary of what the tool does. |
| inputParameters | ToolInput[] | No | Input parameters of the tool. |
| outputParameters | ToolOutput[] | No | Output parameters the tool returns after execution. |
ToolInput
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Name of the input parameter. |
| description | string | No | Explanation of the expected value for this input parameter. |
| type | JSON object | No | Data type of the input parameter. |
ToolOutput
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Name of the output parameter. |
| description | string | No | Explanation of the output value. |
| type | JSON object | No | Type of the output value. |
ConversationMetadata
| Name | Type | Required | Description |
|---|---|---|---|
| agent | AgentContext | Yes | Agent context information. |
| user | UserContext | No | Information about the user interacting with the agent. |
| trigger | TriggerContext | No | Information about what triggered the planner execution. |
| conversationId | string | Yes | ID of the ongoing conversation. |
| planId | string | No | ID of the plan used to fulfill the user request. |
| planStepId | string | No | Step within the plan corresponding to this tool execution. |
| parentAgentComponentId | string | No | ID of the parent agent component. |
AgentContext
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | ID of the agent. |
| tenantId | string | Yes | Tenant where the agent resides. |
| environmentId | string | Yes | Environment in which the agent is published. |
| version | string | No | Agent version (optional if isPublished is false). |
| isPublished | Boolean | Yes | Whether this execution context is a published version. |
UserContext
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | No | Microsoft Entra object ID of the user. |
| tenantId | string | No | Tenant ID of the user. |
TriggerContext
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | No | The id of the trigger that triggered the planner. |
| schemaName | string | No | The name of the trigger schema that triggered the planner. |
Authentication
The integration you develop should use Microsoft Entra ID authentication. Follow instructions on Integrate apps your developers build.
Steps to perform include the following:
- Create an app registration for your resource in your tenant.
- Expose a scope for your web API. The exposed scope must be the base URL for the resource the customers call. For example, if the API URL is
https://security.contoso.com/api/threatdetection, then the exposed scope must behttps://security.contoso.com. - Depending on how you implement your service, you need to implement authorization logic and validate incoming tokens. You need to document how the customer must authorize their apps. There are multiple ways of doing that, for example, using an allowlist of app IDs, or role-based access control (RBAC).
Response time requirements
The agent expects a response from the threat detection system within less than 1,000 ms. You should ensure your endpoint replies to the call within this time frame. If your system doesn't respond in time, the agent behaves as if your response is "allow," invoking the tool.
API Versioning
In requests, the API version is specified via a api-version query parameter (for example, api-version=2025-05-01). Your implementation should be tolerant of other unexpected fields and shouldn't fail if new values are added in the future. Partners shouldn't verify the API version, as all the versions at the moment are considered non-breaking. Partners should track the API versions but not fail the request on seeing a new version.