UI + API Integrations

Your application hosts part of the Blueprint web application inside it, so there is nothing for your
users to download or install. Your frontend sends JavaScript messages to the Blueprint UI, and your
backend exchanges data with Blueprint over the API.

The difference from UI Only is that your backend owns the data model.
It creates clinician and client records mirroring your own users and patients, and authenticates
clinicians with Blueprint on their behalf — so clinicians never sign up for anything. Blueprint still
captures the audio, and completed documentation comes back to you by webhook.

This is the method most partners choose.

The flow

1. Store your credentials. Blueprint provisions a clientId, clientSecret, and apiKey for
your partner organization. These belong in your backend's secret manager and must never reach
frontend code — the clientSecret also signs your webhooks.

2. Authenticate your backend. POST /v2/partners/authenticate returns a server-to-server access
token
, valid for one hour. Cache it; do not re-authenticate per request.

3. Mirror your organizations and clinics. Create an organization per customer and at least one
clinic within it.

4. Create clinician records for the users of your application, with your own user ID as
externalId.

5. Create client records for your patients, with your MRN or row ID as externalId.

6. Mint a clinician token before the clinician can record. POST /v2/clinicians/{clinicianId}/authenticate — no request body — returns a token pair scoped to that one
clinician.

7. Pass the tokens to the embedded UI from your frontend.

8. Select the current client so the recording attaches to the right chart.

9. The clinician records. Blueprint captures the audio and uploads it directly — it never touches
your servers.

10. Receive a webhook when documentation is ready. The payload carries identifiers and a URL;
your backend fetches the artifact and stores it.

See Quickstart for this sequence as runnable curl.

Clinician tokens

This is the part that most often goes wrong, so it is worth being precise.

curl -X POST https://api.blueprint.ai/v2/clinicians/{clinicianId}/authenticate \
  -H 'Access-Token: YOUR_PARTNER_ACCESS_TOKEN' \
  -H 'X-API-Key: YOUR_API_KEY'
{
  "AccessToken": "very-long-alphanumeric-string",
  "IdToken": "very-long-alphanumeric-string",
  "RefreshToken": "very-long-alphanumeric-string",
  "ExpiresIn": 600
}

Pass this object to the widget unchanged. The field names are PascalCase and the widget reads
them exactly as spelled. Rebuilding the object with camelCase keys is the single most common reason
an otherwise-correct integration fails to authenticate.

// your frontend, after fetching the tokens from your own backend
Blueprint.authenticate(clinicianTokens)
Blueprint.selectClient(blueprintClientId)

Blueprint.authenticate() takes one argument.

The widget manages its own refresh

The access token is valid for 10 minutes — shorter than many sessions. You do not need to run
a refresh loop: once the widget has the token pair it renews the access token itself, in the browser,
for the life of the refresh token (about 7 days).

That means the refresh token has to reach the browser. Treat it as equivalent to the clinician's
own session in your application:

  • Deliver it over HTTPS from your backend to your authenticated frontend, and nowhere else.
  • Do not persist it in localStorage or anywhere it outlives the clinician's session in your app.
  • Mint a fresh pair per clinician session rather than reusing one. Refresh tokens rotate on use, and
    a replayed token revokes every refresh token for that clinician as a reuse-detection measure —
    so sharing one pair across tabs or sessions will lock the clinician out.
  • Call Blueprint.logout() when the clinician logs out of your application, and stop passing their
    tokens.

Getting documentation back

Register a callback URL once with PATCH /v2/partners/{partnerId}, then handle events. All event
types go to the same URL.

The events you will care about most:

EventMeaning
progress_note_generatedA note is ready. Fetch payload.progressNoteUrl.
progress_note_regeneratedThe note was regenerated — replace what you stored.
progress_note_finalizedThe clinician finalized the note. It is now locked against edits.
transcript_readyThe transcript is available at payload.transcriptUrl.
session_transcript_errorTranscription failed. payload.error has the reason.

Every payload includes sessionExternalId, echoed back from selectClient, so you can attribute a
note to the appointment it came from without a lookup.

Verify the X-Blueprint-Signature header before trusting any event. See
Listening to Webhooks for a correct implementation and
Webhook Events Reference for all seven event types.

If you would rather not run a webhook endpoint

You can drive it from the frontend instead: onNoteGenerated fires with a sessionId, and your
backend fetches GET /v2/sessions/{sessionId}/progress-note.

Blueprint.onNoteGenerated(async ({ sessionId }) => {
  await myBackend.importBlueprintNote(sessionId)
})

This is simpler to stand up but less reliable — it only works while the clinician's browser tab is
open. Notes finished after they navigate away are missed. Use webhooks for anything load-bearing.

What you can also do from the API

Beyond the core flow, your backend can drive most of the clinician experience:

  • Note preferences per clinician — length, direct quotes, mental status exam components:
    GET/POST /v2/clinicians/{clinicianId}/note-preferences
  • Organization and clinic lookup by ID: GET /v2/organizations/{organizationId} and
    GET /v2/clinics/{clinicId}
  • Available note types for an organization:
    GET /v2/organizations/{organizationId}/progress-note-types
  • Edit a note section by section: PATCH /v2/progress-notes/{progressNoteId}
  • Magic edit with natural-language instructions:
    POST /v2/progress-notes/{progressNoteId}/magic-edit
  • Regenerate in a different format: POST /v2/progress-notes/{progressNoteId}/regenerate
  • Assessments — assign, administer, or submit answers you collected yourself
  • Client lifecycle — transfer between clinicians, archive, unarchive, secondary clinicians

Next


Did this page help you?