Mindset AI

Docs / SDK reference / Drop-in element

The <mindset-agent> element

The full reference for the drop-in chat element: attributes, methods, events, theming.

The drop-in half of the SDK. One custom element that renders a complete chat interface on your page, carrying its own styles, in its own shadow root. Your page loads one script and writes one tag.

Loading it

<script src="https://YOUR-MINDSET-HOST/sdk/mindset-agent.js"></script>

<mindset-agent agent="support-bot"></mindset-agent>

<script>
  document.querySelector("mindset-agent").configure({
    getSession: () => myBackend.fetchAgentSession(),
  });
</script>

The script registers the mindset-agent tag itself.

Configuring it

configure() is what makes the element live. Until you call it, the element renders nothing and send() won't work.

element.configure({
  getSession: () => myBackend.fetchAgentSession(),
});

getSession is a function that returns the session credential, not the credential itself. Your backend creates that credential server-to-server; the element decodes it, runs on it, and renews it without your involvement.

Passing a static string won't work, because a string can't be renewed. An organization API key is dropped rather than sent.

The credential carries everything else the element needs, including your organization and Environment, so there's nothing further to configure.

Attributes and properties

Every attribute mirrors a property in both directions, so declarative and imperative configuration can't disagree. React 19 sets unknown props as DOM properties, which is why each one is a real accessor pair.

Property Attribute What it is
agent agent Which agent to run. Its handle, or its UUID. Copy either from the agent's Embed tab
conversationId conversation-id Which conversation to resume. See below

Note the attribute name is conversation-id in markup and conversationId as a property. HTML lowercases attribute names when it parses, so the two forms are unavoidable.

Resuming a conversation

By default, every time the element mounts your user gets a fresh conversation. If you want someone to come back to what they were doing, this is the section that matters.

It's a round trip, and you don't need to know an ID to start using one.

First visit. Leave conversation-id off. The element starts a fresh conversation. On the first completed turn, once there's something worth coming back to, it tells you the ID two ways: it fires a mindset:conversation event, and it writes the ID onto its own conversation-id attribute.

Store it against whoever is looking, in your own database, keyed to that user.

Next visit. Set conversation-id to the value you stored. The agent picks up with the history it had.

element.addEventListener("mindset:conversation", (e) => {
  saveForCurrentUser(e.detail.conversationId);
});

Rules worth knowing

Prefer the event over reading the attribute back. If your stack morphs the DOM towards a template, it will strip an attribute the template doesn't declare, and the element deliberately doesn't fight your renderer to put it back. Store what the event gave you.

Clearing the attribute does nothing. That's on purpose. An unrelated DOM update must not cost a user their conversation.

Writing the same ID back is safe. A framework re-render doing exactly that is a no-op.

Writing a different ID is a reconfiguration. The running conversation stops, loudly, rather than switching silently.

One thing to get right

A conversation ID is per user. Resolve it per request, from what your backend knows about who is asking.

Templating one into static server-rendered HTML serves one user's conversation to everybody who loads the page, and no check inside the element can tell that apart from a host legitimately setting the attribute from code.

The ID is a correlation key, not a credential. It already travels in every request the element makes, and holding it grants nothing without the session credential. That's why it's safe to express as an attribute at all.

Methods

Method What it does
configure() Set this instance up. Calling it again tears down the previous conversation and starts one for the new config, so a host that reconfigures on identity change can't end up with two live conversations on one element
send() Send a user turn. Before configure(), this reports an error rather than silently dropping the message
stop() Stop the turn in flight. Safe to call when nothing is running

Each of these maps onto a documented headless command. The element is a thin shell over the same contract, with no private channel to the runtime.

Events

DOM CustomEvents, namespaced so they can't collide with your own, dispatched so a listener on an ancestor outside the shadow root still sees them.

Event detail
mindset:runtime-event A RuntimeEvent. The full runtime vocabulary, surfaced directly. See the events reference
mindset:error Carries code, message and an optional cause. Configuration and transport failures you should surface
mindset:conversation Carries the conversationId. Fires once: at mount if you supplied the ID, or on the first completed turn if the element started fresh

mindset:conversation carries the ID and nothing else, deliberately. It crosses the shadow boundary into your document, where every script on the page can read it.

These are additive only. A new event type is a compatible change; renaming or reshaping one is not. Write consumers that switch on the type and ignore anything they don't recognize.

Error codes

mindset:error detail is always structured, never prose alone. Branch on code and treat an unknown code as a generic failure.

code Raised when
missing-agent configure() ran with no agent, from either the attribute or the config
invalid-auth configure() got no getSession function
not-configured send() was called with no live conversation, either before configure() or after a real disconnect
stale-configuration An attribute changed after configure(), so the live conversation no longer matches what the element reports. Call configure() again
turn-failed The turn itself failed: transport, credentials, or the run

turn-failed is worth handling specifically. It's the class of failure you can't diagnose any other way, because the SDK fetches its envelope before the first runtime event, so an expired credential produces no mindset:runtime-event at all.

Theming

Theming is CSS custom properties that pierce the shadow boundary. There's no ::part() API in this version, so don't plan around one.

The element ships a default set of colors on its own shadow host, so an embed on a page with no theme renders in real colors with no cooperation from you. Add class="dark" to the tag for the dark set.

Override any channel from your own stylesheet. A rule on the host element wins over the element's internal defaults.

mindset-agent {
  --ch-accent: 124 58 237;   /* space-separated r g b */
  --ch-page: 255 255 255;
}

Values are space-separated RGB components, not hex and not rgb().

The full vocabulary is 62 channels. The list may change as the interface develops, so treat it as the current set rather than a fixed contract.

--ch-page                       --ch-panel                      --ch-card
--ch-surface                    --ch-input                      --ch-card-hover
--ch-selected                   --ch-overlay                    --ch-text-heading
--ch-text-primary               --ch-text-secondary             --ch-text-muted
--ch-logo                       --ch-edge                       --ch-edge-subtle
--ch-edge-strong                --ch-btn-primary                --ch-btn-primary-hover
--ch-btn-secondary              --ch-btn-secondary-hover        --ch-accent
--ch-accent-hover               --ch-on-accent                  --ch-agent
--ch-agent-hover                --ch-node-agent                 --ch-node-connection
--ch-node-tool                  --ch-node-knowledge              --ch-node-widget
--ch-node-function               --ch-chip                       --ch-chip-text
--ch-chip-border                --ch-status-warning-bg           --ch-status-warning-text
--ch-status-warning-border       --ch-status-success-bg          --ch-status-success-text
--ch-status-success-border       --ch-status-info-bg             --ch-status-info-text
--ch-status-info-border          --ch-status-danger-bg           --ch-status-danger-text
--ch-status-danger-border        --ch-status-neutral-bg          --ch-status-neutral-text
--ch-status-neutral-border       --ch-phase-specify-bg           --ch-phase-specify-text
--ch-phase-specify-border        --ch-phase-specify-container    --ch-phase-build-bg
--ch-phase-build-text            --ch-phase-build-border         --ch-phase-build-container
--ch-phase-verify-bg             --ch-phase-verify-text          --ch-phase-verify-border
--ch-phase-verify-container      --ch-phase-draft-container

Isolation runs both ways and it's structural, not conventional. The interface lives in a shadow root and the compiled stylesheet is adopted into that root rather than added to your document, so neither side's CSS can reach the other.