Embeddable Martha Chat
Martha ships an embeddable chat client that third-party applications can use as a normal Martha chat surface. The v1 distribution model has two first-class install paths:
- Hosted static bundle from the canonical Martha app host for plain HTML, CMS, and no-build pages.
- Public npm package for React, Svelte, and modern JavaScript apps that want typed imports, version pinning, and normal dependency review.
What Is Published
Hosted assets are served from:
https://martha.nomadriver.co/embed/v1/The default web component install is:
<script type="module" src="https://martha.nomadriver.co/embed/v1/martha-chat.ce.js"></script>The v1 assets are:
martha-chat.ce.js- web component bundle for plain HTML and any host frameworkmartha-chat.es.js- ESM bundle with the imperativemountMarthaChatAPImartha-chat.react.js- React wrappermartha-chat.svelte.js- Svelte wrappermartha-chat.css- shared styles
The npm package is:
npm install @aiaiai-pt/martha-chatPackage exports:
@aiaiai-pt/martha-chat- imperative mount API, client helpers, and shared types@aiaiai-pt/martha-chat/web-component- custom element entrypoint@aiaiai-pt/martha-chat/react- React wrapper@aiaiai-pt/martha-chat/svelte- Svelte component exports@aiaiai-pt/martha-chat/css- shared styles
Use the hosted bundle when the host page does not have a build step. Use the npm package when a framework app wants autocomplete, TypeScript declarations, semver pinning, and Renovate/Dependabot support.
How It Works
The host page loads the widget through either the hosted bundle or the npm package, then passes a client-key and an auth callback.
At runtime the widget:
- Reads the embed manifest from
GET /api/embed/manifest/{client_key}. - Applies the configured display mode, feature flags, and theme defaults.
- Gets a Martha-compatible access token from the host page.
- Sends chat traffic to Martha APIs with embed request markers.
- Lets Martha enforce the configured Client, origin allowlist, auth mode, and feature permissions.
The host application does not get admin-level Martha permissions. It only gets the chat capabilities enabled for the configured Client and origin.
Supported V1 Modes
Display Modes
Use launcher mode when the widget should behave like the usual bottom-corner support chat.
<martha-chat
api-url="https://martha.nomadriver.co"
client-key="your-client-key"
launcher
placement="bottom-right"
></martha-chat>Use inline mode when Martha chat should occupy a specific region of the host application.
<section style="height: 720px">
<martha-chat
api-url="https://martha.nomadriver.co"
client-key="your-client-key"
></martha-chat>
</section>Auth Modes
Use host-token when the host app already signs users in with Martha or Keycloak and can provide a valid Martha-compatible user token.
Use embed-token when the host app has its own authentication system. In this mode, the host backend authenticates its own user, calls Martha server-to-server, and returns a short-lived embed token to the browser.
Standalone widget PKCE login is not a supported v1 mode. Do not advertise the embeddable widget as able to open its own Keycloak login flow in arbitrary third-party pages yet.
Admin Setup
Configure embeds from Martha Admin under the Client's Embed tab.
Required setup:
- Enable embeds for the Client.
- Add each allowed host origin.
- Choose the default display mode.
- Enable only the features that host should receive.
- Configure theme defaults if the host should inherit Martha-side branding.
- For
embed-tokenmode, provision backend credentials and store the secret in the host backend. - Copy the generated snippet into the host app.
Allowed origins are exact origins only:
https://partner.example.com
https://app.partner.example.com
http://localhost:5173Do not include paths, query strings, fragments, or wildcard domains. For local development, Martha must be configured to allow localhost embed origins.
You can do the same setup from martha-cli:
martha clients embed create \
--name "Partner Support" \
--client-key partner-support \
--enable \
--origin https://partner.example.com \
--mode launcher \
--enable-feedback \
--provision-credentials \
--theme accent=#155eefFor an existing Client:
martha clients embed configure partner-support \
--enable \
--add-origin https://app.partner.example.com \
--mode launcher \
--feature uploads=true \
--feature history=falseThe public client-key is the value a host page passes to the widget. keycloak_client_id is a deprecated compatibility column and should not be used in new embed instructions.
Useful operator commands:
# Show stored embed configuration.
martha clients embed show partner-support
# Verify that an origin can read the public manifest.
martha clients embed show partner-support \
--manifest \
--origin https://partner.example.com
# Print hosted asset URLs.
martha clients embed urls
# Print a copy-paste install snippet.
martha clients embed snippet partner-support \
--framework html \
--auth embed-token \
--mode launcher
# Provision backend credentials for embed-token mode.
martha clients embed credentials provision partner-support
# Rotate the backend secret when needed.
martha clients embed credentials rotate partner-support
# Revoke embed-token credentials.
martha clients embed credentials revoke partner-support
# Run manifest, denied-origin, and hosted-bundle checks.
martha clients embed smoke partner-support \
--origin https://partner.example.comFor local or CI verification, the smoke command can create and clean up a temporary embed Client:
martha clients embed smoke \
--create-temp-client \
--origin http://127.0.0.1:5188Embed token minting is only for trusted backend/test flows. The command requires the Client's dedicated embed credential service account or a Martha super-admin token. The CLI credential provisioning command prints:
MARTHA_CLIENT_ID=...
MARTHA_CLIENT_SECRET=...
Token endpoint: ...Store these values on the host backend. Then the host backend can obtain a Martha service-account token and mint short-lived embed tokens:
export MARTHA_CLIENT_ID=...
export MARTHA_CLIENT_SECRET=...
martha clients embed token partner-support \
--origin https://partner.example.com \
--external-user-id user-123 \
--display-name "Jane Partner"Human output hides the token by default. Use --print-token or --json only in automation that handles secrets safely.
Quick Start: Plain HTML With Host Token
This is the simplest integration when the host already has a Martha-compatible user token.
<martha-chat
api-url="https://martha.nomadriver.co"
client-key="your-client-key"
launcher
placement="bottom-right"
></martha-chat>
<script>
window.MarthaChat = {
getAccessToken: async () => window.hostAuth.getMarthaAccessToken()
};
</script>
<script type="module" src="https://martha.nomadriver.co/embed/v1/martha-chat.ce.js"></script>Quick Start: Plain HTML With Embed Token
Use this mode when the host app has its own login system.
Browser code:
<martha-chat
api-url="https://martha.nomadriver.co"
client-key="your-client-key"
launcher
></martha-chat>
<script>
window.MarthaChat = {
getAccessToken: async () => {
const response = await fetch("/api/martha-embed-token", {
method: "POST",
credentials: "include"
});
if (!response.ok) {
throw new Error("Could not create Martha embed token");
}
const payload = await response.json();
return payload.access_token;
}
};
</script>
<script type="module" src="https://martha.nomadriver.co/embed/v1/martha-chat.ce.js"></script>Host backend example:
async function getMarthaServiceToken() {
const response = await fetch(process.env.MARTHA_TOKEN_ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.MARTHA_CLIENT_ID,
client_secret: process.env.MARTHA_CLIENT_SECRET
})
});
if (!response.ok) {
throw new Error("Could not authenticate with Martha");
}
return (await response.json()).access_token;
}
app.post("/api/martha-embed-token", requireUser, async (req, res) => {
const serviceToken = await getMarthaServiceToken();
const response = await fetch(`${process.env.MARTHA_URL}/api/embed/tokens`, {
method: "POST",
headers: {
"Authorization": `Bearer ${serviceToken}`,
"Content-Type": "application/json",
"Origin": process.env.PUBLIC_HOST_ORIGIN
},
body: JSON.stringify({
client_key: process.env.MARTHA_EMBED_CLIENT_KEY,
external_user_id: req.user.id,
display_name: req.user.name,
expires_in: 900
})
});
const body = await response.json();
res.status(response.status).json(body);
});Keep MARTHA_CLIENT_SECRET and service-account tokens server-side. Do not expose them to the browser.
React
React hosts should use the npm package.
import "@aiaiai-pt/martha-chat/css";
import { MarthaChat } from "@aiaiai-pt/martha-chat/react";
export function SupportChat() {
return (
<MarthaChat
apiUrl="https://martha.nomadriver.co"
selectedClientId="your-client-key"
launcher
placement="bottom-right"
markEmbedRequests
getAccessToken={async () => window.hostAuth.getMarthaAccessToken()}
/>
);
}React is an optional peer dependency and is only required by the React entrypoint. The base, web component, and Svelte entrypoints do not import React.
If a React host cannot install npm packages, use the hosted web component bundle instead of importing React code from a URL.
Generate this snippet from the CLI:
martha clients embed snippet partner-support --framework react --auth host-tokenSvelte
Svelte and SvelteKit hosts should use the npm package.
<script lang="ts">
import "@aiaiai-pt/martha-chat/css";
import { MarthaChatLauncher } from "@aiaiai-pt/martha-chat/svelte";
async function getAccessToken() {
return window.hostAuth.getMarthaAccessToken();
}
</script>
<MarthaChatLauncher
apiUrl="https://martha.nomadriver.co"
selectedClientId="your-client-key"
placement="bottom-right"
markEmbedRequests
{getAccessToken}
/>Svelte is an optional peer dependency and is only required by the Svelte entrypoint.
If a Svelte host cannot install npm packages, use the hosted web component bundle.
Generate this snippet from the CLI:
martha clients embed snippet partner-support --framework svelte --auth host-tokenImperative Mount API
Use the npm package's ESM entry when a host needs to mount and unmount the widget manually.
import "@aiaiai-pt/martha-chat/css";
import { mountMarthaChat } from "@aiaiai-pt/martha-chat";
const instance = mountMarthaChat(document.getElementById("martha-root"), {
apiUrl: "https://martha.nomadriver.co",
selectedClientId: "your-client-key",
launcher: true,
placement: "bottom-right",
markEmbedRequests: true,
getAccessToken: async () => window.hostAuth.getMarthaAccessToken()
});
// Later, if the host page is being torn down:
// instance.destroy();No-build pages can use the hosted ESM bundle instead:
<div id="martha-root"></div>
<script type="module">
import { mountMarthaChat } from "https://martha.nomadriver.co/embed/v1/martha-chat.es.js";
mountMarthaChat(document.getElementById("martha-root"), {
apiUrl: "https://martha.nomadriver.co",
selectedClientId: "your-client-key",
launcher: true,
getAccessToken: async () => window.hostAuth.getMarthaAccessToken()
});
</script>Generate this snippet from the CLI:
martha clients embed snippet partner-support --framework esm --auth embed-tokenFeature Flags
Feature flags are configured on the Martha Client and returned by the manifest.
uploadsenables scoped file uploads into the current embed chat session.historycontrols whether the embedded surface may show prior session history.feedbackcontrols message feedback affordances.voicecontrols voice UI, subject to the Client's normal voice enablement.
Embed uploads are intentionally scoped. The widget can upload files for the active chat session only. It does not grant the host browser generic document collection read or write access.
Styling
Hosts can style the widget with CSS custom properties. Martha Admin can also store default theme values for a Client, which the manifest returns to the widget.
For package installs:
- React, Svelte, and imperative ESM mounts should import
@aiaiai-pt/martha-chat/css. - Web component installs render inside Shadow DOM. With bundlers such as Vite, import the CSS asset URL and pass it through
window.MarthaChat.stylesheetUrlor thestylesheet-urlattribute:
import stylesheetUrl from "@aiaiai-pt/martha-chat/css?url";
import "@aiaiai-pt/martha-chat/web-component";
window.MarthaChat = {
stylesheetUrl,
getAccessToken: async () => window.hostAuth.getMarthaAccessToken()
};Hosted web component installs do not need this extra CSS URL because martha-chat.ce.js and martha-chat.css are siblings in /embed/v1/.
Common host-side variables:
martha-chat {
--martha-chat-accent: #2563eb;
--martha-chat-bg: #ffffff;
--martha-chat-surface: #f8fafc;
--martha-chat-text: #111827;
--martha-chat-muted: #68707d;
--martha-chat-border: #d6d9df;
--martha-chat-radius: 8px;
--martha-chat-font: Inter, system-ui, sans-serif;
--martha-chat-launcher-size: 56px;
--martha-chat-launcher-width: 390px;
--martha-chat-launcher-height: 640px;
}Use host-side CSS variables for simple visual integration. Use Martha-side theme settings when administrators need centralized per-client defaults across multiple host pages.
Local Testing Checklist
- Start Martha and the SLO PWA locally.
- Create or update a Client in Admin.
- Enable embeds for that Client.
- Add the host origin, for example
http://localhost:5173. - If testing
embed-tokenmode, provision embed credentials from Admin ormartha clients embed credentials provision. - Copy the web component snippet into a local host page.
- Verify
GET /api/embed/manifest/{client_key}succeeds with the host page'sOriginheader. - Open the widget, send a chat message, refresh, and confirm the expected session behavior.
- If uploads are enabled, upload a file from the widget and confirm it is scoped to the embed session.
Production Checklist
Before handing a snippet to a third-party developer:
- Confirm
https://martha.nomadriver.co/embed/v1/martha-chat.ce.jsreturns HTTP 200. - Confirm
MARTHA_EMBED_TOKEN_SECRETis present in the deployed backend environment ifembed-tokenmode will be used. - Configure the Client Embed tab in Admin.
- Add the exact production host origin.
- Provision embed-token backend credentials if the host does not use
host-tokenmode. - Store
MARTHA_CLIENT_ID,MARTHA_CLIENT_SECRET, and the token endpoint in the host backend secret store. - Smoke test the manifest from that origin.
- Smoke test the selected auth mode from the real host page.
- Verify only the intended features are enabled.
The third-party developer should receive:
- Martha host URL
- Client key
- Allowed origin requirements
- Chosen auth mode
- Chosen install path: hosted script tag or
@aiaiai-pt/martha-chat - Snippet for web component, React, Svelte, or imperative mount
- For
embed-tokenmode, backend-only credential names and token endpoint - Expected feature flags