> ## Documentation Index
> Fetch the complete documentation index at: https://docs.voxhealth.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Installation

> Add the widget to a site.

## Get the snippet

A VoxHealth admin generates it under **Settings → Scheduling Widget**. Generating mints the public token and records the domains it is allowed to run on. You cannot construct a working snippet by hand.

## Paste it

```html theme={null}
<script
  src="https://widget.voxhealth.ai/scheduling-widget.global.js"
  data-token="YOUR_PUBLIC_TOKEN"
  data-api-url="https://YOUR_SUPABASE_HOST/functions/v1/scheduling-widget"
  data-title="Book an appointment"
  data-primary-color="#D93C00"
  data-position="bottom-right"
  data-lang="en"
  defer
></script>
```

Put it before the closing `</body>` tag. `defer` matters: the widget captures its own script element at load and needs the document to be parseable.

<Note>
  `data-token` and `data-api-url` are both required. If either is missing the widget initializes nothing and exits silently. By design, so a half-configured tag never renders a broken booking form to patients.
</Note>

## Verify it

<Steps>
  <Step title="Load the page">
    The bubble should appear in the corner you configured.
  </Step>

  <Step title="Open it">
    You should see real available times within a second or two. Times that never load usually mean the domain is not on the allow-list. Check the browser console for a 403.
  </Step>

  <Step title="Book a test appointment">
    Use a real mobile number you control. Then confirm the appointment appears in the practice's PMS with the right provider, operatory, and duration.
  </Step>

  <Step title="Cancel it">
    Use the same form to find and cancel the test booking. This exercises the manage path as well as the booking path.
  </Step>
</Steps>

## Platform notes

<AccordionGroup>
  <Accordion title="WordPress">
    Add it to the theme footer, or use a "custom HTML / code injection" block. Do not paste it into the post editor. The visual editor strips script tags.
  </Accordion>

  <Accordion title="Squarespace">
    **Settings → Advanced → Code Injection → Footer.**
  </Accordion>

  <Accordion title="Webflow">
    **Project Settings → Custom Code → Footer Code.**
  </Accordion>

  <Accordion title="A single-page app">
    Do not inject the tag on every route change. Load it once, or use the programmatic API in [Configuration](/developers/widget/configuration).
  </Accordion>
</AccordionGroup>

## Caching

The script is served with a short cache lifetime and revalidates in the background, so a widget update reaches visitors without anyone clearing a cache.

Regenerating the snippet in the app stamps a version marker on the URL, which is how a practice forces a refresh after a configuration change.

<Warning>
  Do not self-host a copy of the script. The widget and the edge function it talks to ship together; a pinned copy drifts out of compatibility with the API and eventually stops booking.
</Warning>
