The assistant ships as two embeddable widgets sharing the same brain
(POST /api/ask): an inline ask bar that sits in the page like a data
tool, and a floating chat bubble like an Intercom messenger. Both are one-script-tag
installs, isolated in a shadow root so styles never conflict with the host page.
Option A — inline ask bar (embed.js) · recommended for reports
pages. Renders in the page flow, so it reads as a reporting tool and stays away from the
support chat’s corner. Place the div where the bar should appear (on the RMD
reports pages: between the filter row and the first report card). Replace HOST
with the assistant server’s origin.
<!-- where the ask bar should render --> <div id="rmd-reports-ask"></div> <!-- the widget — answers stream from HOST/api/ask --> <script src="https://HOST/embed.js" defer></script>
Option B — floating chat bubble
(embed-chat.js). The launcher-and-panel chat, for pages without a competing
chat widget. No mount div needed — it appends itself to <body>:
<script src="https://HOST/embed-chat.js" defer></script>
data-position="bottom-left". Two bubbles in one corner will overlap.
Either way, that’s the whole install — the widget mounts itself when the page loads.
Defaults work out of the box. Override with data attributes on the script tag:
| Attribute | Applies to | What it does | Default |
|---|---|---|---|
data-api |
both | Base URL of the assistant server (where POST /api/ask lives). |
the script tag’s own origin |
data-target |
ask bar | CSS selector of the element the widget mounts into. | #rmd-reports-ask |
data-position |
chat bubble | Corner for the launcher: bottom-right or bottom-left. |
bottom-right |
For deeper customization — placeholder text or the
suggestion chips — define a global before the script tag
(window.RMDReportsAsk for the ask bar, window.RMDReportsChat for the
chat bubble, which also accepts title and greeting):
<script> window.RMDReportsAsk = { placeholder: "Ask about your referrals…", suggestions: [ "How many referrals did we get this month?", "Who are our top referring providers?" ] }; </script> <script src="https://HOST/embed.js" defer></script>
Data attributes win over the global when both set the same option.
The widget is UI only — questions are answered by the assistant server
(node src/server.js), which runs the agent loop server-side so AWS/Bedrock
credentials and the ReferralMD API key never reach the browser.
data-api origin./api/ask sends CORS headers.RMD_EMBED_ORIGIN (e.g. https://dev.getreferralmd.com) on the server. Default is open (*).Today (demo): the assistant server signs every ReferralMD API call with a
single, hardcoded HMAC key (RMD_API_KEY / RMD_API_SECRET from
.env). Every question — no matter who asks — runs as that one account and sees
that account’s data. Fine for a demo; not acceptable in production, where answers must
respect the signed-in user’s organization, locations, and permissions.
Production: forward the user’s own session. The widget lives on a page
where the user is already signed in to ReferralMD, so their referralMD session
cookie and X-XSRF-TOKEN already exist in the browser. The plan is to ride that
session instead of the shared key:
browser (signed-in RMD user) │ fetch("/assistant/api/ask", { credentials: "include" }) │ → the user's own referralMD cookie + XSRF token ride along ▼ assistant server (same domain, or proxied under /assistant/*) │ reads Cookie + x-xsrf-token off the INCOMING request │ runs the agent loop with that per-request session ▼ ReferralMD API — the query executes as the signed-in user, with their org scoping and permissions. No shared key involved.
/assistant/* to it) so the session cookie is sent automatically. The
auth layer (src/api/auth.js) already speaks cookie + XSRF — it’s the same
header pair a browser session uses./api/ask request) instead. Conversation history
should be keyed per user, not per browser tab.credentials: "include" and the server echoes the exact page origin in
Access-Control-Allow-Origin (set RMD_EMBED_ORIGIN — the
* default blocks credentialed requests) plus
Access-Control-Allow-Credentials: true. Same-domain hosting avoids all of this
and is the recommended shape.window.RMDReportsAsk; the
assistant server verifies it and exchanges it for a scoped API session. More moving parts —
use only if the cookie path is ruled out.data-position="bottom-left"). Don’t mount either widget
inside the support chat’s container.