Meet Work IQ: The Intelligence Behind Copilot, Now an API You Can Build On
If you’ve built an enterprise agent that needs to understand what’s actually happening inside an organization — who’s working on what, what was decided in last week’s meeting, which document is the latest version of the proposal — you already know the hard part isn’t the model. It’s the context.
I’ve spent a good chunk of the last year on exactly this problem. In my SharePoint agents series I grounded an agent in SharePoint content, and more recently I added long-term memory to Foundry agents so they’d stop forgetting everything between turns. Both worked. Both also meant I was building and maintaining plumbing: connectors, indexing, retrieval, and — the part nobody enjoys — permission trimming, so the agent never surfaces something the user isn’t allowed to see.
Work IQ is Microsoft’s answer to that plumbing. Microsoft says the Work IQ APIs go generally available on June 16, 2026, and honestly it’s the missing piece I kept wishing Microsoft would expose. This post is the first in a short series on building with it. In this one I’ll explain what Work IQ actually is, how it’s different from the approaches you might be using today, and make a first grounded call from C#. Later posts will build a proper agent with the A2A SDK, wire Work IQ in as MCP tools, and look at long-running agents with Workspaces.
The problem, stated plainly
Say you want an agent that can answer: “Catch me up on the Contoso migration — what’s the latest, who owns it, and what did we agree in the last review?“
To answer that well, the agent needs to pull from email, Teams chats, meeting transcripts, files in SharePoint and OneDrive, and the org chart, then reason across all of it. The usual approach looks something like this:
- Stand up connectors to each source.
- Extract and index the content into a vector store.
- Keep that index fresh as content changes.
- Re-implement Microsoft 365’s permission model so a user never sees content they don’t have rights to.
- Stitch the retrieved chunks into a prompt and hope your ranking is good enough.
Steps 3 and 4 are where a lot of these projects start to fall apart. Permission trimming is especially nasty. Sensitivity labels, conditional access, sharing boundaries, odd edge cases around who can see what. If you get it almost right, that’s worse than not doing it at all, because now you’ve built something that looks polished and quietly leaks data.
What Work IQ is
Work IQ is the intelligence layer behind Microsoft 365 Copilot, exposed as a developer API. The important distinction is that it doesn’t just hand you raw data and leave the reasoning to you. It exposes intelligence. You ask a question in natural language, it does the assembling, grounding, and reasoning internally, and it returns a Copilot-quality answer with citations, all inside your tenant’s existing security and compliance boundary.
Microsoft introduced it at Build 2026 as one of a family of “IQ” services: Work IQ for organizational intelligence, alongside Foundry IQ, Fabric IQ, and Web IQ. Work IQ is the one that understands your work: emails, meetings, chats, files, people, and the relationships between them.
It’s organized into four domains, and the names are worth learning because they line up pretty neatly with what agents usually need:
- Chat: conversational intelligence. You send a question, you get back a fully reasoned, grounded answer. This is what we’ll use today, via the A2A protocol (and REST, when you’re calling from a web app).
- Context: grounded context, assembled for you. Instead of you running a retrieval pipeline, Work IQ assembles and grounds the relevant organizational context internally and hands the agent ready-to-use input.
- Tools: a compact set of generic actions an agent can take across Microsoft 365 (mail, calendar, files, people, chat, sites). More on these in Part 3.
- Workspaces: persistent working storage (backed by SharePoint Embedded) where a long-running agent can stash intermediate state inside your tenant boundary. We’ll get to this in Part 4.
How it’s different from what you’re probably doing today
If you’ve used Microsoft Graph, the obvious question is: isn’t this just Graph with a nicer wrapper? It isn’t, and that difference is the whole point.
With Graph, you get data. You call /me/messages, you get messages back, and all the reasoning about what matters, how it connects, and what the answer should be is on you. With Work IQ, you get answers. The reasoning happens server-side, with the same grounding quality you see in Copilot.
If you’ve built your own RAG over Microsoft 365 content, Work IQ removes the four hardest parts: you don’t manage a vector store, you don’t run sync jobs, you don’t re-implement permission trimming, and you don’t own retrieval ranking. Responses are permission-trimmed by design. Every request runs in the context of the signed-in user, and because the API exposes intelligence rather than raw data, an app can’t accidentally sidestep tenant security. That’s the part I think matters most.
If you used the older Copilot Chat API during preview, Work IQ is the production-ready version of that direction. Same general idea, but now with enterprise SLAs and stable contracts. If you’re starting something new, this is the one to build on.
Choosing a protocol
Work IQ fits into a few different architectures. The same intelligence runtime is exposed through several protocols, so the grounding and response quality stay consistent whichever one you choose:
| Protocol | Use it when… | The caller is… |
|---|---|---|
| A2A (Agent-to-Agent) | Another agent needs to delegate a task to Work IQ and get a result back | Another agent |
| REST | You’re building an app or service that calls Work IQ programmatically | Your app or backend |
| MCP (local + remote) | An LLM-based client (Copilot, an IDE assistant, a custom agent) invokes Work IQ as a tool | An LLM client |
For this series I’m starting with A2A, because it’s the most agent-native of the three and it makes the model easy to see. Your code sends a message, Work IQ responds as a peer with a grounded answer. We’ll cover MCP in Part 3 and REST in Part 4.
A note on licensing (this changed at GA)
This part tripped me up, so it’s worth calling out. During the public preview, Work IQ access leaned on a Microsoft 365 Copilot add-on license, and the sample errors are not especially helpful if that license isn’t there yet or hasn’t fully propagated.
At GA (June 16), that changes: Work IQ API access is independent of Copilot licensing and billed consumptively through Copilot Credits. Licensed Copilot users get it across their Copilot experiences and are billed consumptively for custom and third-party agents; unlicensed users are billed consumptively too. There isn’t a separate Work IQ SKU or a per-user license, and IT can manage the spend from the Microsoft 365 admin center. In practical terms, if you’re testing against a preview tenant right now, keep the Copilot license assigned and give it 15–30 minutes after assignment before expecting solid answers. The index needs a little time to warm up.
One gotcha before we write code
If you go straight to the official samples repo, you’ll see the current samples target the Work IQ Gateway (workiq.svc.cloud.microsoft) as the dedicated entry point for Work IQ. The dotnet/a2a sample follows that path directly, using the Work IQ audience and delegated WorkIQAgent.Ask scope rather than Microsoft Graph.
That’s the path I’ll use throughout this series. If you run into older docs that still reference Graph endpoints, treat the gateway and the sample code as the more current source of truth.
Let’s make a call
We’ll build the smallest possible thing that proves the point: a console app that sends one question to Work IQ over A2A and prints the grounded answer with citations. I’m deliberately using raw HttpClient + JSON here instead of the A2A SDK, because for a first pass it’s useful to see exactly what goes over the wire. We’ll switch to the SDK in Part 2.
Step 1: Register an Entra app
Before you register your app, there’s one tenant-level prerequisite that is easy to miss: an organization admin has to enable the Work IQ API in the tenant by creating its service principal. That’s a one-time setup per organization, not something every developer repeats.
If it hasn’t been done yet, an admin can run:
az ad sp create --id fdcc1f02-fc51-4226-8753-f668596af7f7Once that service principal exists in the tenant, you can register your own client app.
In the Azure portal → Microsoft Entra ID → App registrations → New registration:
- Supported account types: match your tenant (single-tenant is fine for a demo).
- Redirect URI: add a Public client/native redirect of http://localhost so the interactive sign-in can complete.
Then under API permissions, add the Work IQ delegated permission and grant admin consent. The named delegated scope is WorkIQAgent.Ask on the Work IQ resource (api://workiq.svc.cloud.microsoft). In OAuth terms, that permission is exposed as api://workiq.svc.cloud.microsoft/WorkIQAgent.Ask. The samples often request api://workiq.svc.cloud.microsoft/.default, which simply asks for whatever has already been consented on that resource.

Work IQ uses Entra delegated authentication — every call runs as the signed-in user, on-behalf-of flows are supported, and application-only auth is deliberately not supported. That’s not a limitation, it’s the security model: there’s no “service account that sees everything,” because there’s no scenario where Work IQ returns content outside a specific user’s permissions.
Grab your app (client) ID and tenant ID — we’ll need both.
Step 2: The project
Now let’s keep the project itself as small as possible: a plain console app and the one package we need for interactive sign-in.
dotnet new console -n WorkIQHello
cd WorkIQHello
dotnet add package Microsoft.Identity.ClientWe only need MSAL to acquire a token interactively. Everything else is just HttpClient and System.Text.Json. This targets .NET 10.
Step 3: Get a token, send a message
With that in place, we can write the whole flow end to end: sign the user in, call the Work IQ gateway, and print the answer that comes back.
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using Microsoft.Identity.Client;
// --- 1. Acquire a delegated token for the Work IQ resource ---
const string ClientId = "<your-app-client-id>";
const string TenantId = "<your-tenant-id>";
const string Scope = "api://workiq.svc.cloud.microsoft/.default";
const string Endpoint = "https://workiq.svc.cloud.microsoft/a2a/";
var app = PublicClientApplicationBuilder
.Create(ClientId)
.WithAuthority($"https://login.microsoftonline.com/{TenantId}")
.WithDefaultRedirectUri()
.Build();
var auth = await app
.AcquireTokenInteractive(new[] { Scope })
.ExecuteAsync();
// --- 2. Build an HttpClient pointed at the Work IQ gateway ---
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", auth.AccessToken);
http.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
// Opt in to the A2A v1.0 wire format. Without this header the gateway
// defaults to v0.3 and the v1.0 method name below returns
// JSON-RPC -32601 "Method not found". This one line costs people hours.
http.DefaultRequestHeaders.TryAddWithoutValidation("A2A-Version", "1.0");
// --- 3. Build an A2A message wrapped in a JSON-RPC envelope ---
var message = new Dictionary<string, object?>
{
["role"] = "ROLE_USER",
["messageId"] = Guid.NewGuid().ToString(),
["parts"] = new object[] { new { text = "What meetings do I have today?" } },
// Location lets Work IQ resolve "today" / "this week" in the user's local time.
["metadata"] = new Dictionary<string, object>
{
["Location"] = new
{
timeZoneOffset = (int)TimeZoneInfo.Local.BaseUtcOffset.TotalMinutes,
timeZone = TimeZoneInfo.Local.Id
}
}
};
var rpc = new
{
jsonrpc = "2.0",
id = Guid.NewGuid().ToString(),
method = "SendMessage", // sync; "SendStreamingMessage" for SSE (Part 2)
@params = new { message }
};
var content = new StringContent(
JsonSerializer.Serialize(rpc), Encoding.UTF8, "application/json");
// --- 4. POST to the gateway base URL — the method lives in the body, not the path ---
var response = await http.PostAsync(Endpoint, content);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
Console.WriteLine($"{(int)response.StatusCode} {response.StatusCode}\n{body}");
return;
}
// --- 5. Pull the answer out of result.task.artifacts[].parts[].text ---
using var doc = JsonDocument.Parse(body);
var result = doc.RootElement.GetProperty("result");
Console.WriteLine(ExtractAnswer(result));
PrintCitations(result);
static string ExtractAnswer(JsonElement result)
{
// A completed task carries the answer as one or more artifacts.
if (result.TryGetProperty("task", out var task) &&
task.TryGetProperty("artifacts", out var artifacts))
{
var sb = new StringBuilder();
foreach (var artifact in artifacts.EnumerateArray())
if (artifact.TryGetProperty("parts", out var parts))
foreach (var p in parts.EnumerateArray())
if (p.TryGetProperty("text", out var t))
sb.Append(t.GetString());
return sb.ToString();
}
return "(no answer found)";
}A few details are worth calling out because they’re exactly the kind of thing that wastes an hour if you miss them:
- You POST to the base URL. Unlike a REST API where the verb is the path, A2A puts the method name (SendMessage) inside the JSON-RPC body. The gateway endpoint is the same for every call.
- role is ROLE_USER, in screaming snake case, and there’s no kind discriminator on parts. That’s the A2A v1.0 shape — and the reason the A2A-Version: 1.0 header matters.
- Location metadata isn’t optional for time-sensitive questions. Ask “what’s on my calendar today” without it and Work IQ has no idea what “today” means for you.
Step 4: Show the citations
The reason a Work IQ answer is actually useful is that it tells you where the information came from. Citations come back in the response metadata under attributions:
static void PrintCitations(JsonElement result)
{
// Metadata can sit on the task's status message; shape mirrors the streaming case.
if (!result.TryGetProperty("task", out var task) ||
!task.TryGetProperty("status", out var status) ||
!status.TryGetProperty("message", out var msg) ||
!msg.TryGetProperty("metadata", out var meta) ||
!meta.TryGetProperty("attributions", out var attributions) ||
attributions.ValueKind != JsonValueKind.Array)
return;
Console.WriteLine("\nSources:");
foreach (var a in attributions.EnumerateArray())
{
var name = a.TryGetProperty("providerDisplayName", out var n) ? n.GetString() : "(source)";
var url = a.TryGetProperty("seeMoreWebUrl", out var u) ? u.GetString() : "";
Console.WriteLine($" • {name} {url}");
}
}Run it (dotnet run), sign in when the browser pops up, and you should get back something like:
Today you have: 9 AM standup, 11 AM review with Dana, 2 PM customer call.
Sources:
• Calendar https://outlook.office.com/...That’s a grounded, permission-trimmed answer over your real Microsoft 365 data, and it took roughly forty lines of code to get there. No connectors, no index, no permission logic. The user signs in, and they only see what they’re already allowed to see.
Where this is going
What we built today is intentionally bare-bones: one synchronous question, raw JSON, no SDK. It proves the model, but it’s not how you’d ship a real agent.
In Part 2 we’ll rebuild this properly with the A2A .NET SDK: multi-turn conversations using contextId so the agent remembers the thread, streaming responses over SSE, and cleaner citation handling. Then Part 3 moves into the Work IQ MCP and its ten generic tools, and Part 4 gets into Workspaces for long-running agents.
If you’ve followed my SharePoint and Foundry memory posts, that’s really the through-line here. We’ve gone from grounding an agent in one source that I had to wire up myself to an intelligence layer that can ground across all of Microsoft 365 and enforce permissions for me. That’s a real shift in where the hard work lives, and it’s a much better place to be building from.
Code for this series will be on my GitHub at work-iq-samples.
Next up: building the agent with the A2A SDK.