Web Widgets
Events
Every event the OnePath widgets fire on document, with the exact payload, and the one event you dispatch yourself.
document.addEventListener('onepath:webscheduler:appointment_created', (e) => {
const { customer, location, time, timezone } = e.detail;
// your analytics call here
});
All events are CustomEvents on document; read the payload from e.detail. Attach listeners at any time before the event fires, for example in a script in the head right after the loader. Names and payload shapes are stable; new optional fields may be added.
| Event | Fires when |
|---|---|
onepath:config-ready | The account configuration has loaded (once per page load) |
onepath:widget-ready | The scheduler has opened for the first time and authenticated |
onepath:webscheduler:appointment_created | A visitor booked an appointment |
onepath:webscheduler:task_created | A visitor asked for a call back instead of a slot |
onepath:webchat:request_created | A visitor submitted the chat (text me back) form |
onepath:webscheduler:open | You dispatch this to open the scheduler |
Booking events need a service address
appointment_created and task_created fire only when the visitor got past the address step, so a service address is always present in the payload. A visitor who leaves on step 1 fires nothing.
Events you listen for
onepath:config-ready
Fires once, as soon as the loader has the account's public configuration and before any widget script loads. Use it to read the brand color or business name, or to check which widgets are enabled before rendering your own buttons.
document.addEventListener('onepath:config-ready', (e) => {
const { account, channels } = e.detail;
console.log(account.name); // "Wahoo Plumbing"
console.log(account.brand_color); // "#004993"
console.log(channels.web_scheduler.enabled); // true / false
console.log(channels.web_chat.enabled); // true / false
console.log(channels.web_mcp.enabled); // true / false, booking by AI agents
});
e.detail:
account:name,slug,brand_color,text_color,phone,full_address,logo(URL)channels:web_scheduler.enabled,web_chat.enabled,web_mcp.enabled, telling you which widgets are enabled (web_mcpis booking by AI agents)
The same object is available afterwards as window.onepath.config.
onepath:widget-ready
Fires once, after the scheduler has been opened for the first time and has authenticated with OnePath. It does not fire on page load, and the chat widget never fires it. Do not gate your trigger buttons on it; they work as soon as the loader has run.
document.addEventListener('onepath:widget-ready', (e) => {
if (e.detail.name === 'scheduler') {
// the scheduler modal is open and talking to OnePath
}
});
e.detail: name (always "scheduler"), config (the account configuration above).
onepath:webscheduler:appointment_created
Fires once when a visitor books an appointment. It does not fire again if they cancel from the confirmation screen. Wire it to conversion tracking or a post-booking redirect.
document.addEventListener('onepath:webscheduler:appointment_created', (e) => {
const { customer, location, time, timezone } = e.detail;
});
e.detail:
{
"customer": {
"full_name": "Jane Homeowner",
"contacts": { "email": "jane@example.com", "phone": "+15125550100" }
},
"location": { "address": "2100 Barton Springs Rd, Austin, TX 78704" },
"time": "2026-07-22T19:00:00.000Z",
"timezone": "-05:00"
}
time: ISO 8601 string of the start of the booked window, in UTCtimezone: the UTC offset of that window as a string, for example"-05:00", ornull. It is not an IANA zone namecustomer.contacts.phone: E.164 format;emailmay be empty when the visitor did not give onesource: present only when an AI agent booked through the WebMCP tools, with the value"web_mcp". Scheduler bookings carry nosource
onepath:webscheduler:task_created
Fires once when a visitor uses Need it sooner? Request Call Back instead of picking a slot. A lead with a service address exists; no appointment does.
document.addEventListener('onepath:webscheduler:task_created', (e) => {
const { customer, location } = e.detail;
});
e.detail: customer (same shape as above) and location.address. No time or timezone.
onepath:webchat:request_created
Fires once when the chat form is submitted successfully.
document.addEventListener('onepath:webchat:request_created', (e) => {
const { customer } = e.detail; // full_name, contacts.email, contacts.phone
});
e.detail: customer.full_name, customer.contacts.email, customer.contacts.phone (E.164). No location.
Events you dispatch
onepath:webscheduler:open
Opens the scheduler from your own code, optionally pre-filling the visitor and pre-selecting the service. Full pattern: Open the scheduler from a form submit.
document.dispatchEvent(new CustomEvent('onepath:webscheduler:open', {
detail: {
phone: '5125550100',
name: 'Jane Homeowner',
email: 'jane@example.com',
address: '2100 Barton Springs Rd, Austin, TX',
serviceable_type: 'Water Heater',
service_type: 'Estimate',
context: 'Better Package — 50 Gallon — $2,695'
}
}));
All fields are optional. An empty detail just opens the modal.
| Field | Behavior |
|---|---|
phone | Applied only with at least 10 digits; shorter values are ignored. Looked up immediately, like a typed number |
name | Full name, first and last (Jane Homeowner). A customer already on file for that phone keeps the name on file |
email | Pre-fills the email on the address step. Never required |
address | Seeds the address field; the visitor confirms a suggestion |
serviceable_type | Pre-selects the service (Water Heater). Must match the account's Services exactly, or it is ignored |
service_type | Narrows the pre-selection (Estimate, Repair) |
context | Free text saved verbatim on the booking; not shown to the visitor. There is no notes field |
The event is queued if the scheduler is still loading, but lost if dispatched before loader.js has run. If the scheduler is disabled on the account, the event is dropped with a warning in the console. Exact serviceable_type / service_type values are on the Services page: app.onepath.ai/settings/account/service_details.
Google Tag Manager
Push each event into the data layer, then build GTM triggers on the event names.
<script>
window.dataLayer = window.dataLayer || [];
['onepath:webscheduler:appointment_created',
'onepath:webscheduler:task_created',
'onepath:webchat:request_created'].forEach((name) => {
document.addEventListener(name, (e) => {
window.dataLayer.push({
event: name.replace(/[:]/g, '_'), // e.g. onepath_webscheduler_appointment_created
appointment_time: e.detail.time || null,
service_address: e.detail.location ? e.detail.location.address : null,
});
});
});
</script>
In GTM, create a Custom Event trigger named onepath_webscheduler_appointment_created and attach your conversion tag to it. Keep customer names, phones and emails out of the data layer unless your privacy policy covers it.
Google Analytics 4 (gtag)
document.addEventListener('onepath:webscheduler:appointment_created', (e) => {
gtag('event', 'appointment_booked', {
appointment_time: e.detail.time,
timezone: e.detail.timezone,
});
});
document.addEventListener('onepath:webscheduler:task_created', () => {
gtag('event', 'callback_requested');
});
document.addEventListener('onepath:webchat:request_created', () => {
gtag('event', 'text_back_requested');
});
Mark appointment_booked as a conversion in GA4. For server-side data (the appointment's service, the full customer record, cancellations and reschedules), use webhook events instead; browser events only cover what happens in the widget.