Widget Troubleshooting

Ordered roughly by how often each turns out to be the cause.

The widget does not appear at all

Check your Content Security Policy

The most common cause. Open your browser console and look for Refused to load or
Refused to frame.

Production:

script-src  https://embed.blueprint.ai;
frame-src   https://clinician.blueprint.ai https://mini-widget.blueprint.ai;

Sandbox:

script-src  https://embed.staging.blueprint.ai;
frame-src   https://clinician.staging.blueprint.ai https://mini-widget.staging.blueprint.ai;

Include both frame-src hosts even if you only expect to use one — which host is used depends on
your settings, and it changed for most partners in early 2026.

If you enforce style-src, the loader's injected stylesheet is blocked and the widget mounts but
renders unstyled or invisible. Pass a nonce:

window.blueprintSettings = {
  containerId: 'blueprint-container',
  cspNonce: 'YOUR_PER_REQUEST_NONCE'
}

The nonce must match the one in your CSP header for that response.

Check your script ordering (single-page applications)

The second most common cause, and it is silent — no console error.

The loader reads window.blueprintSettings once when it executes, then mounts on
DOMContentLoaded. Two consequences:

  • window.blueprintSettings must be assigned before the loader script tag. Assigning it
    afterward has no effect.
  • If the loader is injected after DOMContentLoaded has already fired, the widget never
    mounts.
    This is the normal case in React, Vue, and Angular, where a component that appends a
    script tag runs long after that event.

Put both script tags in your static HTML shell — index.html, your Rails/Django layout, your Next.js
_document — not in a component:

<!-- index.html -->
<head>
  <script>
    window.blueprintSettings = {
      containerId: 'blueprint-container',
      isMinifiedView: true
    }
  </script>
  <script src="https://embed.blueprint.ai/index.min.js"></script>
</head>

Then render <div id="blueprint-container"> from your component as usual. The loader watches the DOM
and mounts the iframe when the container appears, so the container does not need to exist at load
time — and if your app unmounts and re-renders it, the loader re-mounts.

There is no re-initialization API. If you need different settings for different parts of your
application, that is not currently supported.

Check the container element

If containerId is set, an element with that exact ID must eventually exist. If it never appears,
nothing mounts and there is no error. If containerId is omitted, you get floating mode instead of
an inline widget — a frequent surprise when the ID is misspelled.

The widget appears but the clinician is not signed in

You should see BP_CLINICIAN_NOT_AUTHENTICATED in the console or in the message events the widget
emits. Work through these in order:

1. Check the token field names. By far the most common cause. The response from
POST /v2/clinicians/{clinicianId}/authenticate uses PascalCase:

{ "AccessToken": "...", "IdToken": "...", "RefreshToken": "...", "ExpiresIn": 600 }

Pass that object through unchanged. If your backend normalizes API responses to camelCase — many
do, automatically — the widget receives accessToken and cannot read it. Exempt this endpoint from
your normalizer.

Note the API Reference page for this endpoint currently shows camelCase field names. That reference
is wrong; PascalCase is correct.

2. Check you passed one argument. Blueprint.authenticate(tokens) takes a single argument. A
second argument is silently ignored, so older sample code that passes a clinician ID as well still
works — but do not add one expecting it to matter.

3. Check the token has not expired. Clinician access tokens live 10 minutes. If you mint one
during a page render and the clinician does not start recording for fifteen minutes, the initial token
is stale. That is normally fine — the widget refreshes itself using the refresh token — but only if you
passed RefreshToken too. Passing only AccessToken produces a widget that works briefly and then
stops.

4. Check for third-party cookie blocking. The widget prefers a cookie on its own origin and falls
back to in-memory storage when cookies are blocked, so this usually works — but blocked cookies mean
the session does not survive an iframe reload. If auth is lost specifically when the widget reloads,
this is why. Testing in Safari or in a Chrome profile with third-party cookies disabled will reproduce
it.

5. Check the clinician belongs to you. A clinician ID from another partner organization, or from
the other environment, returns 404 from the authenticate call. Sandbox and production IDs are not
interchangeable.

The microphone does not work

The widget requests microphone access from inside its iframe, which the loader grants via
allow="microphone; ...".

If your page is itself inside an iframe, that nesting must also permit microphone access. Browsers
require the permission to be delegated at every level, so the outer frame needs:

<iframe src="your-app" allow="microphone; display-capture; clipboard-read; clipboard-write; screen-wake-lock;"></iframe>

Without it, capture fails with a permission error that is hard to trace, because the failing frame is
two levels down.

Also check:

  • The page is served over HTTPS. Browsers only grant microphone access on secure origins
    (localhost is exempt).
  • The clinician has not previously denied permission. A remembered denial is not re-prompted; it
    has to be cleared in site settings.
  • Permissions-Policy on your own response is not restricting microphone.

A setting has no effect

Four settings apply only to the full widget and are silently ignored on the compact widget, which is
the default:

  • copyNoteButtonText
  • patientReference
  • hideTreatmentPlan
  • hidePreviousSessions

To use them, leave isMinifiedView unset. See Widget Reference for the full
support matrix.

Also worth checking: width and height are not applied to the minimized and expanded states, which
use fixed sizes. And fontHref and fontFamily only work when both are supplied.

A callback never fires

Several callbacks only exist on the full widget:

  • onCopyNoteClicked
  • onDeleteNoteClicked
  • onDeleteSessionClicked

If you are on the compact widget — the default — use onNoteGenerated and fetch the note by API
instead. See Controlling the UI.

onGenerateNoteClicked does fire on the compact widget, but its progressNoteId is always an empty
string because the note does not exist yet. Use onNoteGenerated.

selectClient seems to do nothing

selectClient calls made before the widget signals readiness are queued and replayed automatically,
so calling it early is fine. Two things to check:

  • A two-minute limit applies. If the widget never becomes ready within two minutes — usually a CSP
    or auth problem above — the queued call is abandoned. Fix the underlying issue.
  • The ID must be Blueprint's UUID, not your own identifier. If you have your own, resolve it
    first: GET /v2/clinicians/{clinicianId}/clients?externalId=YOUR_ID.

Use onSelectClientComplete to confirm the client actually loaded:

Blueprint.onSelectClientComplete(({ sessionId }) => {
  console.log('client loaded, session', sessionId)
})

Switching clinicians leaves stale state

Call Blueprint.logout() when a clinician logs out of your application, before authenticating anyone
else.

On the compact widget logout() clears loader-side state but does not end the widget's own session.
To fully reset — which you should do when a different clinician signs in on a shared workstation —
call logout() and then remove and re-create the container element so the iframe reloads:

Blueprint.logout()
container.replaceChildren()          // drops the iframe
// re-render your container; the loader re-mounts a fresh iframe

Still stuck

Collect this before contacting support — it covers most of what we would ask for:

  • Browser and version, and whether third-party cookies are enabled
  • Your window.blueprintSettings object
  • Console errors, especially any Refused to messages
  • The Network tab entry for index.min.js and for the bp-widget iframe
  • Whether window.Blueprint exists in the console
  • Whether your page is itself inside an iframe

Did this page help you?