Cards SDK reference
Every method in the Kustomer Cards SDK, for pages you embed with DynamicCard, widgets, modals, expanded insights views and custom settings pages.
The Cards SDK is the script that lets a page you host talk to Kustomer from inside an iframe. Load it in a page embedded with DynamicCard, a widget, a modal, an expanded insights view or a custom settings page, and it gives you the record the agent is viewing, API access as the agent, and control over the card itself.
Load the SDK
<script src="https://cdn.kustomerapp.com/card-js/latest/kustomer-card.min.js"></script>The script creates one window.Kustomer object. Kustomer opens your page with parentUrl and id query parameters, and the SDK uses them to send and accept postMessage events only from the Kustomer window that framed it. Messages from any other origin are ignored.
- Serve the page over HTTPS, and allow framing by Kustomer: no
X-Frame-Options: DENY, and aframe-ancestorspolicy that includes your customers' Kustomer domains. - Keep the
parentUrlandidparameters if your page redirects. Without them the SDK logs a warning that it's running outside Kustomer and can't reach the agent desktop. - Call
Kustomer.initializeonce on load. A card or widget that never initializes isn't shown.
Context
The context describes what the agent is looking at. You receive it from initialize and every time it changes through Kustomer.on('context'). context.type is one of:
Records are full JSON:API resources, the same shape the REST API returns:
{
"type": "conversation",
"customer": {
"type": "customer",
"id": "6650c1f2a8e3b40012ab34cd",
"attributes": {
"displayName": "Ada Lovelace",
"emails": [{ "type": "home", "email": "ada@example.com" }],
"conversationCounts": { "open": 1, "done": 3, "snoozed": 0, "all": 4 }
},
"relationships": { "org": { "data": { "type": "org", "id": "6650c1f2a8e3b40012ab0001" } } }
},
"conversation": {
"type": "conversation",
"id": "6650c1f2a8e3b40012ab56ef",
"attributes": { "name": "Where is my order?", "channels": ["email"], "status": "open", "messageCount": 1 }
}
}To send your own data instead, pass a custom context to DynamicCard. Your page receives the object as is:
<DynamicCard src="https://kustomer.acme.example/card" context={{ type: 'acmeOrder', customContext: { orderId: object.externalId } }} />Context data can contain masked fields. Read optional values defensively.
Setup and events
Kustomer.initialize
Kustomer.initialize(options?, callback?): Promise<[context, { userRoles }]>Connects your page to Kustomer and delivers the first context. The callback receives (context, { userRoles }), and the returned promise resolves with the same two values as an array. Only the first call does anything; later calls resolve with undefined.
debugboolean- Log every message the SDK sends and receives to the console, prefixed
[Kustomer Card SDK]. Also logs ignored messages and request errors. userRolesstring[]- Roles to check for the current agent, such as
org.admin.write. The callback'suserRolesmaps each one totrueorfalse. subscribedFlagsstring[]- Kustomer feature flags your page wants to receive through the
flagsevent.
Kustomer.initialize({ userRoles: ['org.admin.write'] }, (context, { userRoles }) => {
render(context, { canEdit: userRoles['org.admin.write'] })
Kustomer.resize()
})
// Or with the promise:
const [context, { userRoles }] = await Kustomer.initialize({ userRoles: ['org.admin.write'] })Pass only a callback to skip the options: Kustomer.initialize((context) => …).
Kustomer.on
Kustomer.on(event: string, handler: (data, { userRoles }) => void): voidSubscribes to an event. Each event keeps one handler, so a second on for the same event replaces the first. An unknown event name is ignored, with a warning when debug is on.
Kustomer.on('context', (context) => {
render(context)
Kustomer.resize()
})Theme updates also arrive with initialize, so pages that use the theme variables restyle without handling theme at all.
Call APIs
Kustomer.request
Kustomer.request({ method, url, body?, verboseResponse?, targetModal?, modalToken? }, callback?): Promise<data>Calls the Kustomer REST API as the agent, with the agent's permissions. No API key is involved. The callback receives (error, data). The promise resolves with the data, or rejects with an array whose first item is the error.
methodstringrequiredget,post,put,patchordelete, in either case.urlstringrequired- The API path, such as
/v1/customers/6650c1f2a8e3b40012ab34cd. See the API reference. bodyobject- The JSON request body.
verboseResponseboolean- Return the whole response body, including
metaandlinksfor pagination, instead of only itsdata. targetModalboolean- Also send the response to the modal that's currently visible.
modalTokenstring- Identifies the modal that should receive the response, when you use
targetModal.
async function closeConversation(context) {
if (context.type !== 'conversation') return
try {
await Kustomer.request({ method: 'patch', url: `/v1/conversations/${context.conversation.id}`, body: { status: 'done' } })
Kustomer.handleTriggerToast({ type: 'success', message: 'Conversation closed' })
} catch ([err]) {
Kustomer.updateStatus({ status: 'danger', description: 'Could not update the conversation.' })
}
}Kustomer.command.run
Kustomer.command.run(name: string, body?: { args?, headers?, body? }, callback?): Promise<result>Runs one of your app's commands, so your page can call your own API with credentials that stay on Kustomer's servers. It's a POST to /v1/commands/{name}/run through Kustomer.request. The promise resolves with the response's attributes, which hold your API's response, and rejects like request. The callback receives (error, result).
const order = await Kustomer.command.run('acme_orders.app.lookup-order', {
args: { storeDomain: 'shop.acme.example', orderId: '4417' },
})Card display
Kustomer.resize
Kustomer.resize({ height?, width?, modal? }?): voidResizes the iframe. With no height, the SDK measures your page and fits the card to it, so call Kustomer.resize() after every render that changes your page's height.
heightnumber | string- Pixels as a number, or a CSS length such as
"320px". widthnumber | string- The same as
height. Without it, the width doesn't change. modalboolean- Resize the visible modal instead of the card. The SDK checks only that the key is present, so leave it out entirely to resize the card.
modal.resizedoes this for you.
Kustomer.updateStatus
Kustomer.updateStatus({ status: 'info' | 'warning' | 'danger', description: string }): voidShows a colored banner on the card, for example when your API is unreachable. Any other status is ignored, with a warning when debug is on.
Kustomer.updateStatus({ status: 'warning', description: 'Acme is slow to respond. Order data may be out of date.' })Kustomer.clearStatus
Kustomer.clearStatus(): voidRemoves the banner shown by updateStatus.
Kustomer.handleTriggerToast
Kustomer.handleTriggerToast({ type: 'info' | 'warning' | 'error' | 'success', message: string }): voidShows a toast notification in Kustomer, to confirm or report something the agent did.
Kustomer.handleTriggerToast({ type: 'success', message: 'Refunded order 4417' })Navigation and windows
Kustomer.openCustomer
Kustomer.openCustomer(customerId: string): voidOpens a customer's timeline.
Kustomer.openCustomerEvent
Kustomer.openCustomerEvent(customerId: string, eventId: string): voidOpens a customer's timeline at a specific item, such as a conversation.
Kustomer.openCustomerEvent('6650c1f2a8e3b40012ab34cd', '6650c1f2a8e3b40012ab56ef')Kustomer.expandInsightsCard
Kustomer.expandInsightsCard({ appName, appLogo, title, titleExternalLink?, titleLinkText?, height, url, context }): voidOpens an expanded view that fills the whole insights panel, for detail that doesn't fit in a card, such as a full order. The view stays open while the agent moves between conversations and items on the same timeline, and closes on its own when the agent opens another customer.
urlstringrequired- The page to show in the expanded view. It's a separate page that loads the Cards SDK too.
contextanyrequired- Data for the expanded page. Its
Kustomer.initializereceives this value as its context. titlestringrequired- The view's title.
appNamestringrequired- Your app's name, shown in the view's header.
appLogostringrequired- The URL of your app's logo, shown in the view's header.
heightnumberrequired- The view's height, in pixels.
titleExternalLinkstring- A URL linked from the header, such as the record in your own system.
titleLinkTextstring- The text of that link, such as the order number.
It returns nothing and takes no callback. A callback you pass is never called, so don't wait on one.
function showOrder(order) {
Kustomer.expandInsightsCard({
appName: 'Acme Orders',
appLogo: 'https://kustomer.acme.example/logo.png',
title: 'Order',
titleExternalLink: `https://admin.acme.example/orders/${order.id}`,
titleLinkText: `#${order.number}`,
height: 600,
url: 'https://kustomer.acme.example/order-detail',
context: order,
})
}Kustomer.initialize((order) => {
renderOrder(order)
})
document.querySelector('#done').addEventListener('click', () => Kustomer.contractInsightsCard())Kustomer.contractInsightsCard
Kustomer.contractInsightsCard(): voidCloses the expanded insights view. Like expandInsightsCard, it returns nothing and never calls a callback.
Kustomer.open
Kustomer.open(): voidOpens your widget, for example on an incoming call, or to bring the agent back when they close it with unsaved work.
Kustomer.close
Kustomer.close(): voidRemoves your iframe from the page, for example when a card has nothing to show for this customer.
Kustomer.initialize((context) => {
if (!context.customer?.attributes.emails?.length) return Kustomer.close()
render(context)
Kustomer.resize()
})Agent status
Kustomer.requestUpdateUserStatus
Kustomer.requestUpdateUserStatus({ fallbackStatusType, appStatusCode? }): voidAsks Kustomer to change the agent's routing status. Voice and softphone apps use it to keep Kustomer in step with the agent's state on the phone platform, alongside the routing-status-change event for the other direction. Nothing is sent without fallbackStatusType.
fallbackStatusTypestringrequired- When no status maps
appStatusCode, Kustomer sets its built-in status of this type:available,busy,unavailableoroffline. appStatusCodestring- Your platform's code for the agent's status. Kustomer sets the routing status whose
externalStatusMaphas this code under your app'smeta.externalQueue. See Map agent statuses.
softphone.on('call-started', () => Kustomer.requestUpdateUserStatus({ fallbackStatusType: 'busy' }))App setup flow
A setup page walks an admin through connecting your app while they install it. These four methods tell Kustomer where the admin is in that flow. Each returns nothing and never calls a callback.
Kustomer.handleAppSetupStepsStarted
Kustomer.handleAppSetupStepsStarted(): voidThe admin started the setup steps.
Kustomer.handleAppSetupStepsComplete
Kustomer.handleAppSetupStepsComplete(): voidThe admin finished the steps that must happen before install, and Kustomer can install the app. Call it once.
Kustomer.handleAppSetupStepsCanceled
Kustomer.handleAppSetupStepsCanceled(): voidThe admin canceled setup.
Kustomer.handleAppSetupStepsClosed
Kustomer.handleAppSetupStepsClosed(): voidThe admin closed the setup page.
Modals
A card can open modals: each is its own page, which also loads the Cards SDK. Register a modal with init, then show and hide it as needed. A card can have up to three registered at once.
await Kustomer.modal.init({ id: 'refund', url: 'https://kustomer.acme.example/refund', title: 'Refund order', additionalData: { orderId: '4417' }, height: 480, width: 560 })
Kustomer.modal.show('refund')
// Later, from the modal or the card:
Kustomer.modal.hide('refund', 'Refund issued')
Kustomer.modal.destroy('refund')The modal page loads the SDK and reads the additionalData you passed from the modal_initialize event:
Kustomer.on('modal_initialize', (context) => {
renderRefund(context.modalAdditionalData.orderId)
})Kustomer.modal.init
Kustomer.modal.init({ id, url, title?, additionalData?, height?, width? }, callback?): PromiseRegisters a modal without showing it. The promise resolves, and the callback runs, once Kustomer has set the modal up.
idstring | numberrequired- Your name for the modal. Throws if it's missing or already registered.
urlstringrequired- The modal's page.
titlestring- The title Kustomer shows above the modal.
additionalDatastring | number | object- Data for the modal page, which receives it as
modalAdditionalDatain themodal_initializeevent. heightnumber | string- Pixels, or a CSS length.
widthnumber | string- Pixels, or a CSS length.
Throws once three modals are registered. Free a slot with destroy.
Kustomer.modal.show
Kustomer.modal.show(id: string | number, title?: string): voidShows a registered modal and hides any other. Pass title to change the title set in init.
Kustomer.modal.hide
Kustomer.modal.hide(id: string | number, toast?: string): voidHides the modal but keeps it registered, so you can show it again. With toast, Kustomer shows that text as a success toast.
Kustomer.modal.resize
Kustomer.modal.resize({ height?, width? }): voidResizes the visible modal. Without height, the SDK measures the page that calls it.
Kustomer.modal.destroy
Kustomer.modal.destroy(id: string | number): voidCloses the modal if it's showing and unregisters it, which frees a slot.
Kustomer.modal.destroyAll
Kustomer.modal.destroyAll(): voidCloses and unregisters every modal.
Kustomer.showModal
Kustomer.showModal(modalContent: object): void