Embedding the Fixably Booking App into your site
How to seamlessly integrate the Fixably Booking App into your website for enhanced user experience and streamlined bookings.
- Release Notes
- Newly Released Features
- Getting Started
- Roles & Permissions
- Manifests
- Order Management
- Automations
- Employee Management
- Customer Management
- Customer Portal
- Stock Management
- Part Requests
- Price Lists
- Invoicing
- Reporting
- Custom Fields
- Integrations
- GSX
- Shipments
- Service Contracts
- Configuring Your Environment
- Incoming Part Allocation
- Appointment Booking
Table of Contents
Audience: partner integrators embedding a Fixably tenant’s booking flow inside an iframe on their own site.
Overview
The Fixably Booking App can be embedded inside an <iframe> on your site. The embed runs the full booking flow — service selection, scheduling, customer details, confirmation — without the customer leaving your page.
Your page and the embedded app talk over standard window.postMessage: the app sends your page events (it resized, a booking completed), and your page reacts. There is no SDK and nothing to npm install.
Embedding takes three steps:
- Get your site’s origin allowlisted by your Fixably contact — one-time, per tenant.
- Drop the iframe and script snippet into your page (see Quick start below).
-
Handle the events the app sends you — at minimum
fixably:resizeto size the frame, andfixably:booking-completedas your conversion signal.
Here’s the message flow once it’s running:
Your page (parent) Fixably Booking App (iframe)
------------------ ----------------------------
| |
| loads <iframe src=...> ----------------> |
| |
| <-- fixably:ready ------------------------ | embed is alive
| <-- fixably:resize { height } ------------ | size the frame
| |
| (customer books) |
| <-- fixably:booking-completed { orderId } | conversion
| |
you react:
resize the iframe, fire analytics, redirect
Before you begin
-
Your origin is allowlisted by Fixably. Ask your Fixably contact to add your site’s origin (for example
https://yoursite.example.com) to the relevant tenant. Until your origin is allowlisted, the browser refuses to render the iframe — you get a blank box and a CSP error in the console. - Your page is served over HTTPS. The booking app is HTTPS-only, and a secure page cannot embed an insecure frame. If your page isn’t already on HTTPS, move it there first.
-
You have the tenant’s booking-app URL. The format is
https://{tenant}-booking.repairportal.com. Your Fixably contact will confirm the exact value.
Quick start
Drop this into your page:
<iframe id="fixably-booking"
src="https://{tenant}-booking.repairportal.com/"
sandbox="allow-scripts allow-forms allow-same-origin allow-popups allow-popups-to-escape-sandbox"
style="width:100%;border:0"></iframe>
<script>
(function () {
var iframe = document.getElementById('fixably-booking');
var expectedOrigin = 'https://{tenant}-booking.repairportal.com';
window.addEventListener('message', function (event) {
if (event.origin !== expectedOrigin) return;
var msg = event.data;
if (!msg || msg.source !== 'fixably-booking') return;
switch (msg.type) {
case 'fixably:resize':
iframe.style.height = msg.payload.height + 'px';
break;
case 'fixably:booking-completed':
// Your success handling — analytics, redirect, success message.
// msg.payload.orderId is the Fixably order id.
break;
case 'fixably:booking-cancelled':
// Optional: handle abandonment.
break;
case 'fixably:error':
// Optional: forward to your monitoring.
// msg.payload.code is one of: "config-load-failed".
break;
// Forward-compatibility: ignore any unknown types.
}
});
})();
</script>
Replace {tenant} with the tenant identifier in both the src and expectedOrigin.
Check it worked: load your page. The booking flow should render and the frame should resize to fit its content as the customer moves through it. If you get a blank box instead, see Troubleshootingbelow.
The sandbox attribute is required
The booking app needs each of these sandbox tokens to function:
| Token | Why |
|---|---|
allow-scripts |
The booking app is a JavaScript SPA. |
allow-forms |
The wizard submits booking data. |
allow-same-origin |
The app reads its own subdomain config and uses local storage. |
allow-popups |
Required by reCAPTCHA. |
allow-popups-to-escape-sandbox |
Required by reCAPTCHA. |
Do not drop tokens to “be safer” — the embed will break in non-obvious ways (forms won’t submit, the captcha won’t render).
We deliberately omit the tokens below, and you do not need to add them:
-
allow-top-navigation— would let our content navigate your page. -
allow-modals,allow-downloads— not used today.
Event reference
All inbound messages from the booking app share this envelope:
{
source: 'fixably-booking',
type: 'fixably:<name>',
payload: { ... } // event-specific; may be omitted
}
Before reacting, validate two things: event.origin === expectedOrigin and event.data.source === 'fixably-booking'. The Quick start snippet does both.
| Type | Payload | When it fires |
|---|---|---|
fixably:ready |
none | Fired once when the SPA mounts inside the iframe. Useful as an “the embed is alive” signal. |
fixably:resize |
{ height: number } |
Fired on content height changes (debounced). Use it to size the iframe so the customer doesn’t see internal scrollbars. |
fixably:booking-completed |
{ orderId: number } |
Fired after the customer successfully creates a booking. This is the conversion event. |
fixably:booking-cancelled |
none | Reserved. Not currently emitted; will fire if and when in-progress abandonment can be detected reliably. Subscribe defensively. |
fixably:error |
{ code: string, message?: string } |
Fired on application-level errors you may want to surface to monitoring. Initial code: config-load-failed. The list is expected to grow. |
Forward compatibility
Ignore unknown type values rather than throw. New event types are added over time — for example, payment-related events when payment is introduced. A handler that throws on unknown types will start crashing on the rollout day; one that ignores them keeps working.
The Quick start snippet uses a switch with no default action, which is the right shape.
Recipes
Wire fixably:booking-completed into your analytics
This example assumes Google Analytics 4. Other platforms (Segment, Mixpanel, Tealium) follow the same shape.
case 'fixably:booking-completed':
if (typeof gtag === 'function') {
gtag('event', 'booking_completed', {
event_category: 'fixably',
transaction_id: String(msg.payload.orderId),
channel: 'iframe', // helpful if you also have a direct booking link elsewhere
});
}
break;
Your existing UTM and pageview state stays in your parent page. The booking-completed event tells you when to record the conversion; your analytics platform attributes it to the right session.
Wire fixably:error into your monitoring
This example assumes Sentry’s browser SDK is already loaded.
case 'fixably:error':
if (typeof Sentry !== 'undefined') {
Sentry.captureMessage('fixably-booking ' + msg.payload.code, {
level: 'warning',
extra: { message: msg.payload.message },
});
}
break;
Detect a blocked iframe (the embed never loads)
If your origin is not allowlisted, the browser refuses to render the iframe at all and fixably:ready never arrives. Detect this with a timeout:
var ready = false;
window.addEventListener('message', function (event) {
if (event.origin !== expectedOrigin) return;
if (event.data && event.data.type === 'fixably:ready') ready = true;
});
setTimeout(function () {
if (!ready) {
// Likely causes: origin not allowlisted, network failure, or the iframe was blocked.
// Surface this to monitoring or render a fallback message.
console.warn('Fixably booking embed did not load within 10s.');
}
}, 10000);
A 10-second window is conservative; tune it to your site’s expected load times.
Troubleshooting
The iframe is blank and the console shows “Refused to display … frame-ancestors”
Your origin is not in the tenant’s allowlist. Note the exact origin string in the error message and ask your Fixably contact to add it. Allowlist changes go live with the tenant’s next deploy.
To check the live header from your terminal:
curl -sI "https://{tenant}-booking.repairportal.com/" | grep -i content-security-policy
The frame-ancestors value lists who is allowed to frame the embed. If your origin isn’t there, that’s the issue.
Mixed content errors
Both your page and the iframe must be HTTPS. If the page hosting the iframe is on HTTP, the browser either blocks the iframe entirely or strips cookies, which breaks the booking flow. Serve everything over HTTPS.
event.origin is null or unexpected
A null origin means the parent page is loaded from a context with no real origin — file://, about:srcdoc, or a sandboxed iframe without allow-same-origin. Embed only from a normal HTTPS page.
Booking completes but fixably:booking-completed doesn’t fire
The most common cause is that the listener is attached after the message has already arrived. Attach the listener before setting the iframe src, or before the page mounts the iframe.
Browser support
Modern evergreen browsers are supported: the latest Chrome, Edge, Firefox, and Safari on desktop, plus iOS Safari and Android Chrome. The booking app relies on ResizeObserver, postMessage, and CSP frame-ancestors, all of which are baseline-supported.
On older browsers the embed may still render, but resize won’t auto-adjust. Set a reasonable fixed height on the iframe element as a fallback.
Getting support
Contact your Fixably account manager or support channel with:
- The exact origin you’re embedding from.
- The tenant identifier whose booking app you’re trying to embed.
- A screenshot of the browser console, if you’re seeing an error.
- A link to a page where the issue can be reproduced, if possible.