Licensing
TreatmentConsultant checks a license key locally, in the browser — signature, expiry, and domain are all verified offline, no server component required. If the key is missing, invalid, expired, or not valid for the current domain, the widget shows a watermark overlay. The wizard underneath stays fully interactive either way — the watermark is visual only, so a licensing edge case never breaks the actual booking flow for a real visitor.
On top of that local check, the widget also does a cached, best-effort remote check-in against GuideMyHair's license API — once every 24 hours per license key, not on every page load — to catch a license that's been canceled or expired server-side even though its token is still validly signed and unexpired. This check fails open: a network error or unreachable GuideMyHair server never revokes a locally-valid license, only an explicit "inactive" response does. Your client's site is never broken by our uptime.
See it running with a real license key on our live demo page.
Getting a key
Buy a plan on the pricing page and your license key is emailed to you automatically. It's a signed token — treat it like any other API key (don't commit it to a public repo), but it's safe to ship inside your client-side bundle, since it only proves entitlement, it doesn't grant access to anything sensitive.
Passing the key
Pass it as a prop on a single instance, or once via the plugin to cover every instance in your app — a prop always wins over the plugin's key if both are set.
<TreatmentConsultant license-key="eyJhbGciOi..." />// app/plugins/hair-treatments.ts
import { createHairTreatments } from "srcdev-hair-treatments"
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(createHairTreatments({ licenseKey: "eyJhbGciOi..." }))
})Domain matching
Every license is locked to one or more domains, set on your account. The current page's hostname is checked against that list:
localhostand127.0.0.1are always allowed, so local development never needs a real key.- A plain domain (
example.com) also covers itswww.version, and vice versa — you don't need to list both. - A wildcard entry (
*.example.com) covers any subdomain of that domain —staging.example.com,preview.example.com, and so on — but not the bare domain itself, so pair it with a plainexample.comentry if you need both. - Shared preview-hosting domains —
*.vercel.app,*.netlify.app, and similar — are never matched, on any license, under any circumstances. That domain isn't yours to license: it's shared with every other app hosted on the same platform, so allowing it would mean allowing everyone. Expect the watermark on preview deploys — point a subdomain of your own domain at the deployment instead if you need a license-free preview to share.
Need to add or change domains on an existing license — e.g. adding a staging subdomain? Get in touch.
Content-Security-Policy
If your site sets a connect-src directive, add https://guidemyhair.com to it — that's the remote check-in described above, and a blocked request looks identical to a network error from the widget's side, so it fails open silently rather than erroring visibly. You'll still get a specific console warning either way ("blocked by Content-Security-Policy" vs. a generic network-failure message), so check your browser console if you're not sure which applies.
Watermark reasons
| Reason | Shown when |
|---|---|
| No license key provided | Neither the prop nor the plugin has a key set |
| License key could not be verified | The key is malformed, or wasn't signed by GuideMyHair |
| This license has expired | Past the license's expiry date (subscriptions only — lifetime licenses never expire) |
| This license is not valid for this domain | The current hostname isn't covered by the license's domains — see Domain matching above |
| This license is no longer active | The daily remote check-in got an explicit "inactive" response — the license was canceled or expired server-side |