Back to Blog

Building Copilot API Plugins with Office JavaScript - When the Plugin Needs to Touch the Document

May 27, 20267 min readMichael Ridland

Most Microsoft 365 Copilot plugins do one of two things. Either they call an external REST API, or they pull data from a knowledge source. The pattern we have not been able to lean on until recently is a plugin that actually does something to the document the user is sitting in. Read the selected text in Word. Insert a chart into Excel. Drop a new slide into PowerPoint based on a Copilot conversation.

Microsoft now lets you build exactly that. It is called an API plugin with an Office JavaScript Library API, and it changes what is reasonable to ask Copilot to do.

I have been waiting for this one. The previous options were workable but felt like working around the platform rather than with it. This new pattern is genuinely useful for a particular set of problems, and worth understanding even if you do not need it today.

What it actually is

The short version. You build a Copilot plugin, but instead of the plugin's "API" being a remote REST endpoint, the API is implemented locally inside an Office Add-in, using the Office JavaScript library. When Copilot decides to call your plugin, it calls a function running inside the add-in, which has direct access to the host application (Word, Excel, PowerPoint, Outlook).

That sentence does a lot of work, so let me unpack it.

A normal API plugin has a backend somewhere on the internet. Copilot sends an HTTP request, the backend does something, Copilot reads the response. The plugin has no idea what document the user is looking at. It cannot read the selection. It cannot insert content. It can only do whatever its remote API exposes.

An Office JavaScript API plugin runs inside an Office Add-in. That add-in is already loaded into the host application. It has access to the Office JS object model. It can read and write the document. When Copilot calls a function on this plugin, that function runs locally with full access to whatever the user is doing.

That is a meaningful capability. It opens up a class of plugin that was very hard to build before.

Where this matters

Three patterns come up immediately in client conversations:

Document-aware actions. "Take the table I just selected in Excel and analyse it." Previously, you would have had to ask the user to paste the table into Copilot, which is friction. With an Office JS plugin, Copilot can ask the plugin "what is the user looking at right now", get a structured representation back, and reason about it.

Generating in-document content. "Insert a summary at the top of this Word document based on the meeting notes I just pasted." The plugin can format and insert content directly. No copy paste back and forth.

Domain-specific manipulation. "Apply our finance team's standard cell formatting to this Excel sheet." The plugin understands what your team's standards are. The plugin can apply them. The user does not have to know the rules.

These are the kinds of asks we get from clients all the time. Up until now the answer was usually "you can sort of do this with a custom Office Add-in launched from a ribbon button". Which works, but defeats the point of Copilot, because the user has to know the add-in exists and click it.

With an API plugin, the user just asks Copilot in natural language, Copilot picks the right function, and the work happens. That is a real shift in the user experience.

How it fits together

The mental model. You have:

  • An Office Add-in. This is the same kind of add-in you might have built before, with a manifest (these days, the unified app manifest), HTML and JavaScript code, Office JS calls, and so on.

  • An API plugin definition. This is an OpenAPI document that describes what functions the plugin exposes, what parameters they take, and what they return. Copilot uses this to decide when to call your plugin.

  • A binding between the two. The OpenAPI document points at functions that live inside your add-in, not at remote URLs. Microsoft handles the wiring.

When Copilot decides to call a plugin function, the call goes through the add-in runtime, not over the network. The function executes locally. It can use Office JS. It returns a result. Copilot uses that result in the response to the user.

The whole thing only works when the user has the relevant Office app open and the add-in loaded. If the user is asking Copilot a question in Teams chat about an Excel file, this kind of plugin will not fire. The plugin is host-specific. It needs the host to be running.

What is genuinely good about this

A few things stand out from early builds we have done.

The data round-trip is much faster than going through an external API. You are not making HTTP calls. You are calling a local function. For interactive workflows, where the user expects Copilot to "do the thing" quickly, this matters.

You do not have to host anything new. If your team already has Office Add-ins in production, you already have the deployment infrastructure. You are adding a plugin definition on top of an existing add-in, not building a new service.

The security model is sensible. The add-in runs in the user's context, with the user's permissions, against the user's document. You are not introducing new data flows or new authentication paths. Whatever the add-in could already see, the plugin can see. Whatever the add-in could already change, the plugin can change. No more, no less.

For Australian organisations with strict data residency requirements, this is a useful property. The data the plugin touches stays inside the Microsoft 365 boundary. The plugin is not posting it to a third party. It is the same compliance story as the rest of M365.

We work through these architecture decisions with clients as part of our Microsoft AI consulting engagements, and this pattern is becoming a real option to put on the table.

What is still rough

Honest assessment. A few things.

The development experience is improving but is not as smooth as building a normal API plugin. You are writing Office Add-in code, plugin manifest, and OpenAPI document, and the three need to stay in sync. Sideloading and testing has more moving parts than I would like.

Documentation is patchy in places. Microsoft's official guide gets you started, but once you go off the happy path the answers are harder to find. Expect to read source code and try things.

Host coverage is uneven. Word and Excel are the strongest. PowerPoint is workable. Outlook works for some scenarios. If you need cross-host behaviour, you will need to write host-specific implementations and feel out the differences.

The "when does Copilot decide to call this plugin" question is, as with all Copilot extensibility, partly a matter of writing very clear function descriptions in the OpenAPI document. If the description is vague, Copilot will not pick the plugin. If two plugins have similar descriptions, Copilot may pick the wrong one. You can iterate on this, but expect to spend time tuning prompts and descriptions before the experience feels natural.

What we recommend

For most clients today, the question is "does this pattern fit our problem". The honest answer is: only if the user is going to be in an Office document, the plugin needs to read or modify that document, and you have or can build an Office Add-in.

If those are all true, this is the right pattern. The integration with Copilot is meaningful. The user experience is much better than ribbon-button-launched add-ins.

If the user is not in an Office document, or the plugin's job is to fetch information rather than manipulate content, a standard API plugin is the right answer. Do not over-complicate it.

If your team has never built an Office Add-in, factor in some learning time. The Office JS object model is its own world. Reading the selection in Word is one thing. Doing it well across different table layouts, content controls, comments, tracked changes, and so on is a craft. We have engineers who have been building Office Add-ins for years and they still get caught out by edge cases.

For Australian organisations exploring Microsoft 365 Copilot extensibility, this pattern is worth a serious look. It is one of the more meaningful additions to the platform in the last twelve months, and it shifts what is reasonable to expect from an in-document assistant.

If you are weighing this approach against a Copilot Studio agent, a declarative agent, or a custom solution sitting outside the M365 boundary, that is the sort of architectural conversation we have all the time with clients. Often the right answer is a mix. Worth a thirty-minute call before you commit to a single direction.

Reference: Build an API plugin with an Office JavaScript Library API - Microsoft Learn.