MIDNIGHT/ docs
PricingSign inStart free

Embed guide

The widget is a single script tag that mounts an isolated chat panel on your page. This page lists every attribute it accepts and every method it exposes.

The script tag

html
<script
  src="https://your-dashboard-origin/widget.js"
  data-agent="your-agent-public-key"
  async
></script>

Copy it from the Deploy tab rather than typing it: the tab fills in your agent's public key, a 32-character hex string, and points src at the origin serving your dashboard.

The widget renders inside a <div id="midnight-chat-widget"> with an open shadow root. Your page's CSS cannot leak in and the widget's cannot leak out, so it looks the same on a Tailwind site and a 2009 stylesheet.

Attributes

Only data-agent is required. Without it the widget logs an error and does not mount.

AttributeValueDefault
data-agentYour agent's public key.Required
data-apiOrigin of the chat API.Baked into the bundle at build time. Only set this if you self-host the API.
data-openSet to true to open the panel on load.Closed
data-accentAny CSS colour for the launcher and buttons.Your agent's configured accent, else #6366F1
data-localeOne of en, es, pt, de, fr, hi, ar.Your agent's configured locale, else the browser's language, else English
data-proactiveSeconds to wait before showing a teaser bubble.Off

Arabic renders right-to-left automatically. An unrecognised locale falls back to English rather than failing.

The agent's display name, greeting, and branding come from the session handshake, so changing them in the dashboard changes every embed at once. There are matching data-name, data-message, and data-branding attributes, but they exist for previews and demos — do not use them in production, where they only drift from the dashboard.

JavaScript API

The script defines a global midnightchat() function taking a command name and arguments.

javascript
midnightchat("open");                       // open the panel
midnightchat("close");                      // close it
midnightchat("identify", { /* traits */ }); // see Identity verification
midnightchat("on", "message_sent", (data) => {
  console.log("visitor asked:", data.text);
});

Calls made before the bundle finishes loading are not lost. Queue them and they replay on load:

javascript
window.midnightchat = window.midnightchat || function () {
  // Push a real array, not `arguments` — the widget replays each queued
  // entry with .slice(), which an arguments object does not have.
  (window.midnightchat.q = window.midnightchat.q || []).push([...arguments]);
};
midnightchat("open"); // safe even if widget.js hasn't loaded yet

Events

Subscribe with midnightchat("on", event, handler). An exception thrown inside your handler is swallowed and will not break the widget.

EventPayloadFires when
openYou called midnightchat("open").
closeYou called midnightchat("close").
openedThe visitor opened the panel themselves.
closedThe visitor closed the panel themselves.
message_sent{ text }The visitor sent a message.
message_answeredThe agent finished answering.
message_error{ code }The message failed.
identify_verifiedthe traits you passed, including user_hashAn identify call was accepted.
identify_rejectedthe traits you passed, including user_hashAn identify call was rejected.
lead_submittedThe visitor submitted the lead form.
lead_error{ code }The lead form failed.
The identify events carry your signature
Handlers for identify_verified and identify_rejected receive the traits you passed, which include user_hash. That value is a credential — do not forward these payloads wholesale to analytics or logging. Read the fields you need instead.
open and opened are different events
open and close fire only for your own programmatic calls. opened and closed fire when the visitor clicks. They are not aliases, so analytics that listens only to open will miss every real visitor. Subscribe to both.

Sharing without embedding

Every agent also has a hosted page at /a/<public-key> with the widget already open — useful for testing, for sharing with a colleague, or as a support link when you have nowhere to paste a script tag. Enable it with the share setting on the agent.

Troubleshooting

SymptomCause
Nothing renders, console says missing data-agentThe script tag has no data-agent attribute, or the key was truncated when pasted.
Panel opens, first message fails with origin_not_allowedYour domain is not on the agent's allow-list. Add it, including the exact scheme and port used in development.
Errors mentioning token_origin_mismatchThe session token was issued to a different origin than the one now calling. This usually means the page moved between origins, for example a redirect from apex to www.
Works locally, fails when deployedThe deployed origin is a different one. Allow-list entries accept bare hosts, wildcards such as *.example.com, or full origins.
Identity verification
Prove who a visitor is so the agent can answer account questions.
API reference
The endpoints behind the widget.