What is the CMD+K widget postMessage API?
The postMessage API lets your page open, close, toggle, prefill, and theme the CMD+K widget without touching its DOM. Use it to wire a custom launcher button anywhere in your UI or seed the chat with a starter question. All host messages are scoped to the cmdk:host: namespace so they don't collide with your app, and the same controls are mirrored as functions on window.CommandK.
Host → widget messages
Post these to your own window (the loader listens for same-window messages) from anywhere in your app:
- cmdk:host:open — open the chat. Optional payload: { text?: string, submit?: boolean } —
textprefills the composer, andsubmit(default true when text is present) sends it immediately. - cmdk:host:close — close the chat. No payload.
- cmdk:host:toggle — toggle open/closed. No payload.
- cmdk:host:set_theme — switch theme. Payload: { theme: "light" | "dark" | "auto" }.
End-user identity is not a message
data-cmdk-token script attribute at load — there is no identify/refresh message in v1. See End-user authentication.The window.CommandK functions
The loader exposes the same controls (plus walkthrough helpers) as plain functions:
CommandK.open(text?, submit?) // open, optionally prefill/send
CommandK.close() // close
CommandK.toggle() // toggle
CommandK.isOpen() // boolean
CommandK.setTheme("dark") // "light" | "dark" | "auto"
CommandK.startWalkthrough(id) // run a published guided tour by id
CommandK.listWalkthroughs() // [{ id, name }]
CommandK.snoozeWalkthroughs(ms)
CommandK.resetWalkthroughs() // clear first-visit/opt-out flags (QA)
CommandK.fireTourEvent(name) // trigger event-based tourswindow.CommandK exists from the moment the loader script runs, before the widget has finished booting. A call made in that first moment — a click on your own “Ask AI” button right after a page load — is queued and replayed as soon as the widget is ready, so the first click always opens the panel. The same is true for cmdk:host:*messages posted early.
Custom launcher example
<button id="ask-ai">Ask AI</button>
<script>
document.getElementById("ask-ai").addEventListener("click", () => {
window.postMessage(
{ type: "cmdk:host:open", text: "How do I invite my team?" },
window.location.origin
);
// …or equivalently: window.CommandK.open("How do I invite my team?");
});
</script>Pair this with data-cmdk-launcher="custom" if you want the loader to inject no launcher of its own.
What about widget → host messages?
The chat iframe and the loader exchange their own messages (cmdk:ready, cmdk:init, cmdk:close, page-context, walkthrough, and consent-gated page-action requests). These are an internal transport between the two CMD+K components, not a public event stream for your page — don't build on them from host code. The complete inventory, published for transparency and versioned as v1 (stable), lives in the partner embed guide.
Next steps
FAQ
- Why postMessage and not a JS API?
- Both exist. The chat runs in an iframe, so its DOM is isolated from your page; the loader script listens for cmdk:host:* messages on your own window and also exposes the same controls as window.CommandK functions. Use whichever fits your code.
- Can I read messages from the widget?
- The messages the chat iframe exchanges with the loader (cmdk:ready, cmdk:close, walkthrough and page-context events) are an internal transport, not a public host-page event stream — handle opening/closing state via your own calls to CommandK.open()/close()/isOpen(). The full internal inventory is documented on the partner embed page for transparency.
- How do I refresh an expired end-user JWT?
- There is no refresh message in v1. The token is read once from data-cmdk-token at page load and handed to the iframe during the cmdk:init handshake; when it expires the widget asks the visitor to refresh the page. Mint a short-lived JWT on every page render.
- Is there a TypeScript SDK?
- Not yet. The raw cmdk:host:* messages and the window.CommandK functions documented here are the stable v1 surface.