ResytechResytech Docs

ResytechClient Reference

Complete API reference for ResytechClient — the iframe-based booking widget.

ResytechClient opens the booking UI in an iframe. It handles the overlay, loading spinner, body scroll lock, and cross-frame communication automatically.

Two presentations are available: the default full-screen modal, and an opt-in side drawer. See Presentation: modal or drawer.

Constructor

const client = new ResytechClient(config);

ResytechClientConfig

PropertyTypeRequiredDescription
locationIdstringYesYour Resytech location UUID
baseUrlstringYesBase URL of your booking platform (e.g. https://booking.yourdomain.com)
themeobjectNoTheme overrides
theme.primaryColorstringNoPrimary brand color (CSS color value)
theme.fontFamilystringNoFont family for the booking UI
debugbooleanNoEnable debug logging to the console. Default: false
closeOnEscapebooleanNoClose the modal when the user presses Escape. Default: true
closeOnBackdropClickbooleanNoClose the modal when the user clicks outside the booking card. Default: true
otaCodestringNoOTA attribution code, when embedding as an authorized partner. See OTA Partner Integration
presentation'modal' | 'drawer'NoHow the booking UI is presented. Default: 'modal' — the full-screen overlay every existing embed uses. See Presentation: modal or drawer
drawerDrawerOptionsNoDrawer geometry. Ignored unless presentation is 'drawer'. See DrawerOptions
const client = new ResytechClient({
  locationId: 'abc123-def456',
  baseUrl: 'https://booking.yourdomain.com',
  theme: {
    primaryColor: '#e63946',
    fontFamily: '"Poppins", sans-serif'
  },
  debug: true
});

Throws an Error if locationId or baseUrl is missing.

Auto-Initialization

When loaded via a script tag with data-resytech-location-id and data-resytech-base-url attributes, the library automatically creates a ResytechClient instance and assigns it to window.resytech.

<script
  src="https://js.resytech.com/latest/resytech.js"
  data-resytech-location-id="YOUR_LOCATION_ID"
  data-resytech-base-url="https://booking.yourdomain.com"
  data-resytech-debug="true">
</script>

<script>
  // The client is ready to use
  window.resytech.showUI();
</script>

Auto-initialization also scans the DOM for elements with data-resytech-activity-id, data-resytech-equipment-id, or data-resytech-gift-card and binds click handlers to them. See Embedding the Booking UI for details.

Methods

showUI(options?)

Opens the booking modal. If the modal is already open, this is a no-op.

client.showUI();

With pre-fill options:

client.showUI({
  activity: 'activity-uuid',
  equipment: [
    { equipmentUuid: 'equipment-uuid', quantity: 1 }
  ],
  date: '2025-07-15',
  durationMins: 120,
  timeSlotStart: '2025-07-15T14:00:00',
  timeSlotEnd: '2025-07-15T16:00:00',
  timeSlotPrice: 199.99
});

equipment is an array — each entry can include a quantity and add-ons:

client.showUI({
  activity: 'activity-uuid',
  equipment: [
    {
      equipmentUuid: 'single-kayak-uuid',
      quantity: 2,
      addons: [
        { addonUuid: 'dry-bag-uuid', quantity: 2 }
      ]
    }
  ]
});

CheckoutRequest Options

PropertyTypeDescription
locationstringLocation UUID. Auto-filled from config.
activitystringActivity UUID to pre-select
equipmentstring | EquipmentSelection[]Equipment selections to pre-fill the cart (see EquipmentSelection). A single equipment UUID can be passed as a string — shorthand for [{ equipmentUuid: <uuid>, quantity: 1 }].
equipmentCategorystringEquipment category UUID to filter the activity's selection page to. Requires activity. On activities with equipment mixing enabled, the multi-item picker shows only that category's section; on standard activities the equipment card list is narrowed to the category's members. Ignored when equipment is also provided, and silently ignored (unfiltered page) when the category has no equipment on the activity.
showCalendarPricingbooleanShow per-day starting prices inside the date calendar. Default true; pass false for a plain availability-only calendar.
showSeatsAvailablebooleanPass false to hide remaining seat counts on time slots and the tour guest selector for this session. Omit to follow the location's Show Seats Available checkout setting. When that setting is off, counts are stripped from the API response server-side, so true cannot re-enable them. "Fully booked" always shows.
datestringDate to pre-select (format: YYYY-MM-DD)
durationMinsnumberDuration in minutes
timeSlotStartstringTime slot start (ISO 8601 datetime)
timeSlotEndstringTime slot end (ISO 8601 datetime)
timeSlotPricenumberPrice for the selected time slot
lockSchedulebooleanWhen true, hides the "Choose another date/time" link inside the modal so the customer can't swap the slot you've pre-selected. The modal runs in SchedulingOnly nav mode, so suppressing the only crumb effectively hides the navbar entirely. Default false.
presentation'modal' | 'drawer'Presentation override for this open only. Parent-side chrome only — it is stripped before the payload reaches the booking UI.
giftCardGiftCardSelectionOpens the modal in gift card purchase mode instead of the activity flow. Presence is the discriminator — when set (even to {}), the activity/equipment fields are ignored. See GiftCardSelection and the Gift Card Mode walkthrough.
voucherVoucherSelectionOpens the modal to an Activity Voucher flow: the redeem page for a code, or an offer's purchase page for a slug. Activity/equipment fields are ignored; giftCard wins if both are set. See VoucherSelection and the Activity Voucher Mode walkthrough.
EquipmentSelection
PropertyTypeRequiredDescription
equipmentUuidstringYesEquipment UUID
quantitynumberYesHow many of this equipment to add to the cart
addonsAddonSelection[]NoAdd-ons to attach to this equipment
AddonSelection
PropertyTypeRequiredDescription
addonUuidstringYesAdd-on UUID. Must belong to the parent equipment.
quantitynumberYesHow many of this add-on to attach

Add-on availability is date/time-specific: operators can black out add-ons for a window, in which case the eligible-addons response returns them with available: false and a customer-facing unavailableMessage to display. Don't submit unavailable add-ons — the cart rejects them with AddonNotAvailable and the same message. See Custom Booking Flow — Add-on availability and blackouts for the full contract; the <resytech-addon-selector> component handles it automatically.

GiftCardSelection

All properties are optional. Pass an empty object (giftCard: {}) to open the gift card form with no pre-fills.

PropertyTypeDescription
amountnumberGift card amount. Matches a flat amount when configured by the operator; otherwise the form switches to custom-amount mode (if allowed).
purchaser.namestringPurchaser's full name
purchaser.emailstringPurchaser's email
purchaser.phonestringPurchaser's phone number
recipient.namestringRecipient's name
recipient.emailstringRecipient's email
recipient.messagestringCustom message sent with the gift card
client.showUI({
  giftCard: {
    amount: 100,
    purchaser: { name: 'Alex Morgan', email: 'alex@example.com' },
    recipient: { name: 'Jamie Lee', email: 'jamie@example.com', message: 'Happy birthday!' }
  }
});

Gift card purchases emit giftcard:complete / giftcard:close events — see Checkout Events.

VoucherSelection

Needs a slug (purchase page) or a code (redeem page). code wins when both are present; pass code: '' to open the redeem page empty. The rest are optional purchase-form pre-fills.

PropertyTypeDescription
slugstringOffer slug to sell
codestringVoucher code to check on the redeem page (AV + 32 hex; a malformed value is ignored)
quantitynumberNumber of vouchers to pre-select
purchaser.namestringPurchaser's full name
purchaser.emailstringPurchaser's email
recipient.namestringRecipient's name
recipient.emailstringRecipient's email (switches the form to gift mode)
client.showUI({ voucher: { slug: 'black-friday-kayak', quantity: 2 } });
client.showUI({ voucher: { code: 'AV0123456789ABCDEF0123456789ABCDEF' } });

A voucher purchase emits voucher:complete; a redemption is a booking and emits checkout:complete — see Checkout Events.

Presentation: modal or drawer

The default presentation is the full-screen modal — a fixed overlay covering the whole viewport with the booking card centred inside it. This is unchanged, and every existing embed keeps it without any configuration.

Set presentation: 'drawer' to open the booking UI as a side panel instead: a narrow drawer pinned to the left or right edge of the viewport, sliding in over a dimmed backdrop.

const client = new ResytechClient({
  locationId: 'abc123-def456',
  baseUrl: 'https://booking.yourdomain.com',
  presentation: 'drawer',
  drawer: {
    side: 'right',
    width: '460px',
    fullScreenBelow: 640,
    showCloseButton: true
  }
});

Or on the script tag, for auto-initialized embeds:

<script
  src="https://js.resytech.com/latest/resytech.js"
  data-resytech-location-id="YOUR_LOCATION_ID"
  data-resytech-base-url="https://booking.yourdomain.com"
  data-resytech-presentation="drawer"
  data-resytech-drawer-side="right"
  data-resytech-drawer-width="460px"
  data-resytech-drawer-full-screen-below="640"
  data-resytech-drawer-close-button="true">
</script>
DrawerOptions
PropertyTypeDefaultDescription
side'right' | 'left''right'Viewport edge the panel is pinned to
widthstring'460px'Panel width as any CSS length ('32rem', '30vw', …). Never exceeds the viewport width.
fullScreenBelownumber640Below this viewport width (px) the panel fills the screen and the floating close button is hidden — the booking UI's own header close button takes over. Pass 0 to always keep the configured width.
showCloseButtonbooleantrueRender the circular close button on the backdrop beside the panel
Choosing a presentation per open

presentation is also accepted on showUI() and as a data-resytech-presentation attribute, so a site that uses the modal everywhere can use the drawer on one surface:

client.showUI({ activity: 'activity-uuid', presentation: 'drawer' });
<button data-resytech-activity-id="activity-uuid"
        data-resytech-presentation="drawer">
  Book Now
</button>

setPresentation('drawer' | 'modal') changes the client-level setting for subsequent opens, and getPresentation() reads it back.

Behaviour notes
  • The panel is narrow, so the booking UI renders in its mobile layout — the same layout customers already see on phones.
  • The parent page owns the backdrop in drawer presentation, so backdrop clicks are handled directly rather than reported from the iframe. closeOnBackdropClick and closeOnEscape work exactly as they do for the modal.
  • The slide animation is skipped for visitors with prefers-reduced-motion: reduce.
  • The drawer only injects its stylesheet when it is actually used — a modal-only embed adds nothing to the page.

Locking the schedule

When the parent page has already chosen the slot (via your own calendar UI or a third-party scheduler), set lockSchedule: true so the customer can't navigate back to pick a different time from inside the modal:

client.showUI({
  activity: 'kayak-uuid',
  equipment: [{ equipmentUuid: 'single-kayak-uuid', quantity: 1 }],
  date: '2025-07-15',
  durationMins: 120,
  timeSlotStart: '2025-07-15T10:00:00',
  timeSlotEnd: '2025-07-15T12:00:00',
  timeSlotPrice: 89.99,
  lockSchedule: true
});

The modal always runs in SchedulingOnly nav mode, where the only crumb is the "Choose another date/time" link. lockSchedule removes that crumb, leaving an empty navbar that hides itself.

When showUI() is called it:

  1. Creates a fixed-position overlay covering the entire viewport with a dark blurred backdrop
  2. In drawer presentation, adds the side panel over that overlay and slides it in
  3. Displays a loading spinner while the iframe loads
  4. Loads the booking UI in an iframe — filling the overlay in modal presentation, or the panel in drawer presentation
  5. Locks body scroll (document.body.style.overflow = 'hidden')
  6. Emits the checkout:open event

close()

Closes the booking UI, removes the iframe and overlay, and restores body scroll. In drawer presentation the panel slides out first, and the DOM is removed once the animation finishes.

client.close();

Emits the checkout:close event. If the modal is not open, this is a no-op.

on(event, callback)

Registers an event listener.

client.on('checkout:complete', (data) => {
  console.log('Booking confirmed:', data.confirmationCode);
});
ParameterTypeDescription
eventstringEvent name (see Checkout Events)
callback(data: any) => voidFunction called when the event fires

off(event, callback)

Removes a previously registered event listener. You must pass the same function reference used with on().

function onComplete(data) {
  console.log('Done:', data);
}

client.on('checkout:complete', onComplete);
// Later...
client.off('checkout:complete', onComplete);
ParameterTypeDescription
eventstringEvent name
callback(data: any) => voidThe same function reference passed to on()

setOtaCode(otaCode)

Sets the OTA attribution code at runtime. Pass null or undefined to clear it. Takes effect on the next showUI() call — the iframe URL is built at open time, not edited live. See OTA Partner Integration.

client.setOtaCode('viator');
// later, switch partners
client.setOtaCode('getyourguide');
// or clear it
client.setOtaCode(null);
ParameterTypeDescription
otaCodestring | null | undefinedOTA code from the operator, or null/undefined to clear

Useful when:

  • Creating a ResytechClient programmatically without knowing the OTA code yet
  • Configuring the auto-init'd window.resytech instance from a script that loads after resytech.js
  • Running multiple ResytechClient instances on one page that target different OTA partners

getOtaCode()

Returns the currently configured OTA code, or undefined if none is set.

const code = client.getOtaCode(); // "viator" | undefined

updateConfig(config)

Merges new configuration values into the current config. Does not affect an already-open modal.

client.updateConfig({
  debug: true,
  theme: { primaryColor: '#22c55e' }
});
ParameterTypeDescription
configPartial<ResytechClientConfig>Properties to merge

destroy()

Closes the modal (if open), removes the window.message listener, and clears all event listeners. Call this when you no longer need the client (e.g. in a SPA on route change).

client.destroy();

When showUI() is called, the widget:

  • Creates a fixed-position overlay at z-index: 2147483645 covering the full viewport (100dvw x 100dvh)
  • Applies a dimmed backdrop (rgba(0, 0, 0, 0.5)) with a blur(4px) backdrop filter
  • Shows a loading spinner centered in the overlay while the iframe loads
  • Loads the booking UI in an iframe that fills the entire overlay. The booking UI's own internal layout centers a white card; the transparent area around it shows the dimmed/blurred backdrop through the iframe. The iframe fades in (opacity 0 -> 1 over 300ms) once the checkout-ready message is received from the iframe (or after a 3-second timeout)
  • Locks body scroll to prevent background scrolling
  • The iframe has the allow="payment" attribute for Stripe payment integration

Dismiss Behavior

By default, the modal can be dismissed three ways:

  1. The X button in the booking UI header
  2. Pressing Escape — disable with closeOnEscape: false
  3. Clicking outside the booking card (on the dimmed backdrop area) — disable with closeOnBackdropClick: false

The X button always closes regardless of config. ESC and click-outside are detected by the booking UI inside the iframe and reported to the parent via PostMessage with a reason field; the lib gates close on the corresponding config option. See Checkout Events.

Full Example

<!DOCTYPE html>
<html>
<head>
  <title>My Rental Shop</title>
</head>
<body>
  <h1>Kayak Rentals</h1>

  <button id="book-btn">Book a Kayak</button>

  <script src="https://js.resytech.com/latest/resytech.js"></script>
  <script>
    const client = new ResytechClient({
      locationId: 'YOUR_LOCATION_ID',
      baseUrl: 'https://booking.yourdomain.com'
    });

    // Listen for completed bookings
    client.on('checkout:complete', (data) => {
      alert('Booking confirmed! Code: ' + data.confirmationCode);
    });

    // Open modal on button click
    document.getElementById('book-btn').addEventListener('click', () => {
      client.showUI({
        activity: 'kayak-activity-uuid',
        date: '2025-08-01'
      });
    });
  </script>
</body>
</html>

On this page