Skip to content

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:

text
https://martha.nomadriver.co/embed/v1/

The default web component install is:

html
<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 framework
  • martha-chat.es.js - ESM bundle with the imperative mountMarthaChat API
  • martha-chat.react.js - React wrapper
  • martha-chat.svelte.js - Svelte wrapper
  • martha-chat.css - shared styles

The npm package is:

bash
npm install @aiaiai-pt/martha-chat

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

  1. Reads the embed manifest from GET /api/embed/manifest/{client_key}.
  2. Applies the configured display mode, feature flags, and theme defaults.
  3. Gets a Martha-compatible access token from the host page.
  4. Sends chat traffic to Martha APIs with embed request markers.
  5. 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.

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

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

  1. Enable embeds for the Client.
  2. Add each allowed host origin.
  3. Choose the default display mode.
  4. Enable only the features that host should receive.
  5. Configure theme defaults if the host should inherit Martha-side branding.
  6. For embed-token mode, provision backend credentials and store the secret in the host backend.
  7. Copy the generated snippet into the host app.

Allowed origins are exact origins only:

text
https://partner.example.com
https://app.partner.example.com
http://localhost:5173

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

bash
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=#155eef

For an existing Client:

bash
martha clients embed configure partner-support \
  --enable \
  --add-origin https://app.partner.example.com \
  --mode launcher \
  --feature uploads=true \
  --feature history=false

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

bash
# 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.com

For local or CI verification, the smoke command can create and clean up a temporary embed Client:

bash
martha clients embed smoke \
  --create-temp-client \
  --origin http://127.0.0.1:5188

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

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

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

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

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

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

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

bash
martha clients embed snippet partner-support --framework react --auth host-token

Svelte

Svelte and SvelteKit hosts should use the npm package.

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

bash
martha clients embed snippet partner-support --framework svelte --auth host-token

Imperative Mount API

Use the npm package's ESM entry when a host needs to mount and unmount the widget manually.

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

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

bash
martha clients embed snippet partner-support --framework esm --auth embed-token

Feature Flags

Feature flags are configured on the Martha Client and returned by the manifest.

  • uploads enables scoped file uploads into the current embed chat session.
  • history controls whether the embedded surface may show prior session history.
  • feedback controls message feedback affordances.
  • voice controls 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.stylesheetUrl or the stylesheet-url attribute:
ts
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:

css
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

  1. Start Martha and the SLO PWA locally.
  2. Create or update a Client in Admin.
  3. Enable embeds for that Client.
  4. Add the host origin, for example http://localhost:5173.
  5. If testing embed-token mode, provision embed credentials from Admin or martha clients embed credentials provision.
  6. Copy the web component snippet into a local host page.
  7. Verify GET /api/embed/manifest/{client_key} succeeds with the host page's Origin header.
  8. Open the widget, send a chat message, refresh, and confirm the expected session behavior.
  9. 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:

  1. Confirm https://martha.nomadriver.co/embed/v1/martha-chat.ce.js returns HTTP 200.
  2. Confirm MARTHA_EMBED_TOKEN_SECRET is present in the deployed backend environment if embed-token mode will be used.
  3. Configure the Client Embed tab in Admin.
  4. Add the exact production host origin.
  5. Provision embed-token backend credentials if the host does not use host-token mode.
  6. Store MARTHA_CLIENT_ID, MARTHA_CLIENT_SECRET, and the token endpoint in the host backend secret store.
  7. Smoke test the manifest from that origin.
  8. Smoke test the selected auth mode from the real host page.
  9. 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-token mode, backend-only credential names and token endpoint
  • Expected feature flags

Martha is built by aiaiai-pt.