Developers
JavaScript API
Control the widget from your own code with window.GoodGuy, and react to bookings and messages.
For developersOn this page
Calling it before the script loads
The widget loads with async, so your code may run first. Paste this queue stub before your own calls. Anything you call early is queued and run, in order, as soon as the widget is ready.
<script>
window.GoodGuy = window.GoodGuy || function () { (GoodGuy.q = GoodGuy.q || []).push(arguments) };
GoodGuy('open', 'booking');
</script>
<script src="https://justagoodguy.us/w.js" data-key="pk_your_key" async></script>In the queued form you pass the method name first: GoodGuy('book', { service: 'Haircut' }). Once the widget has loaded, GoodGuy also has every method on it, so GoodGuy.book({ service: 'Haircut' }) works too.
Methods
| Method | What it does |
|---|---|
GoodGuy.open(view?, opts?) | Opens the floating panel. view is 'chat', 'booking', 'callback' or 'contact'; without it, the last view is used. For booking, opts can be { service, staff }. |
GoodGuy.close() | Closes the panel. |
GoodGuy.toggle(view?) | Opens or closes the panel. |
GoodGuy.ask(text) | Opens chat and sends text as the visitor's message. |
GoodGuy.book({ service?, staff? }) | Opens booking from the start, with a service (id or name) and person (staff id) selected if you pass them. |
GoodGuy.identify({ name?, phone?, email? }) | Prefills the visitor's details in forms and chat. Nothing is sent until the visitor submits something themselves. |
GoodGuy.mount(element, type, options?) | Renders an inline widget into element. |
GoodGuy.on(event, callback) | Subscribes to an event. |
GoodGuy.off(event, callback) | Unsubscribes. Pass the same function you passed to on. |
GoodGuy.config | The settings loaded from /api/public/config, or null until they arrive. |
GoodGuy.version | The widget version. |
identify
Use identify when your visitor is already signed in to your own site, so they don't type their details twice:
GoodGuy('identify', { name: 'Dana Ruiz', phone: '+15555550123', email: '[email protected]' });Only name, phone and email are used, and empty values are ignored. Calling it again merges with what you passed before.
mount
const el = document.querySelector('#book-here');
GoodGuy.mount(el, 'booking', { service: 'Haircut' });options uses the same settings as the data-gg-* attributes, without the prefix:
| Option | Used by |
|---|---|
service | booking, contact |
staff | booking |
source | booking ('widget' or 'booking_page') |
height | chat |
token | manage, offer |
An element is only mounted once. An unknown type logs a warning and does nothing.
Events
GoodGuy('on', 'booking.created', (booking) => {
console.log('Booked', booking.serviceName, booking.whenLabel);
});| Event | When | What the callback gets |
|---|---|---|
ready | Settings have loaded | The config object. If you subscribe after it fired, your callback still runs once. |
open | The panel opens | { view } |
close | The panel closes | nothing |
message | A chat message is sent or arrives | { role, content }, where role is 'user', 'assistant' or 'owner' (a person from the business replying). Assistant messages also carry cards. |
booking.created | A booking was made, from the booking flow or the chat | Booking details: id, status, serviceName, staffName, whenLabel, manageUrl and source. From chat you also get start (ms since 1970). |
callback.created | A call-back was requested | { id, windowLabel, source } |
lead.created | A message or quote request was sent | { id, source } |
source is 'widget' or 'booking_page' for the booking flow and forms, and 'chat' when the AI did it.
Note
A booking.created event can carry a status other than confirmed: pending_approval means you approve each booking first. See booking statuses.
Example: send bookings to your analytics
GoodGuy('on', 'booking.created', (b) => {
if (typeof gtag === 'function') {
gtag('event', 'booking', { service: b.serviceName, status: b.status, source: b.source });
}
});Events only run in the visitor's browser. To record bookings reliably on your server, use webhooks.
Errors
Problems are logged to the console with a [GoodGuy] prefix, for example an unknown method name or widget type. The one exception is mount, which throws if you don't pass it an element. An error inside your own event callback is caught and logged so it can't break the widget.