WidgetsPatient portal

Configure the patient portal

Every MedosPatientPortal option with its type and default, the init, open and close methods, and the PatientPortalConfig TypeScript type.

The portal takes the same config object as the booking widget, so theming, sizing, and verification behave identically. Want to try options before writing code? Use the playground.

Methods

init(config)

MedosPatientPortal.init(config: PatientPortalConfig): void

Starts the portal. Call it once per page.

  • Inline mode — mounts into the element named by containerId.
  • Modal mode — opens the modal immediately, so call it from a click handler rather than on page load.

Required: apiKey. Plus containerId when mode is "inline".

open(config?)

MedosPatientPortal.open(config?: Partial<PatientPortalConfig>): void

Opens the portal as a modal again after init(), merging the argument into the last-known config. Throws if no apiKey has ever been set. Call init() first: it is what exchanges your key for a session.

close()

MedosPatientPortal.close(): void

Closes the open modal and fires onClose if one was provided. A no-op when the portal is inline or the modal is already closed.

Options

Core

OptionTypeRequiredDefaultDescription
apiKeystringYes—Your Medos publishable key. See API keys.
mode"modal" | "inline"No"inline"Anything other than "modal" is treated as inline.
containerIdstringFor inline—DOM id the portal mounts into. Ignored in modal mode.

Sizing

OptionTypeDefaultDescription
maxWidthnumber | string720pxCap the width. A number means px.
maxHeightnumber | string—Cap the height; inner content scrolls.

Verification

OptionTypeDefaultDescription
otpChannels("phone" | "email")[]["phone", "email"]Which channels the sign-in step offers.

The same rules as the booking widget: the order does not matter, and a value that isn't a usable array falls back to ["phone"] alone rather than to both.

Appearance

OptionTypeDefaultDescription
theme"default" | "modern" | MedosTheme | PartialTheme"default"Built-in name, full theme object, or partial override. See Theming.
primaryColorstringFrom themeBrand colour. Hover and active shades are derived automatically.
secondaryColorstringFrom themeSecondary brand colour.
borderRadiusstring | numberFrom themeCorner radius. A number means px.
fontFamilystringFrom themeCSS font stack applied across the portal.
fontUrlstring—Stylesheet URL (e.g. Google Fonts) loaded so fontFamily resolves.
fontSize"small" | "medium" | "large""medium"Scales every font size proportionally.

Callbacks

onError and onClose. onSuccess is accepted but never called. See Callbacks.

Accepted but ignored

The portal inherits the booking widget's config type, so these keys type-check and are silently ignored rather than rejected:

externalMemberId, calendarOnly, compact, layout, doctorInfo, doctorInfoByDoctorId.

The portal is patient-scoped, not doctor-scoped

It shows the appointments on the verified contact across every doctor in the workspace — there is nothing to pre-select, which is why the booking-scope options have no effect here.

PatientPortalConfig type

interface PatientPortalConfig {
  /** Required. Your publishable key. */
  apiKey: string;

  // mounting
  mode?: "modal" | "inline";
  containerId?: string;
  maxWidth?: number | string;
  maxHeight?: number | string;

  // verification
  otpChannels?: ("phone" | "email")[];

  // appearance
  theme?: "default" | "modern" | MedosTheme | PartialTheme;
  primaryColor?: string;
  secondaryColor?: string;
  borderRadius?: string | number;
  fontFamily?: string;
  fontUrl?: string;
  fontSize?: "small" | "medium" | "large";

  // callbacks
  onError?: (error: Error) => void;
  onClose?: () => void;

  /** Accepted for config compatibility with the booking widget, never called. */
  onSuccess?: (result: unknown) => void;
}

Booking-scope keys (externalMemberId, calendarOnly, compact, layout, doctorInfo, doctorInfoByDoctorId) are part of the shared type and accepted here, but have no effect.

On this page