Versioning and deprecations
The widget is delivered from a single URL and updates without action on your part. This page states what that update can change.
The bundle you load
Section titled “The bundle you load”<script src="https://app.useservice.app/widget/widget.js" async></script>That address is stable. It redirects to a content-hashed file:
| Response | Cache-Control |
|---|---|
/widget/widget.js — 302 to the current build | public, max-age=300, must-revalidate |
/widget/widget.<hash>.js — the build itself | public, max-age=31536000, immutable |
So a browser rechecks the stable address every five minutes and caches the file it is sent for a year. A new release reaches your visitors within minutes of shipping.
Copying the hashed URL into your page, self-hosting the file, or letting a CMS caching plugin rewrite the address pins you to one build. You will stop receiving fixes, including security fixes. Load the stable address.
The version you are running
Section titled “The version you are running”ServiceWidget.version; // "2.8.0"This is the version of the JavaScript API — the options, methods and events on this site — not of the funnel the guest sees. Report it when raising an issue.
What an update can change
Section titled “What an update can change”Anything the guest sees. Layout, copy, steps, styling, added fields, changed wording, new booking states. The funnel is ours to change and it changes often.
Write your integration so it does not depend on the widget’s internals:
- Do not query the widget’s DOM, its shadow root, or any class name.
- Do not depend on the number or order of steps in the funnel.
- Do not treat
reservation.statusas a closed set — new values are added.
What an update will not change
Section titled “What an update will not change”The API on this site is what you build against, and we do not break it:
- Options accepted by
ServiceWidget.create(). - Methods on the instance, and their behaviour.
- Event names, and the fields already present on their payloads.
- The hint names accepted by
setContext()and by URL parameters. - The token format, its claim names, and the meaning of
locked. - The query parameters appended to your
return_url.
Payloads gain fields. A new key on an event payload or a bootstrap response is not a breaking change, so read the fields you need rather than asserting on the whole object.
Removed names
Section titled “Removed names”data-mode="manual" was the script tag’s name for popover, and
ServiceWidget.open({ slug }) opened a widget the global created on demand.
Both were removed on 6 August 2026. data-mode="manual" now behaves like any
unrecognised value and falls back to inline; open() takes no argument and
drives the widget the script tag mounted.
An earlier version of this page said both were “accepted indefinitely” and live
on restaurant sites that would never be edited again. That was an assumption, not
a fact: Service publishes every embed snippet in use, and the back office now
emits data-mode="popover". Removing them cost nothing and removed a second name
for one thing.
One consequence is worth stating plainly: one script tag drives one
restaurant. A [data-service-widget-open] trigger carrying its own data-slug
used to get a widget of its own. It no longer does — it is ignored, with an
explanation on the console, rather than quietly opening the restaurant the script
tag serves. For several restaurants on one page, call create() per restaurant
and keep the handles.
If we ever need to break something
Section titled “If we ever need to break something”There is no deprecation timetable here to point at, because nothing is deprecated. If that changes, a breaking change would be announced on this page before it shipped, with the old behaviour retained for the announced period. Until then, the guarantee above is the whole policy.