Widgets
Four embeddable widgets ship from the Medos SDK — booking, patient portal, enquiry form, and package purchase. Each is its own script tag and its own global.
The Medos client SDK is a set of independent CDN bundles, one per widget.
Each is a self-contained IIFE that mounts React into a container you provide,
and each exposes exactly one global on window.
They share the same API key, the same theming system, and the same modal-or-inline mounting model — but they are separate scripts. Load only the ones the page needs; every bundle carries its own copy of React.
There is no npm package
medos-sdk on npm is deprecated and no longer published. Every integration
loads a CDN bundle — no build step, no framework lock-in, no peer dependency
to reconcile. If you have npm i medos-sdk in a project, remove it.
The four widgets
Each widget has an overview that explains what it is for and how it behaves, with a live playground, then pages for its configuration, callbacks, and errors — every config key, every callback payload, the TypeScript types, and the error model.
Booking
The appointment widget. Doctor, date, slot, verification, confirmation — scheduled appointments and queue tokens in one flow.
Patient portal
Where booking ends. A returning patient verifies once and sees their appointments, family members, and session credits.
Enquiry form
A contact form that files an enquiry against your workspace instead of landing in an inbox.
Package purchase
Sell a multi-session package on its own page, outside the booking flow.
Bundles and globals
| Widget | Script | Global | Methods | Options |
|---|---|---|---|---|
| Booking | https://widgets.medos.one/v2/unified.js | window.MedosBooking | init, open, close | Configure |
| Patient portal | https://widgets.medos.one/v2/portal.js | window.MedosPatientPortal | init, open, close | Configure |
| Enquiry form | https://widgets.medos.one/v2/enquiries.js | window.MedosEnquiry | init, open, close | Configure |
| Package purchase | https://widgets.medos.one/v2/packages.js | window.MedosPackagePurchase | init | Configure |
The URL is the same for every site and every environment — there is nothing to configure per deployment.
Most of these docs are about the booking widget
The booking widget section has the most depth — configuration, booking system types, callbacks, and advanced topics — and the framework guides under Install use it as their example. Theming applies to every widget. Each widget's overview says what it shares with the booking widget and where it differs.
What they share
- One API key. The same
mk_key starts every widget — see API keys. Each widget identifies its own type when it exchanges the key for a session token, so the platform can scope and observe them separately. - Instance isolation. Every mounted widget gets its own React tree, its own TanStack Query client, and its own store. Two widgets on one page never share state. See Architecture at a glance.
mode: "modal" | "inline". Modal overlays the page; inline mounts into the element whose id you pass ascontainerId. The package widget is inline only.
What they don't share
- Config surface. Every widget takes the same theming options — built-in
theme names, the flat brand shortcuts, and
fontSize. Beyond that the surfaces differ: only the booking widget and the patient portal take sizing andotpChannels, the enquiry form has no verification step, and the package widget mounts inline only. Each widget's Configure page has the exact list. - Globals. They are separate singletons. Loading all four gives you four independent widgets, not one widget with four modes.
A widget type has to be enabled for your workspace
Each widget presents a distinct type when it starts a session, and the platform gateway only mints sessions for types it has been told to allow. If a widget fails immediately with an authentication error while the booking widget works with the same key, ask your Medos contact to enable that widget type for your workspace.
Trying them
Each widget's page carries its own playground, pinned to that widget: configure it with the controls, watch the preview update, and copy the generated snippet. The preview runs against a demo workspace, so the doctors, locations and availability are sample data, and no key is needed to use it.
The controls on offer change with the widget, because the option surfaces genuinely differ:
| Widget | Theme | fontSize | layout | otpChannels | Compact / Calendar | maxHeight | Modal |
|---|---|---|---|---|---|---|---|
| Booking | Name + shortcuts | Yes | Yes | Yes | Yes | Yes | Yes |
| Patient portal | Name + shortcuts | Yes | — | Yes | — | Yes | Yes |
| Enquiry form | PartialTheme object | — | — | — | — | — | Yes |
| Package purchase | — | — | — | — | — | — | — |
The enquiry form's colour and font controls are emitted as a nested theme object
rather than as the flat primaryColor / fontFamily shortcuts; both forms work on
that widget. The playground doesn't offer theming controls for the package widget,
though the widget itself accepts the same theming options — see its
Configure page.
What the controls map to
| Control | Config option | Reference |
|---|---|---|
| Theme presets | theme, primaryColor, secondaryColor, borderRadius | Theming |
| Override brand colours | primaryColor, secondaryColor | Brand colours |
borderRadius slider | borderRadius | Brand colours |
| Font picker | fontFamily + fontUrl | Brand colours |
fontSize | fontSize | init() options |
| Inline / Modal | mode (+ containerId for inline) | Modal vs inline |
| Full / Compact / Calendar | compact, calendarOnly | Calendar-only and compact |
maxHeight slider | maxHeight | Sizing |
layout | layout | init() options |
| Verification | otpChannels | init() options |
| Device buttons | — preview only, the widget is responsive by default | — |
The device buttons don't map to a config option. The widget adapts its own layout to the space it's given, so resizing the preview frame is the same as resizing a browser window.
Options that render identically to leaving them out — fontSize: "medium",
layout: "vertical", both verification channels — are omitted from the generated
snippet, so what you copy is the shortest config that reproduces what's on screen. The
snippet also never carries a real key: it shows apiKey: "mk_your_publishable_key" as
a placeholder, because you may be sharing your screen and the copied sample is meant
to be pasted into a repo.
A preview that won't load isn't necessarily your fault
The preview loads each widget's bundle from widgets.medos.one. If something on
your side blocks it — an ad blocker, a corporate network policy, an offline
connection — the preview says so and the generated snippet stays correct.
Server-side
These bundles run in the browser and use a publishable key. To book, reschedule, or cancel from your own backend — with a secret key, an IP allowlist, and no UI — use the Server-side API instead.