Your website
Add the widget to your site
One line of code puts the chat and booking bubble on any website. Here's where to paste it on the common site builders.
For owners and developersOn this page
Your line of code
Open Dashboard → Website and copy your widget code. It looks like this, with your own key in it:
<script src="https://justagoodguy.us/w.js" data-key="pk_your_key" async></script>pk_keys are publishable. They are meant to sit in your web pages, so it's fine that anyone can see them.- Paste the line once, on every page, just before the closing
</body>tag. Most site builders have one box that adds code to every page. - The
asyncpart means it never slows your page down.
When it works, a round button appears in the bottom corner of your site. Tapping it opens the chat and booking panel.
Note
If you host your site with us, or use your own server with the host app, the widget is added for you. You don't need to paste anything.
Where to paste it
Plain HTML
Paste the line just before </body> in every page. If your pages share a footer file or template, put it there once.
...
<script src="https://justagoodguy.us/w.js" data-key="pk_your_key" async></script>
</body>
</html>WordPress
The easiest way is a plugin that adds code to every page:
- In WordPress, go to Plugins → Add New and search for "header footer code".
- Install and activate one of the well-reviewed plugins.
- Open its settings and paste your line into the footer (or "body end") box.
- Save, then clear any caching plugin's cache.
If you use a child theme and are comfortable editing it, you can paste the line just before </body> in its footer.php instead.
Wix
- In your Wix dashboard, go to Settings, then Custom code (under Advanced).
- Tap + Add Custom Code and paste your line.
- Under "Add Code to Pages", choose All pages. Under "Place Code in", choose Body - end.
- Tap Apply.
Wix only runs custom code on sites with a paid plan and a connected domain.
Squarespace
- Open your site's Settings, then find Code injection (it's under Advanced or Developer tools, depending on your version).
- Paste your line into the Footer box.
- Save.
Code injection is only available on some Squarespace plans.
Shopify
- In Shopify admin, go to Online Store → Themes.
- Next to your current theme, open the ... menu and choose Edit code.
- Open
layout/theme.liquid. - Paste your line just before
</body>and tap Save.
If you change themes later, paste it into the new theme too.
Webflow
- Open Site settings → Custom code.
- Paste your line into Footer code.
- Save, then Publish the site.
Custom code needs a paid Webflow site plan.
GoDaddy and other builders
Look for a setting called "custom code", "code injection", "tracking code" or "footer code" that applies to the whole site. Paste your line there.
Watch out
Some builders, including GoDaddy's Website Builder, only let you add code inside a box on one page (an "HTML" or "embed" section). That box is a separate frame, so the floating button stays stuck inside it. On those builders, link a button to your booking page instead, or put an inline booking widget in the box.
Check that it's working
- Open your website in a normal browser window (not a private one) and load a page.
- In Dashboard → Website, tap the button to check your install.
The check passes when the widget has reported a visit from your site in the last 30 days. It also shows which web address it saw.
If it doesn't pass:
- Wrong key. Copy the line from the dashboard again. If you ever revoked the key, the old line stops working.
- Cached pages. Clear your site's cache or your caching plugin, then reload.
- Pasted in the wrong place. Make sure the code is in a sitewide footer box, not inside a text block where it shows up as text.
- Allowed sites. If you've set allowed sites, make sure this site is on the list.
- Your site blocks outside scripts. If your site sends a Content Security Policy, see the note for developers.
Allowed sites
By default your key works on any website. To stop other sites from using your assistant, list the sites that may use it. You'll find this on your publishable key in More → Developers.
Each entry can be:
| Entry | Matches |
|---|---|
https://joesplumbing.com | exactly that address: scheme, host and port, with no trailing slash |
joesplumbing.com | that host name, over http or https |
*.joesplumbing.com | any subdomain, like www.joesplumbing.com, but not joesplumbing.com itself |
joesplumbing.comdoesn't coverwww.joesplumbing.com. If your site answers on both, add both, or add the host name and the*.entry.- An empty list means any site may use the key.
- You can add up to 20 entries.
- Your hosted booking page always works, whatever is on the list.
- Requests from a site that isn't on the list get
403with the message "This site isn't allowed to use this booking assistant."
Note
The check uses the Origin header that browsers send. It stops other websites from embedding your assistant, but a publishable key is not a password. Anything that needs real secrecy belongs on your server with a secret key.
Replacing your key
Revoking the publishable key in More → Developers creates a new one straight away. Update the line on your site with the new key. Sites served by us or by the host app pick up the new key on their own.
Contact forms
A contact form on your site works with no extra setup once the line of code is on the page. When someone sends it:
- it lands in your inbox as an email conversation (and under Requests);
- when email is set up for your business, they get an email straight back: your receptionist's answer when it's on, otherwise "we got your message". It comes from your business, so their reply lands in the same conversation. When it isn't, nothing is promised: the thank-you note doesn't mention email, and your alert says to call, text or email them back yourself;
- you get a text and an email with who they are, how to reach them, which page they were on, and what they wrote.
Which forms are picked up:
- Plain contact forms that have nowhere else to send: no
action(or#, or amailto:link), with a message box and an email or phone field. This is what most hand-made and builder-made sites have. - Any form with
data-gg-contact, even one that already posts somewhere else.
A form that posts to its own working backend is left alone unless you add data-gg-contact. To leave a form alone either way, add data-gg-ignore.
<form data-gg-contact data-gg-thanks="Thanks! We'll call you back today.">
<label>Name <input name="name" autocomplete="name"></label>
<label>Email <input name="email" type="email" autocomplete="email"></label>
<label>Phone <input name="phone" type="tel" autocomplete="tel"></label>
<label>Message <textarea name="message"></textarea></label>
<button type="submit">Send</button>
</form>- Fields are matched by type, name, label or placeholder: name (or first and last name), email, phone, subject and the message. Anything else (a date, a budget, a checkbox) is sent along as "Label: value".
- After sending, the form is replaced by a thank-you line. Set your own with
data-gg-thanks. - Problems ("Please write your message.") show above the form's button in plain words.
- A hidden "honeypot" field that a bot filled in means nothing is sent.
- The widget's
lead.createdevent fires withsource: "site_form".
Script options
All of these are optional except data-key. Add them to the same <script> tag.
| Attribute | What it does |
|---|---|
data-key="pk_..." | Your publishable key. Required. |
data-launcher="false" | Don't show the floating button. Use this when the page only has inline widgets. |
data-position="left" or "right" | Which side the button sits on. Overrides the dashboard setting. |
data-color="#RRGGBB" | Brand colour. Overrides the dashboard setting. |
data-open="chat" or "booking" | Open that view as soon as the page loads. |
data-font="inherit" | Use your page's font instead of loading Figtree. |
data-api="https://..." | Which server to talk to. Defaults to wherever the script was loaded from. |
For example, a booking page with no floating button that opens straight into booking:
<div data-gg-widget="booking"></div>
<script src="https://justagoodguy.us/w.js" data-key="pk_your_key" data-launcher="false" async></script>For developers
How loading works
/w.js is a tiny loader. Browsers may cache it for 5 minutes. It finds its own <script> tag (through document.currentScript, falling back to script[src*="/w.js"][data-key]), then loads the real widget from /w/app.<hash>.js, which is cached forever because its name changes with every release.
Everything renders inside a shadow root, so your site's CSS can't break it and its CSS can't leak into your site. See Styling the widget.
The widget never sets cookies. It only writes to localStorage after the visitor opens the chat or submits a form. See Customer memory and privacy.
Content Security Policy
If your site sends a Content-Security-Policy header, allow the widget's server and the font it loads:
Content-Security-Policy: script-src 'self' https://justagoodguy.us; connect-src 'self' https://justagoodguy.us; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.comMerge these into your existing policy rather than replacing it. If you use data-font="inherit", you can leave out the Google Fonts entries.
Single-page apps
Add the script once in your app shell. The triggers work on elements you add later, and you can render inline widgets from code with GoodGuy.mount.