Stackable Extensions
One extension model, from the messenger to the agent workspace
How Stackable Extensions work: a sandboxed runtime, a declared manifest, host-mediated capabilities and named slots. Why that model powers Stackable Messenger today, and why the same extensions are coming to agnoStack next.
Stackable Messenger lets brands put their own interactive UI beside a Zendesk Messenger conversation. The unit that makes that possible is the extension. This post is about what an extension is, the constraints we built it around, and why the same model is about to show up somewhere new: inside agnoStack, in the Zendesk agent workspace.
The problem an extension model has to solve
Any platform that runs other people's code inside its own product has three competing goals:
- Extensions have to be genuinely useful. They need real data, real actions and real UI, not a link out to somewhere else.
- The host has to stay in charge. A third-party extension must not be able to read what it shouldn't, call wherever it likes or break the page around it.
- Building one has to be fast. If shipping an extension takes weeks of per-customer setup, nobody builds them.
Most embedding approaches pick two. A remote iframe per integration is isolated but looks and behaves like a foreign object in the page. Loading a script straight into the host is seamless but trusts it completely. We wanted all three.
What a Stackable Extension is
A Stackable Extension is a sandboxed JavaScript application built with the Stackable SDK (@stackable-labs/sdk-extension-react and its contracts package), plus a manifest that declares what it needs:
{
"name": "Order Lookup",
"targets": ["slot.content"],
"permissions": ["context:read", "data:fetch"],
"allowedDomains": ["api.example-store.com"]
}
Everything interesting follows from that declaration.
Targets: extensions render into named slots
A host exposes slots, named places where an extension may render. An extension lists the slots it targets and supplies a surface for each. The host decides where slots live, how big they are and when they appear, and the extension decides what goes in them. Neither needs to know the other's layout.
Permissions: capabilities, not raw access
An extension doesn't get the host's globals, and it doesn't get unrestricted network access. It asks the host for things through capabilities: read the current context (who the customer is, which conversation this is), fetch data, invoke an action, send a message. Each capability maps to a permission the manifest has to declare, and the host refuses anything undeclared.
Allowed domains: the network is opt-in
Direct fetch is blocked inside the sandbox. External calls go through the data.fetch capability, and only to hostnames the manifest safelists. A brand installing an extension can see exactly where it will send data before it's allowed to.
The host renders, the extension describes
Extensions run in an isolated sandbox. Rather than painting pixels into the host page, an extension describes its UI using the SDK's component set, and the host renders that description with its own components. This choice does a lot of work:
- The UI inherits the host's theme, so an extension looks native in whichever product it's running in.
- The host controls layout and sizing, so a misbehaving extension can't break the page around it.
- The same extension code can render correctly in more than one host, because each host brings its own rendering of the shared component vocabulary.
That last point is the one that matters for what comes next.
Where extensions run today: Stackable Messenger
In Stackable Messenger, the host is the layer around Zendesk Messenger. It exposes slots around the conversation, provides the customer and conversation as context, and mediates data and messaging capabilities. A brand installs an extension from the marketplace (or builds its own), configures it for its instance, and it appears for customers the next time they open the messenger.
On the build side, the SDK, CLI and Stackable AI Studio exist to make that loop short: scaffold an extension, iterate against a local preview, validate the manifest, deploy, and publish a listing. Documentation, recipes and a full capability reference live at developers.stackablelabs.com.
Where extensions go next: agnoStack
agnoStack puts commerce data and actions in front of support agents inside the Zendesk agent workspace: orders, shipments, refunds, subscriptions and loyalty, in the ticket. Every retailer we work with has something specific to them that sits next to an order. A warranty lookup, a custom fulfillment status, a loyalty tier from a system we don't integrate with, a one-off approval flow.
Historically that meant a feature request. With Stackable Extensions, agnoStack becomes a host.
The same model carries over directly:
- Slots, named for the agent workspace. agnoStack exposes slots at meaningful points in its panels (the order panel's header, for example), named by what they sit next to, so an extension can put its UI exactly beside the order it relates to.
- Context from the ticket. The capability layer hands the extension the ticket and commerce context agnoStack already has, so an extension starts from the right customer and order without making the agent search.
- The same guardrails. Declared permissions, safelisted domains and host-side rendering apply unchanged, which matters even more when the audience is an agent with access to customer data.
- Native look, for free. Because agnoStack renders the extension's UI with its own components, an extension looks like part of agnoStack, not a panel from somewhere else.
The payoff is that a team building on Stackable doesn't start over for a new surface. The skills, the SDK and much of the code carry across, from what the customer sees in the messenger to what the agent sees in the ticket.
What's next
Extensions in agnoStack are in active development, and we'll share more as they roll out. In the meantime, the fastest way to get a feel for the model is to build one: start with the quick start, or read how the idea started in our Stackable Messenger post.
