Browser SDK
The Browser SDK exposes window.BiomAPI with no dependencies. Use it to open the Web Widget, retrieve and share BiomPINs, or receive reports through URL fragments.
For your first import button, start with the Web Widget quick start, which includes the SDK setup.
Each feature works independently. The widget provides BiomAPI’s upload and review interface. The SDK can retrieve public BiomPINs, preserve local identity context, and manage optional local history. It does not process files or manage API credentials.
Try the demo for widget import, sharing, and automatic fragment import, with one response viewer and a copyable integration example.
Load once
Section titled “Load once”<script src="https://biomapi.com/static/js/biomapi-sdk.js"></script>Load the script from the BiomAPI instance you use. Its origin determines the widget and default sharing destination. Loading the script starts no requests or listeners and does not access storage.
The /static/v2.2.15/js/biomapi-sdk.js path serves the current release with long-lived caching. Only the deployed version is mounted; older versioned paths are not a permanent archive.
The examples use your application’s buttons and display callbacks. Where response appears, it is a successful StandardAPIResponse with a BiomPIN, returned by your REST request or the widget.
Use one stable, lowercase integration ID for the widget and retrieval helper:
const integrator = { id: 'example-ehr', name: 'Example EHR' };name is required by the widget for display. Retrieval requires only id, but accepts the same object so one definition can be reused.
The biomapi.* ID namespace is reserved by convention for official BiomAPI clients. Integration IDs identify the caller for operational tracking. Callers declare these IDs themselves, so an ID does not authenticate a client.
Widget
Section titled “Widget”Open BiomAPI’s upload and review interface directly from a button click:
button.onclick = async () => { try { const response = await BiomAPI.widget.open({ integrator }); if (response) displayResponse(response); } catch (error) { showError(error.message); }};When the user presses Send, the promise resolves to the reviewed StandardAPIResponse. If the user cancels or closes the popup, it resolves to null. See the Web Widget guide for options, errors, identity choices, and delivery security.
Copy and share
Section titled “Copy and share”currentResponse is the response currently displayed in your application. Update it whenever a new report is shown. Like the demo, this example includes available patient initials/ID in every copied BiomAPI link:
copyPinButton.onclick = () => BiomAPI.biompin.copyPin(currentResponse?.biompin?.pin).then(showCopyResult);copyLinkButton.onclick = () => BiomAPI.biompin.copyUrl({ pin: currentResponse?.biompin?.pin, context: BiomAPI.context.fromResponse(currentResponse)}).then(showCopyResult);To build a BiomAPI link with identity without copying it, pass the response’s identity context explicitly:
const link = BiomAPI.biompin.buildUrl({ pin: currentResponse.biompin.pin, context: BiomAPI.context.fromResponse(currentResponse)});// https://biomapi.com/pin/lunar-rocket-731904#biomctx=...The demo and these examples include identity by default. The SDK includes it only when you pass context. BiomPIN storage excludes patient name and ID, so a PIN alone cannot restore them.
If the widget user chooses Anonymous, or the response has no identifiers, fromResponse() returns null. The link then has no identity context.
For a BiomAPI link, omit targetUrl. For an integrator link, supply a URL where your application can read fragments, such as targetUrl: 'https://calculator.example/import'.
To include identity, pass the same context. To share without initials/ID, omit context or pass null:
| Destination | Without identity | With identity |
|---|---|---|
| BiomAPI | /pin/{pin} |
/pin/{pin}#biomctx=... |
| Integrator | /import#biompin={pin} |
/import#biompin={pin}&biomctx=... |
Generated URLs are absolute. To build one without copying it, use BiomAPI.biompin.buildUrl({ pin, targetUrl, context }). Missing identifiers produce a link without context; sharing never searches history or saves an entry.
Copy behavior
Section titled “Copy behavior”biompin.copyPin() and biompin.copyUrl() return Promise<boolean>: false means invalid input or clipboard failure. Display the outcome in your callback, such as showCopyResult.
Call copy helpers directly from a user action, before awaiting other work. They try the Clipboard API first, then fall back to copying through the DOM. Disable PIN/link copying when no PIN is available.
The demo has no identity checkbox. Both link actions include available identifiers from the current response. Your application can offer a choice: pass the current response’s context to include identity, or null to omit it. Never reuse context from a previous report or search history implicitly.
URL validation
Section titled “URL validation”buildUrl() throws an error if you supply:
- An invalid PIN or a destination that does not use HTTP(S).
- A URL with credentials or existing BiomPIN/context query parameters.
- An incompatible anchor or hash-router fragment.
It preserves ordinary query parameters and unrelated key=value fragment fields. It replaces existing BiomAPI fragment fields, so an anonymous link cannot retain old identity context.
Consume fragments
Section titled “Consume fragments”Use these helpers in the application that receives integrator links. The SDK reads and removes #biompin and optional biomctx. Its retrieval helper can then load the report and identify your integration. This does not open a widget or save history automatically.
For one-time consumption:
const handoff = BiomAPI.fragments.consume();// { pin: 'lunar-rocket-731904', context: { patientName: 'SD', patientId: 'SYN-001' } }// Or null if no valid PIN was supplied; context itself can be null.For automatic imports on page load and later fragment changes, register a listener. This example retrieves the report, restores missing identifiers locally, and prevents older requests from replacing a newer result:
let generation = 0;const stopWatching = BiomAPI.fragments.watch(async ({ pin, context }) => { const request = ++generation; showLoading(); try { // Only the PIN and integration attribution are sent. Context stays local. const response = await BiomAPI.biompin.retrieve(pin, { integrator }); if (request !== generation) return; displayResponse(BiomAPI.context.apply(pin, response, context)); } catch (error) { if (request === generation) showError(error.message); }});// Call stopWatching() when the component is disposed.Retrieval behavior
Section titled “Retrieval behavior”BiomAPI.biompin.retrieve(pin, { integrator, signal }) validates and normalizes the PIN and integration ID. It calls the public retrieval endpoint on the BiomAPI instance that supplied the SDK and returns a StandardAPIResponse.
The helper checks for patient data and a matching PIN. It does not validate every clinical field against the schema.
The helper sends X-BiomAPI-Integrator-ID. For HTTP(S) or extension pages, it also sends X-BiomAPI-Integrator-Origin from the calling page. The server retains only the origin’s hostname.
These values identify the integration in analytics. Callers can falsify them. They never control authentication, authorization, billing, or quotas.
The custom headers may require a CORS preflight for cross-origin retrieval. BiomAPI advertises a one-week preflight cache duration, but browsers may use a shorter limit.
Retrieval uses cache: 'no-store', omits cookies and referrer data, and never sends fragment identity context. It uses the public retrieval quota and does not accept an API key. Integrations requiring authenticated retrieval should call the REST endpoint directly and include the same integration headers.
HTTP errors and invalid responses reject with BiomAPI.biompin.BiomAPIRetrieveError, which exposes status, requestId, and details. Invalid arguments throw TypeError. Network and abort errors keep the browser’s native error type. To support cancellation, pass an AbortSignal as signal.
Fragment behavior
Section titled “Fragment behavior”consume() reads only parameter-style fragments. It does not read PINs from query strings or paths. It normalizes the PIN and removes all biompin/biomctx fields with history.replaceState().
Unrelated fields, the query string, and existing navigation state remain. Cleanup happens before your callback. It does not navigate or add a browser history entry.
Malformed or duplicate PINs return null. Malformed, duplicate, or unsupported-version context becomes null without blocking a valid PIN. Unrelated anchors and hash routes remain unchanged.
Retrieval failures do not restore the fragment. To offer Retry, keep the consumed PIN and context in your component. Otherwise, reopen the link.
watch() handles the current fragment immediately, then handles later hashchange events. Your callback must handle asynchronous errors. The SDK does not patch navigation APIs.
After your own pushState() or replaceState(), call consume() explicitly. Those APIs do not emit hashchange.
Identity context
Section titled “Identity context”Identity context carries patient initials/name and ID separately from the retrieved report. Use fromResponse() when sharing and apply() after retrieval. These helpers never read history or send context to the server.
context.fromResponse(response): returns{ patientName, patientId }or null.context.encode(context): returns the base64url context string, or null when empty.context.decode(encoded): returns{ patientName, patientId }or null for malformed or unsupported context.context.decodeFromLocation(location = window.location): reads context without modifying the URL.context.apply(pin, response, context = null): fills missing patient name/ID from the supplied context. Returns a copy when identifiers change. It does not change the input, overwrite existing identifiers, or search history. Invalid PINs and responses with a different BiomPIN remain unchanged.
The version-1 format is UTF-8 JSON encoded as base64url: { v: 1, patient_name, patient_id }. Only nonempty string identifiers are accepted. Context never changes clinical measurements, provenance, or API schemas.
Advanced: local history
Section titled “Advanced: local history”Integrator links work without history. Add it when users need to find and reopen previously imported PINs in your application. The SDK provides the store and utilities; your code decides when to save and supplies the history UI.
This store is separate from BiomAPI’s history. Both are browser-local, not synced across browsers or devices. Saving a PIN does not extend its expiry or store the clinical response.
Create a store
Section titled “Create a store”const store = BiomAPI.history.create({ storageKey: 'my-app:biomapi-history', maxEntries: 250});try { store.add({ pin: response.biompin.pin, dbId: response.biompin.db_id, expiresAt: response.biompin.expires_at, // Optional: include identifiers only if your app chooses to retain them. patientName: response.data.patient.name, patientId: response.data.patient.id });} catch (error) { showHistoryError(error.message);}Options default to storageKey: 'biomapi_history', maxEntries: 250, and the calling page’s localStorage. Use an app-specific key to keep stores separate. maxEntries must be a positive integer. Custom storage must implement getItem() and setItem().
Creating a store does not access storage. Reads and writes are synchronous. If they fail, they throw BiomAPI.history.HistoryStorageError with code STORAGE_READ_FAILED or STORAGE_WRITE_FAILED.
The store does not overwrite unreadable or incompatible data as if it were empty. Catch errors where your application displays history. Show a failed save or delete as a failure. Invalid arguments throw TypeError or RangeError.
Manage entries
Section titled “Manage entries”| Method | Behavior |
|---|---|
add({ pin, dbId, expiresAt, patientName, patientId }) |
Adds or refreshes an entry, moves it to the front, deduplicates the PIN, enforces capacity, and returns the entry. |
get(pin) |
Returns one entry or null. |
list() |
Returns stored entries, newest first, without pruning. |
search(query) |
Searches initials/name, patient ID, or PIN case-insensitively; an empty query returns all entries. |
pruneExpired() |
Removes expired entries and returns the remaining entries. |
pruneDbIdMismatch(dbId) |
Removes entries from other databases, including entries with no stored ID, and returns the remainder. |
clearOne(pin) |
Removes one PIN and returns the remaining entries. |
clearAll() |
Clears this store and returns an empty array. |
Two utilities work directly on entries without reading storage:
BiomAPI.history.isExpired(entry)checks expiry.BiomAPI.history.hasDbIdMismatch(entry, dbId)compares database IDs.
Both return false for a missing entry. An entry with no stored database ID mismatches a known ID. A missing or unparseable expiry also returns false. This does not guarantee that the PIN can be retrieved. If you have a PIN, get its entry with store.get(pin) first.
const entries = store.list();const matches = store.search('SYN-123');const expired = BiomAPI.history.isExpired(store.get(pin));
// Your app obtains the current database ID through its own REST request.store.pruneDbIdMismatch(currentDbId);store.pruneExpired();Bind deletion to separate user actions:
deleteButton.onclick = () => store.clearOne(selectedPin);clearHistoryButton.onclick = () => store.clearAll();Entry format and updates
Section titled “Entry format and updates”Entries contain pin, patientName, patientId, expiresAt, dbId, createdAt, and updatedAt. PIN validation and lowercase normalization match sharing and fragments.
Expiry is an ISO timestamp. createdAt and updatedAt record creation and the latest add() in Unix milliseconds. Reads do not change timestamps. Returned entries are snapshots. Call add() to update storage.
When updating an entry, omit a field to keep its value. Pass null or an empty string to clear it. A different dbId starts a fresh record for that PIN without the previous identity or expiry.
Adding an entry removes entries from other databases only if the capacity limit requires it. To remove entries from other databases explicitly, call pruneDbIdMismatch(). These local checks do not verify a PIN with the server.
Identity and origin boundaries
Section titled “Identity and origin boundaries”Local history is not encrypted. Retain only what your application needs, and treat stored PINs and identifiers as sensitive. Loading the SDK from BiomAPI does not give it access to BiomAPI’s history: storage belongs to the integrating page’s origin.
To restore missing identifiers from history, first look up the entry and check its database and expiry. Pass that entry as explicit context to BiomAPI.context.apply(pin, response, entry). The identity-context rules still apply. There is no automatic fallback from incoming context to history.
API reference
Section titled “API reference”BiomAPI.widget
Section titled “BiomAPI.widget”| Action | Signature | Result |
|---|---|---|
| Open the hosted review interface | open({ integrator: { id, name } }) |
`Promise<StandardAPIResponse |
| Identify widget failures | BiomAPIWidgetError |
Error class with code |
| Inspect the messaging protocol | protocolVersion |
Number |
BiomAPI.biompin
Section titled “BiomAPI.biompin”| Action | Signature | Result |
|---|---|---|
| Retrieve with attribution | retrieve(pin, { integrator, signal? }) |
Promise<StandardAPIResponse> |
| Build a BiomAPI or integrator link | buildUrl({ pin, targetUrl?, context? }) |
Absolute URL string |
| Copy a PIN | copyPin(pin) |
Promise<boolean> |
| Build and copy a link | copyUrl({ pin, targetUrl?, context? }) |
Promise<boolean> |
| Identify retrieval failures | BiomAPIRetrieveError |
Error class with status, requestId, and details |
BiomAPI.fragments
Section titled “BiomAPI.fragments”| Action | Signature | Result |
|---|---|---|
| Consume the current handoff | consume() |
{ pin, context } or null |
Consume now and on hashchange |
watch(callback) |
Stop-listening function |
BiomAPI.context
Section titled “BiomAPI.context”| Action | Signature | Result |
|---|---|---|
| Read identifiers from a response | fromResponse(response) |
{ patientName, patientId } or null |
| Encode context | encode(context) |
Base64url string or null |
| Decode context | decode(encoded) |
Context object or null |
| Read context without changing the URL | decodeFromLocation(location?) |
Context object or null |
| Restore missing identifiers locally | apply(pin, response, context?) |
Original or copied response |
BiomAPI.history
Section titled “BiomAPI.history”| Action | Signature | Result |
|---|---|---|
| Create a local store | create({ storage?, storageKey?, maxEntries? }) |
History store |
| Check one entry’s expiry | isExpired(entry) |
Boolean |
| Compare one entry’s database | hasDbIdMismatch(entry, dbId) |
Boolean |
| Identify storage failures | HistoryStorageError |
Error class with code |
History stores expose add, get, list, search, pruneExpired, pruneDbIdMismatch, clearOne, and clearAll, described in Manage entries.
Migrate from the legacy history SDK
Section titled “Migrate from the legacy history SDK”The legacy history SDK and window.BiomPinSDK remain available unchanged during migration. New integrations should load only biomapi-sdk.js; it includes the history store without depending on the old script.
| Legacy usage | Browser SDK equivalent |
|---|---|
BiomPinSDK.history.create(options) |
BiomAPI.history.create(options) with a new storage key and the entry format above. |
store.isExpired(entryOrPin), store.hasDbIdMismatch(entryOrPin, dbId) |
BiomAPI.history.isExpired(entry), BiomAPI.history.hasDbIdMismatch(entry, dbId); look up PINs with store.get(pin). |
BiomPinSDK.context.encode(), decodeFromLocation() |
The corresponding BiomAPI.context helpers, using camelCase context fields. |
BiomPinSDK.context.merge(pin, response, context, entries) |
Look up history explicitly if needed, then use the return value of BiomAPI.context.apply(pin, response, context). The response is no longer mutated. |
BiomPinSDK.context.fromResponse(response) |
BiomAPI.context.fromResponse(response); returns { patientName, patientId } or null instead of the wire-format object. |
BiomPinSDK.context.buildUrl(pin, context) |
BiomAPI.biompin.buildUrl({ pin, context }); returns an absolute BiomAPI URL. |
BiomPinSDK.context.buildCalculatorUrl(targetUrl, pin, context) |
BiomAPI.biompin.buildUrl({ pin, targetUrl, context }); the target must consume #biompin, not the legacy query parameter. |
Both SDKs can coexist, but must use separate storage keys. The new default biomapi_history does not read or change the legacy biompin_history.
To keep old entries, map their fields into store.add() on the new store. Nothing migrates automatically. Do not reuse a legacy key for the new entry format.
The version-1 encoded identity format is unchanged, but the new decoder validates context more strictly. Update context object shapes and link handling, and do not rely on legacy aliases or raw encoding helpers. History writes now report failures, and updates no longer prune other databases or treat null as an omitted value.
