Skip to main content
Copilot Studio exposes one hook to an external security provider: the call immediately before a tool runs. This page configures it end to end. Read Microsoft Copilot Studio first if you have not decided between the two integration paths, and for what this path does and does not cover. Microsoft calls TrustGuard, so nothing is deployed and nothing is proxied. Authentication uses an Entra federated identity credential — there is no shared secret and no API key on this path. Setup spans three places: the collector’s Connection tab, your Entra app registration, and Power Platform admin center. The Connection tab produces every value the other two ask for.
You visit Entra twice, and the order is not optional. Step 2 creates the application so you have its ID; step 4 comes back to add the credential. They cannot be done in one pass, because the credential encodes an endpoint the collector only produces once it knows the application ID.

Before you start

1. Copy the threat detection endpoint

Open the collector and read the Connection tab. The Threat detection endpoint is unique to this collector:
One endpoint per collector, never shared. The federated credential binds this exact URL, so if the endpoint changes the credential stops matching.

2. Register the client application in Microsoft Entra ID

In Microsoft Entra ID → App registrations, create a single-tenant application. Copy two values:
Two applications are involved, and you create only one of them. This one is the client: it is the identity Copilot Studio authenticates as, it holds the federated credential from step 4, and its Application (client) ID is what you paste into Power Platform.The other is the audience — the application that claims your endpoint’s origin as its Application ID URI, so Entra knows who the token is for. That one is NeuralTrust’s, and it cannot be yours: Entra only accepts an Application ID URI on a domain verified in the tenant that owns the application, and the endpoint is on our domain. Its own client ID is never entered anywhere. If it is not present in your tenant, step 5 fails with AADSTS500011 — contact us rather than trying to register it yourself.

3. Store the tenant and application IDs on the collector

Back on the Connection tab, paste both values and press Save. Separate several Application IDs with commas if more than one application will call. The status changes from Waiting for your tenant and application to Ready.
Deliveries are refused until both values are stored. TrustGuard checks the caller’s application against this allowlist on every request, so an unsaved collector rejects Microsoft’s calls with 401.

4. Add the federated identity credential

The Connection tab now shows a Federated credential subject identifier. Copy it. In your Entra app registration, open Certificates & secrets → Federated credentials → Add credential and fill the form:
On the audience application’s Expose an API page, only the first line matters. Scopes defined by this API and Authorized client applications stay empty, as do App roles and API permissions. They exist for APIs where a user or an admin consents to permissions; here nothing consents to anything — the token is issued against a federated credential, machine to machine. Defining scopes builds something that is never evaluated.
Never build the subject identifier by hand. It encodes your tenant and the exact endpoint URL, and the tenant GUID is not written as a GUID — it is re-ordered into .NET byte order and base64url-encoded. A subject assembled by hand from the tenant ID looks entirely reasonable, Entra accepts it without complaint, and it never matches an incoming token. Always copy the value the Connection tab computes.
If the endpoint ever changes, two things go stale, not one. Both are bound to the URL: the Application ID URI claims its origin, and the subject encodes the full URL. A new environment, a recreated collector or a different host invalidates both at once, and fixing only one produces an error that points at the other.Adding the new Application ID URI is enough — an application accepts several. The credential is not editable: the subject cannot be changed after creation, so delete the credential and add a new one with the subject the Connection tab shows for the current endpoint.
By this point you are handling five identifiers that look alike. Mixing them up is the most common setup error, and none of the portals warns you:The rule that avoids all four confusions: if a Microsoft portal asks for the value, it comes from Microsoft or from the collector’s Connection tab — never from the address bar of the NeuralTrust console.

5. Turn on threat detection in Power Platform admin center

Open the environment → SecurityThreat detectionAdditional threat detection and protection for Copilot Studio agents, then enter the endpoint from step 1 and the Application (client) ID from step 2.
The provider list is empty until you type in the filter. It loads on search, so a blank list is the normal starting state and not a sign that anything is wrong — search for your provider by name.
Power Platform admin center also asks what should happen when TrustGuard cannot answer in time.
Microsoft’s default is to let the agent respond. If a missed verdict should stop the tool rather than wave it through, choose to block the query instead. TrustGuard answers within a 700 ms budget because Copilot Studio waits 1000 ms and treats a slower answer as no answer.
Pressing Save makes Power Platform call the collector’s readiness probe. A healthy collector answers {"isSuccessful": true, "status": "OK"}.

6. Assign a policy

On the collector’s Policies tab, assign a default policy. Start it in Observe so findings are recorded without stopping any tool, check Agent Runtime → Activity, then switch to Enforce.
Write the rule in the Input direction. Copilot Studio calls threat detection once, before a tool runs, so every delivery is evaluated as input. A rule placed in the Output direction is never reached on this collector — it saves without complaint and silently matches nothing.
Because the delivery is a planner snapshot rather than a chat turn, the choice of detector decides what is actually read:
If you are protecting against a poisoned document, ticket, or API response, use Indirect Prompt Injection. Prompt rules do not read tool output on this path, so a policy built only from them stays quiet while the injection goes through.
A collector with no matching policy is unguarded: TrustGuard allows every tool and inspects nothing.

If your agent uses workflows

Add one from Tools → + → Workflows. The flow needs the When an agent calls the workflow trigger — an ordinary Power Automate cloud flow cannot be used as an agent tool, and Copilot Studio says so on the picker. Two more things are worth knowing before you write a policy against a workflow, because neither is visible until you try. Give every input parameter a short, readable description. Power Automate names them after their type — text, text_1, text_2 — and that is what arrives, whatever you type in the name field. Renaming a parameter in Power Automate changes its description, not its name; the name reaches TrustGuard as text_N regardless. Confirmed against live traffic: a flow whose four parameters were named destinatario, asunto, cuerpo and copia_oculta delivered them as text, text_1, text_2, text_3, carrying those words as the descriptions. So the description is the only place a readable name exists, and it does two jobs. It tells the model what to put in the field — without it the model leaves the field empty. And TrustGuard offers it as a dimension of its own whenever it reads like a label rather than a sentence, so a policy can target tool.arguments.copia_oculta instead of tool.arguments.text_3. Write a label, not a paragraph: a long description stays a description and you are left with the number. That matters more than it sounds: reordering a workflow’s fields changes which parameter text_3 refers to, and a condition on an attribute that no longer exists matches the empty string rather than failing. A policy pinned to text_N breaks silently the day someone adds a field. Tool names arrive without spaces. A flow shown as Enviar respuesta in Copilot Studio reaches TrustGuard as Enviarrespuesta, so a condition on tool.name typed the way the tool appears on screen never matches. Copy the name from Agent Runtime → Activity rather than from the Copilot Studio canvas.
Publish the flow after every change, and the agent after changing its tools. A flow with unpublished changes stays in the agent’s tool catalog, so the model still selects it and finds nothing behind it. There is no error and no timeout — the turn simply never finishes, and it looks like threat detection is hanging. It is not: when a verdict is late, the tool is blocked per the environment setting, never left pending.

Confirm it blocks

Assigning a policy is not evidence that it fires. This is the smallest test that produces some, and it takes one conversation. It needs two tools, not one. Threat detection is called once per tool, just before it runs. With a single tool there is nothing to see: the injection is not in the request, it is in the response, which arrives afterwards. It is only observable in the gap between two calls — the first returns the poisoned content and the second carries it along.
  1. Give the agent a tool that returns text you control, and a second tool that acts on it — reading a record and sending a message is the usual pair.
  2. Put an instruction that does not belong in the content the first tool returns, alongside some ordinary text. Something the agent would have to obey to be compromised, such as sending a summary to an outside address.
  3. Assign a policy with Indirect Prompt Injection in the Input direction, set to block.
  4. Remove every other tool and Knowledge source while you test. With three possible paths and one measured, a clean trace proves nothing — it may only mean there was an easier route.
  5. Publish, then ask the agent to do the work end to end in one turn.
If both are blocked, that is not a pass. It means anything would be flagged, and the block on the second proves nothing — it would have blocked without the injection too. The negative control is what gives the positive one its meaning. Lower the rule’s sensitivity and repeat until the first delivery passes clean.
Read the blocked call in Agent Runtime → Activity before moving on, because it is the point of the whole path. In a real run of this test the proposed call was impeccable — a correct recipient, an ordinary subject, a well-written body, and the model had not obeyed the injection at all — and it was blocked anyway. Nothing in the call itself was objectionable. The evidence was entirely in what the previous tool returned, which is why a policy that reads only prompts or only arguments lets this exact case through.

Reference

What Copilot Studio sends to threat detection

Each delivery carries the planner context (the user message, chat history and previous tool outputs), the definition of the tool about to run with its input and output parameters, the input values being passed to it, and conversation metadata — agent, user, trigger, conversation and message ids, channel, and the incoming client IP. Earlier tool output arrives under plannerContext.previousToolsOutputs, one entry per tool that already ran in the turn, each carrying the calling tool’s name and its output values with the role tool. The values are verbatim — the platform does not truncate or summarise them — which is what makes a poisoned result visible before the agent acts on it. On the turn’s first tool call the list is empty, so that delivery has no earlier output to evaluate. The contract also defines a thought field for the planner’s reasoning, and planId / planStepId for the step being executed. All three have arrived empty in every delivery observed so far, so nothing should be built on them until Copilot Studio starts populating them. Every field is treated as optional, so a Copilot Studio API version that adds or drops one does not break evaluation.

The verdict contract

Threat detection answers with a single decision:
A block adds a reason and diagnostics for your own records:
blockAction is the whole contract. That is why this path cannot redact: there is no field in which to return rewritten content.
The person using the agent does not see your reason. Copilot Studio shows its own message when a tool is blocked — “This action was blocked by threat detection tools. Please contact your administrator if you believe this is an error.” — and appends an instruction telling the agent to relay that text verbatim rather than paraphrase it. Your reason and diagnostics are for your logs and for Activity, so do not plan end-user wording around them.In the maker’s trace the blocked call carries a different, more useful pair: errorCode: "pre-tool-blocked" with “This action was blocked before the tool could run.” That is the one to look for when diagnosing, and it distinguishes a block from every other reason a tool call can fail.

Failure behavior

Troubleshooting