Widgets
Add an iframe agents can open from the Kustomer navigation rail on any page, with per-org URLs, sizing, browser permissions and background loading.
A widget is a page you host that agents open from the navigation rail, whatever they're looking at. Use one for tools agents need alongside every conversation: a softphone, an order lookup, a knowledge tool, a portal into another system. Unlike a KView, a widget isn't tied to a record. It stays open as the agent moves around, and receives context updates through the Cards SDK.
If an org has one widget, its icon sits directly in the rail. With several, they're grouped under a widgets menu.
Define a widget
{
"settings": {
"default": {
"storeDomain": { "type": "string", "defaultValue": "", "required": true }
}
},
"widgets": [
{
"name": "order-lookup",
"title": "Acme lookup",
"url": "https://kustomer.acme.example/widget?store={{{settings.default.storeDomain}}}",
"icon": "https://kustomer.acme.example/widget-icon.png",
"width": "24vw",
"height": "60vh",
"iframePermissions": "microphone *; autoplay *;",
"backgroundLoad": false
}
]
}urlstringrequired- The page to load. Reference your app's settings with
{{{settings.<category>.<name>}}}to give each org its own URL. Widgets can only read their own app's settings. iconURLrequired- An SVG or PNG, at least 128×128 px, shown in the navigation rail.
titlestring- The widget's title in Kustomer.
namestring- Tells an app's widgets apart, and keys their translations:
<app>.widgets.<name>.title. widthnumber | string- Pixels as a number, or any CSS length such as
"24vw"or"calc(30vmin + 50px)". heightnumber | string- Same as
width. iframePermissionsstring- Browser features to delegate to the iframe, in Permissions Policy syntax:
"microphone *; camera *; autoplay *;". Kustomer supportsmicrophone,cameraandautoplay. backgroundLoadboolean- Load the widget when Kustomer starts, before the agent opens it. Needed for softphones that must ring while closed.
Talk to Kustomer from the widget
A widget page loads the same Cards SDK as an embedded KView:
<script src="https://cdn.kustomerapp.com/card-js/latest/kustomer-card.min.js"></script>
<script>
Kustomer.initialize((context) => {
showFor(context)
})
// Fires when the context changes, including when the agent opens or closes the widget.
Kustomer.on('context', (context) => {
if (context.widget && !context.widget.isOpen && hasUnsavedWork()) {
Kustomer.open() // reopen so the agent can finish
return
}
showFor(context)
})
</script>The widget context has type: "widget" and widget.isOpen, so your page can react when the agent opens or closes it. From there you can:
- Read and write Kustomer data with
Kustomer.request(as the agent). - Call your own API with credentials kept server-side through commands.
- Send the agent to a customer with
Kustomer.openCustomer(id)orKustomer.openCustomerEvent(customerId, eventId). - Open the widget with
Kustomer.open(), for example on an incoming call.
Voice and presence events
Softphone widgets can subscribe to telephony and routing events:
Kustomer.on('number-dialed', (payload) => softphone.dial(payload))Background loading
backgroundLoad: true loads your page for every agent in the org as soon as Kustomer opens. Keep that cheap:
- Load under 1 MB of assets before the agent interacts.
- Keep
consoleoutput to a minimum. - Don't open the widget automatically unless something needs the agent, like an incoming call. Several auto-opening widgets competing for attention frustrate agents.
Design checklist
- Works at the smallest width you set, and at 2× that.
- Uses the theme's CSS variables so it's readable in dark mode.
- Handles masked fields in context data.
- Shows a useful state when settings are missing, instead of a blank frame.
- Served over HTTPS and allows framing by Kustomer (no
X-Frame-Options: DENY, and aframe-ancestorspolicy that includes your customers' Kustomer domains).