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

HTML
<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 a frame-ancestors policy that includes your customers' Kustomer domains.
  • Keep the parentUrl and id parameters 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.initialize once 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:

typeWhereCarries
customerA card on a customer timeline with no item expandedcustomer
conversationA card while the agent has a conversation opencustomer, conversation
companyA card on a companycompany
kobject.<klass>A card while a custom object is expanded, for example kobject.acme_ordercustomer and the object
widgetA widget, anywhere but a customer's timeline. On a timeline, a widget gets that page's type and records.widget (your app, with isOpen), path and isWidget, which every widget context has
appA custom settings pageThe app
Your ownA DynamicCard given a context propYour customContext

Records are full JSON:API resources, the same shape the REST API returns:

A conversation context (trimmed)
{
  "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:

template
<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

TypeScript
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.

options
debugboolean
userRolesstring[]
subscribedFlagsstring[]
JavaScript
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

TypeScript
Kustomer.on(event: string, handler: (data, { userRoles }) => void): void

Subscribes 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.

EventFires whendata
contextThe agent moves to another conversation, timeline item or record, or opens or closes a widgetThe new context
themeThe agent's Kustomer theme changes, such as switching to dark modeThe theme update. Before your handler runs, the SDK has already set the theme's CSS variables and color-scheme on your page's root element.
flagsThe values of your subscribedFlags are sent or changeAn object of flag names to booleans
number-dialedThe agent clicks a phone number in Kustomer to call it. Needs the click-to-dial data subscription.The number to dial, as a string
routing-status-changeThe agent's routing status changes, such as available to busy. Needs status-sync.The new status: { app, externalQueue, kustomerStatusId, externalStatusId, statusType }
kustomer-typing-status-changedThe agent's typing status changes in Kustomer. Needs typing-indicators.The typing status
JavaScript
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

TypeScript
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.

options
methodstringrequired
urlstringrequired
bodyobject
verboseResponseboolean
targetModalboolean
modalTokenstring
JavaScript
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

TypeScript
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).

JavaScript
const order = await Kustomer.command.run('acme_orders.app.lookup-order', {
  args: { storeDomain: 'shop.acme.example', orderId: '4417' },
})

Card display

Kustomer.resize

TypeScript
Kustomer.resize({ height?, width?, modal? }?): void

Resizes 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.

options
heightnumber | string
widthnumber | string
modalboolean

Kustomer.updateStatus

TypeScript
Kustomer.updateStatus({ status: 'info' | 'warning' | 'danger', description: string }): void

Shows 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.

JavaScript
Kustomer.updateStatus({ status: 'warning', description: 'Acme is slow to respond. Order data may be out of date.' })

Kustomer.clearStatus

TypeScript
Kustomer.clearStatus(): void

Removes the banner shown by updateStatus.

Kustomer.handleTriggerToast

TypeScript
Kustomer.handleTriggerToast({ type: 'info' | 'warning' | 'error' | 'success', message: string }): void

Shows a toast notification in Kustomer, to confirm or report something the agent did.

JavaScript
Kustomer.handleTriggerToast({ type: 'success', message: 'Refunded order 4417' })

Kustomer.openCustomer

TypeScript
Kustomer.openCustomer(customerId: string): void

Opens a customer's timeline.

Kustomer.openCustomerEvent

TypeScript
Kustomer.openCustomerEvent(customerId: string, eventId: string): void

Opens a customer's timeline at a specific item, such as a conversation.

JavaScript
Kustomer.openCustomerEvent('6650c1f2a8e3b40012ab34cd', '6650c1f2a8e3b40012ab56ef')

Kustomer.expandInsightsCard

TypeScript
Kustomer.expandInsightsCard({ appName, appLogo, title, titleExternalLink?, titleLinkText?, height, url, context }): void

Opens 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.

params
urlstringrequired
contextanyrequired
titlestringrequired
appNamestringrequired
appLogostringrequired
heightnumberrequired
titleExternalLinkstring
titleLinkTextstring

It returns nothing and takes no callback. A callback you pass is never called, so don't wait on one.

card.js
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,
  })
}
order-detail.js
Kustomer.initialize((order) => {
  renderOrder(order)
})

document.querySelector('#done').addEventListener('click', () => Kustomer.contractInsightsCard())

Kustomer.contractInsightsCard

TypeScript
Kustomer.contractInsightsCard(): void

Closes the expanded insights view. Like expandInsightsCard, it returns nothing and never calls a callback.

Kustomer.open

TypeScript
Kustomer.open(): void

Opens your widget, for example on an incoming call, or to bring the agent back when they close it with unsaved work.

Kustomer.close

TypeScript
Kustomer.close(): void

Removes your iframe from the page, for example when a card has nothing to show for this customer.

JavaScript
Kustomer.initialize((context) => {
  if (!context.customer?.attributes.emails?.length) return Kustomer.close()
  render(context)
  Kustomer.resize()
})

Agent status

Kustomer.requestUpdateUserStatus

TypeScript
Kustomer.requestUpdateUserStatus({ fallbackStatusType, appStatusCode? }): void

Asks 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.

params
fallbackStatusTypestringrequired
appStatusCodestring
JavaScript
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

TypeScript
Kustomer.handleAppSetupStepsStarted(): void

The admin started the setup steps.

Kustomer.handleAppSetupStepsComplete

TypeScript
Kustomer.handleAppSetupStepsComplete(): void

The admin finished the steps that must happen before install, and Kustomer can install the app. Call it once.

Kustomer.handleAppSetupStepsCanceled

TypeScript
Kustomer.handleAppSetupStepsCanceled(): void

The admin canceled setup.

Kustomer.handleAppSetupStepsClosed

TypeScript
Kustomer.handleAppSetupStepsClosed(): void

The 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.

JavaScript
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:

refund.js
Kustomer.on('modal_initialize', (context) => {
  renderRefund(context.modalAdditionalData.orderId)
})

Kustomer.modal.init

TypeScript
Kustomer.modal.init({ id, url, title?, additionalData?, height?, width? }, callback?): Promise

Registers a modal without showing it. The promise resolves, and the callback runs, once Kustomer has set the modal up.

options
idstring | numberrequired
urlstringrequired
titlestring
additionalDatastring | number | object
heightnumber | string
widthnumber | string

Throws once three modals are registered. Free a slot with destroy.

Kustomer.modal.show

TypeScript
Kustomer.modal.show(id: string | number, title?: string): void

Shows a registered modal and hides any other. Pass title to change the title set in init.

Kustomer.modal.hide

TypeScript
Kustomer.modal.hide(id: string | number, toast?: string): void

Hides 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

TypeScript
Kustomer.modal.resize({ height?, width? }): void

Resizes the visible modal. Without height, the SDK measures the page that calls it.

Kustomer.modal.destroy

TypeScript
Kustomer.modal.destroy(id: string | number): void

Closes the modal if it's showing and unregisters it, which frees a slot.

Kustomer.modal.destroyAll

TypeScript
Kustomer.modal.destroyAll(): void

Closes and unregisters every modal.

Kustomer.showModal

TypeScript
Kustomer.showModal(modalContent: object): void

Troubleshooting

SymptomCheck
"kustomer-card.js appears to be running outside the Kustomer environment"The page lost its parentUrl and id query parameters, often through a redirect, or it isn't in a Kustomer iframe.
The card or widget never appearsCall Kustomer.initialize on load. Turn on initialize({ debug: true }, …) to see each message.
Nothing happens and there are no errorsMost methods do nothing quietly on bad input, such as an unknown status in updateStatus or an unknown event in on. With debug: true the SDK warns about these.
request responses or events never arriveThe parentUrl query parameter was changed, so the SDK ignores Kustomer's messages as coming from an unknown origin. With debug: true it logs each one it ignores.
A promise around expandInsightsCard never settlesIt takes no callback and returns nothing. Don't wait on it.
A maximum of 3 modals may be on the stackdestroy a modal you no longer need before registering another.