Skip to content

Embed the widget on your site

Paste one snippet and your site shows real availability, then walks a guest through a quote, a hold, and payment — no server code required on your side. Payment happens on a Stripe-hosted checkout page, not inside the widget itself; the booking is confirmed automatically once Stripe tells us the card payment succeeded.

Only want to show availability, with no booking flow at all? A separate, smaller embed does exactly that — see Embed the calendar.

Want to see it running before you paste anything? The live widget demo embeds this exact snippet against a real resource — pick dates, get a quote, create a hold, and pay with a real Stripe-hosted checkout page.

<script async src="https://widget.<domain>/v1.js"></script>
<div
data-booking-widget
data-key="pk_live_..."
data-resource="<RESOURCE_ID>"
></div>

Both lines are required. v1.js finds every [data-booking-widget] element on the page and mounts one widget into each, inside a Shadow DOM — the host page’s CSS can’t leak in, and the widget’s CSS can’t leak out. This works the same inside WordPress, Wix, or hand-rolled HTML.

  • data-key — your pk_live_… or pk_test_… key. Required. A missing or malformed key (wrong prefix, empty) replaces the element with a plain “Booking widget misconfigured — see console” message, visible only to whoever’s building the page, never a guest.
  • data-resource — one resource’s ID. Required. A resource picker (data-resource="all") is not built yet — if you have several bookable resources, embed one widget per resource id, on separate parts of the page, until a picker ships.
  • data-rate-plan-id — optional. Required only if the resource has a room type and more than one rate plan; otherwise the widget’s quote request works without it. There’s no rate-plan picker in the widget yet — set this attribute to choose which one guests see.
  • data-langen or id. Optional, defaults to en.
  • data-fallback-contact — optional. A tel:, mailto:, or WhatsApp link shown to a guest if the widget can’t reach the API. See “Honest failure behavior” below.
  1. Dates. The guest sees a 7-day availability strip, picks a check-in date, and sets a length of stay.
  2. Quote. The widget prices the stay (POST /v1/quotes) and shows a per-night breakdown and total.
  3. Hold. “Continue” turns the quote into a 15-minute hold (POST /v1/holds). A countdown shows how long the guest has left.
  4. Pay. “Pay now” creates a Stripe-hosted Checkout Session for that hold and sends the guest’s browser there — a real page navigation, away from your site, to a page Stripe serves and secures. Nobody’s card details pass through your site or through us.
  5. Return and confirm. Stripe sends the guest back to the same page they started on, either after payment or after cancelling. Confirmation itself happens in the background: once Stripe tells us the payment succeeded, the reservation is marked confirmed automatically. The widget’s own “Payment received” screen means the charge went through and confirmation is on its way — not a claim the widget itself has any way to verify further.

If a guest closes the tab or cancels on Stripe’s page, nothing is charged and the hold stays in place for a few minutes so they can try again.

Five options, all as data-* attributes on the same element. Anything outside these bounds falls back to a safe default — never a broken layout — and logs a console warning for you to see, never the guest.

Attribute Values What happens if it’s invalid
data-accent A 6-digit hex color, e.g. #2563eb Falls back to the default accent
data-radius A number (pixels), clamped to 2–16 Out-of-range values are clamped, not rejected
data-font system, serif, or mono Falls back to system
data-density comfortable or compact Falls back to comfortable

The text color drawn on top of your accent color is always computed automatically for readable contrast — there’s no attribute for it, and none is planned. You pick the accent; we make sure the text on it is legible.

These five options are a fixed, closed set. There’s no free-text font field and no way to change spacing, component shape, or the booking-flow order — that’s deliberate, not a current limitation.

Your dashboard’s widget configurator (under Keys) builds this exact snippet for you from a live preview — pick your key, resource, and theme there, and copy the result instead of hand-typing attributes.

The publishable key and the domain allowlist

Section titled “The publishable key and the domain allowlist”

Your operator contact issues pk_ keys with an allowlist of the domains they’re allowed to run on. If the widget’s request comes from a domain not on that list, the API rejects it, and the widget falls back to its outage message (below) — check the allowlist first if the widget looks “down” only on one domain. The same allowlist check applies to the Stripe-hosted checkout handoff, so a guest is only ever redirected back to an approved domain.

Two different things can go wrong, and the widget treats them differently on purpose:

  • A bad snippet (missing/malformed key, data-resource="all", a missing resource id) is a mistake in your page, not a guest-facing outage. The element is replaced with plain, unstyled text pointing you at the browser console. This is for you, not your guests.
  • A working snippet that can’t reach the API (network failure, rejected key, rate limit) never renders an empty calendar that could look like “no availability” — that would be a silent loss of bookings with no signal anyone could act on. If every visible day fails, the whole strip is replaced with a “Booking temporarily unavailable” message, plus your contact details if you set data-fallback-contact to a tel:, mailto:, or WhatsApp link. If only some days fail, the ones that succeeded still show real data, and the failed ones show as “unknown” rather than joining either bucket.
  • A hold that expires mid-payment, or a checkout session that fails to create, sends the guest back to a step they can actually retry from — never an optimistic “you’re booked” ahead of a real, confirmed charge.

Paste both lines wherever you want the calendar to appear — anywhere inside <body>, most naturally right where a guest would expect to see dates.

Add a Custom HTML block (in the block editor) or a code-snippet plugin, and paste the exact two lines — don’t split them across two blocks. Many WordPress themes apply global font and color rules to every element on the page; the widget’s Shadow DOM and internal reset stop those from reaching inside it, so it renders the same regardless of your theme.

  • Quickstart — the same flow from your own server, using curl and TypeScript, if you’d rather build your own checkout instead of embedding ours.
  • Embed the calendar — the read-only, availability-only version of this embed, if you don’t want a booking flow at all.