I wanted a chat agent that could pull security alerts from Sophos Central on demand. Type "get Sophos alerts" into a Copilot Studio chat, get back a readable answer. No dashboard, no manual API calls.
It works now, end to end. Agent calls a Power Automate flow, the flow talks to the Sophos API, and the response comes back formatted in chat. This post is how I got there, including the bugs that ate most of my time.
The shape of the thing
Three pieces:
- A Copilot Studio agent that the user talks to
- A Power Automate flow that does the actual API work
- The Sophos Central API on the other end
The agent does not call Sophos directly. It calls the flow, the flow handles auth and the request, and the result gets passed back to the agent to format. Keeping the API logic in the flow means the agent stays simple.
The flow
The flow is named Sophos - Get Alerts, living in a solution called Sophos Integration. Here is the structure:
[Trigger: When Copilot Studio calls a flow]
|
[Init ClientId] - String (Sophos Client ID)
[Init ClientSecret] - String (Sophos Client Secret)
[Init TenantId] - String (your Sophos tenant ID)
[Init ApiHost] - https://api-<region>.central.sophos.com
|
[HTTP Get Token]
POST https://id.sophos.com/api/v2/oauth2/token
Header: Content-Type: application/x-www-form-urlencoded
Body: grant_type=client_credentials&client_id={ClientId}&client_secret={ClientSecret}&scope=token
|
[Parse Token] - extract access_token
|
[HTTP Get Alerts]
GET {ApiHost}/common/v1/alerts
Headers:
Authorization: Bearer {access_token}
X-Tenant-ID: {TenantId}
Accept: application/json
|
[Return value(s) to PVA: AlertsResponse = body('HTTP_Get_Alerts')]
Auth is OAuth client credentials. You request a token, then use it as a bearer token on the alerts call. The tenant ID goes in an X-Tenant-ID header, not the URL.
Where the credentials live
This is the part people will have opinions about, so let me be upfront. The Sophos client ID, secret, and tenant ID are stored as flow-scoped variables with restricted owner access, pending Key Vault migration
That was not my first choice. I looked at two cleaner options first:
- Azure Key Vault. Key Vault was not available in this build, so it was off the table.
- Dataverse environment variables. Reading four values needed something like twelve actions just for the plumbing. Too much overhead for what it bought me.
So the variables it is, for now. The flow owner is locked to me, and the plan is to migrate to Key Vault the moment I get access. If you have Key Vault available, use it from day one and skip my detour.
One more thing on this: during debugging I turned off Secure Inputs and Outputs on the HTTP actions so I could actually see request and response bodies. By the time this post is published, secure input/output is already re-enabled on my flow.
The bugs
Three things cost me real time.
The Content-Type header. I put Content-Type in the Value column of the headers field instead of the Key column. Sophos rejected the request with a customer.validation Bad Request. The error message did not point at the header at all, so I spent a while looking in the wrong place. Key goes in Key, value goes in Value. Obvious in hindsight.
A variable reference that broke on rename. I renamed an action from Parse JSON to Parse_Token and a downstream reference did not update with it. The flow could not find the value it was looking for. Power Automate is not always forgiving about renames, so check your references after you change an action name.
encodeUriComponent that I did not need. I wrapped a variable in encodeUriComponent thinking the request needed it. It did not. Plain variable chips worked fine, and the encoding was actually getting in the way. Reverted it.
What I tried and dropped
For anyone going down this path, a few approaches I ruled out:
- A custom connector for Sophos. Sophos does not expose what you would need to configure one cleanly, so this did not work out.
- Azure Key Vault and Dataverse environment variables for credentials, for the reasons above.
Result
The first working test returned an empty alert set, which makes sense because the queue was clear. Status 200, a clean JSON body with the paging info, and the agent turning that into a readable "No Active Alerts" line with a timestamp. Boring output, but boring is the goal here.
Next
This is one flow. The plan from here:
- Re-enable the secure input/output settings and document a credential rotation plan
- Trigger a real alert in Sophos Central to check the formatting holds up with actual data, not just an empty array
- Add filter parameters: severity, date range, status
- Build more flows: acknowledge an alert, get alert details, isolate an endpoint
- Move credentials to Key Vault when I can
The longer goal is a single agent that pulls from more than one security source and helps triage, but Sophos is the first piece and it is in place.
If you have wired up a vendor security API to Copilot Studio and handled the credential question differently, I would like to hear how.





Top comments (0)