Controlling the UI

Once the loader script is on the page it creates a global Blueprint object. Use it to authenticate a
clinician, select a client, and respond to what the clinician does.

All of it is available as window.Blueprint. See
Customizing the Experience for installation.

Authenticating a clinician

For the embedded UI to act on a clinician's behalf it needs an access token for that clinician. Your
backend mints one without the clinician's password by calling
POST /v2/clinicians/{clinicianId}/authenticate with the internal identifier of a clinician your
partner organization is authorized for.

Return the response to your frontend unchanged and pass it to the widget:

Blueprint.authenticate(accessTokens)

authenticate takes one argument — the whole token object as returned by the API:

{
  "AccessToken": "very-long-alphanumeric-string",
  "IdToken": "very-long-alphanumeric-string",
  "RefreshToken": "very-long-alphanumeric-string",
  "ExpiresIn": 600
}

The field names are PascalCase and the widget reads them exactly as spelled. Do not rebuild the
object with camelCase keys — this is the most common cause of a widget that silently fails to
authenticate. See Authentication.

You do not need to refresh the token. The widget renews it itself for the life of the refresh token.

Logging out

Blueprint.logout()

Clears the tokens and the selected client held by the loader.

On the compact widget this clears loader-side state but does not end the widget's own session —
its cookies and in-memory tokens survive. To fully reset it, call logout() and then remove and
re-create the container element so the iframe reloads. This is a known gap.

Selecting a client

Once a clinician is authenticated you can select a client, and the clinician will be prompted to start
recording:

Blueprint.selectClient(clientId)

clientId is Blueprint's internal identifier for the client. If you only have your own identifier,
look it up first with GET /v2/clinicians/{clinicianId}/clients?externalId=YOUR_ID.

You can call selectClient before the widget has finished loading — the call is queued and replayed
once it is ready. If the widget does not become ready within two minutes the call is abandoned.

Session and note defaults

Optionally pass configuration that will be pre-selected for the session:

Blueprint.selectClient(clientId, {
  sessionExternalId: 'appt-99031',
  sessionSetting: 'in-person',        // 'in-person' or 'telehealth'
  usingHeadphones: true,
  noteOptions: {
    sessionType: 'individual',        // 'individual', 'couple', or 'group'
    noteType: 'birp',
    availableNoteTypes: ['birp', 'soap'],
    treatmentApproaches: ['cbt', 'dbt'],
    noteGroup: 'therapists'          // 'therapists' or 'prescribers'
  }
})
OptionNotes
sessionExternalIdYour appointment ID. Echoed back in webhook payloads, so you can attribute a note to an appointment without a lookup.
sessionSetting'in-person' or 'telehealth'
usingHeadphonesAffects how Blueprint captures audio
noteOptions.sessionType'individual', 'couple', or 'group'
noteOptions.noteTypeThe note format pre-selected in the dropdown
noteOptions.availableNoteTypesRestricts the dropdown to these note types
noteOptions.treatmentApproachesPre-selected treatment approaches
noteGroupLimits available note types to 'therapists' or 'prescribers'

Call GET /v2/organizations/{organizationId}/progress-note-types for the note types available to an
organization rather than hardcoding them.

Controlling a session

Blueprint.endSession()      // opens the end-session confirmation
Blueprint.discardSession()  // discards the in-progress recording

Useful when the clinician navigates away from a chart in your application and you need the widget's
state to follow.

Listening to events

Register a callback for each event you care about. Each takes a single function.

Which callbacks fire

The compact widget (isMinifiedView: true, the current default) and the full widget emit different
events. Registering a callback that cannot fire is harmless but silent, so check this before you rely
on one.

CallbackCompactFull
onSelectClientCompleteyes
onSessionRecordingStartedyesyes
onSessionRecordingPauseyes
onSessionRecordingResumeyes
onSessionRecordingEndyes
onGenerateNoteClickedyesyes
onNoteGeneratedyes
onCopyNoteClickednoyes
onDeleteNoteClickednoyes
onDeleteSessionClickednoyes

onSelectClientComplete

The client you selected is loaded and the widget is ready to record.

Blueprint.onSelectClientComplete(({ sessionId }) => {
  console.log('Ready to record for session', sessionId)
})

onSessionRecordingStarted

The clinician started recording.

Blueprint.onSessionRecordingStarted(({ sessionId }) => {
  console.log('Recording started:', sessionId)
})

onSessionRecordingPause / onSessionRecordingResume / onSessionRecordingEnd

Blueprint.onSessionRecordingPause(({ sessionId })  => setPaused(true))
Blueprint.onSessionRecordingResume(({ sessionId }) => setPaused(false))
Blueprint.onSessionRecordingEnd(({ sessionId })    => setRecording(false))

Use these to disable conflicting UI in your application while a recording is live — for example
preventing the clinician from navigating away mid-session.

onGenerateNoteClicked

The clinician ended the session to generate a note, or regenerated one using Magic Edit.

Blueprint.onGenerateNoteClicked(({ sessionId, progressNoteId, sessionType, noteType, treatmentApproaches }) => {
  console.log('Generating note for', sessionId, noteType)
})

On the compact widget progressNoteId is always an empty string — the note does not exist yet. Use
onNoteGenerated or the progress_note_generated webhook to get the real ID.

onNoteGenerated

The note has been generated and is available by API. This is the recommended way to pull a note into
your application from the frontend.

Blueprint.onNoteGenerated(async ({ sessionId }) => {
  const note = await myBackend.fetchBlueprintNote(sessionId)
  insertIntoChart(note)
})

Your backend then calls GET /v2/sessions/{sessionId}/progress-note with partner credentials. The
payload only carries sessionId — the note text is deliberately not passed through the browser.

For anything load-bearing, prefer the progress_note_generated webhook: it still arrives if the
clinician closes the tab. See Listening to Webhooks.

onCopyNoteClicked

Full widget only. The clinician clicked the copy button after documentation was generated. The
callback receives the note text directly.

Blueprint.onCopyNoteClicked(({ sessionId, note }) => {
  document.getElementById('chart-note').value = note
})

Note the payload is an object with sessionId and note — not the note string itself.

The button's label can be changed with the copyNoteButtonText setting. Both the callback and the
setting require the full widget: leave isMinifiedView unset. See
UI Only Integrations.

onDeleteSessionClicked / onDeleteNoteClicked

Full widget only. The clinician deleted a session or a note — use these to keep your own records in
sync.

Blueprint.onDeleteSessionClicked(({ sessionId }) => removeSession(sessionId))
Blueprint.onDeleteNoteClicked(({ sessionId, progressNoteId }) => removeNote(progressNoteId))

A complete example

<script>
  window.blueprintSettings = {
    containerId: 'blueprint-container',
    isMinifiedView: true,
    width: '250px',
    height: '220px'
  }
</script>
<script src="https://embed.blueprint.ai/index.min.js"></script>

<div id="blueprint-container"></div>

<script>
  // tokens fetched from your own backend
  const tokens = await fetch('/api/blueprint/clinician-token').then(r => r.json())

  Blueprint.authenticate(tokens)

  Blueprint.selectClient(blueprintClientId, {
    sessionExternalId: currentAppointmentId,
    sessionSetting: 'telehealth',
    noteOptions: { sessionType: 'individual', noteType: 'soap' }
  })

  Blueprint.onSessionRecordingStarted(() => lockNavigation())
  Blueprint.onSessionRecordingEnd(()     => unlockNavigation())

  Blueprint.onNoteGenerated(async ({ sessionId }) => {
    const note = await fetch(`/api/blueprint/note/${sessionId}`).then(r => r.json())
    renderNote(note)
  })
</script>

Next


Did this page help you?