Tích hợp

Embedding the direct booking widget

Embed commission-free direct booking on your own site. The server prices it, stop-sell binds it, and the booking arrives as an ordinary reservation.

Cập nhật lần cuối

Who this is for: Owner, Manager — the listing the widget sells is managed under crs.vanityproperty.manage. Where: Reservations → Listings

The booking widget is a small JavaScript bundle you embed on your own website. It quotes live rates and takes bookings against the same inventory the OTAs sell, and it pays no commission to anybody.

A booking made through it is not a second class of reservation. It lands in the PMS as an ordinary booking, with a folio, counted as direct.

The booking widget mounted on a plain host page, showing dates, a promo code field and a Search availability button

The widget on a page that is not Nerve. Everything below the heading is the embed.

Embed it#

  1. Include the bundle on the page.
  2. Call mount() with your ids and a target element.
<div id="book"></div>
<script src="https://cdn.example.com/nerve-booking.js"></script>
<script>
  NerveBooking.mount({
    apiBase: "https://api.nervepms.com",
    orgId: "1a520975-7319-4ac3-9d73-844770cc078a",
    hotelId: "9c59daff-dd5b-4763-b89b-255cd8623080",
    target: "#book",
    hotelName: "Acme Downtown",
    theme: { accent: "#0f766e" }
  });
</script>
Option What it is
apiBase The Nerve API origin the widget calls. Not your own site's origin
orgId Your organisation id
hotelId The property id — the widget sells one property
target A CSS selector for the element to mount into
hotelName The name shown above the form
theme.accent One accent colour, applied to the buttons and links

orgId and hotelId are readable from the property screens under Settings → Hotel.

Ghi chú

There is nothing secret in that snippet. The widget talks to public, unauthenticated endpoints — they take no API key, because a prospective guest has no credential. What protects them is that they can only quote and book, and that the server decides the price.

Quote#

The widget calls one endpoint to price a stay:

GET /api/booking-engine/{orgId}/{hotelId}/quote?arrival=2026-08-22&departure=2026-08-24&promo=CODE

A quote for 2 nights offering Best Available Rate, Non-Refundable and Per-Person Breakfast with totals

Live rates, priced by the server. Non-Refundable is derived from BAR, so it moves when BAR moves.

Every rate plan open for the whole range comes back with its nightly rate, its total, its tax breakdown and its cancellation policy. A promo code is re-resolved on the server — a code that does not resolve leaves the offer at rack rate rather than erroring.

Book#

POST /api/booking-engine/{orgId}/{hotelId}/book

The guest details step with the stay summary, filled contact fields, and an empty Card field

Note the Card field, and note that it is empty. It carries a gateway token from the host site, not a card number, and the booking goes through without it.

Thận trọng

The Card field is a placeholder for a payment-gateway token, not a card number. It is optional and the booking completes without it; the amount is recorded on the folio and no charge is authorised. Do not collect card numbers into it, and do not ship it to guests as-is — wire your gateway's own field in its place before the widget goes on a live site.

A Booking confirmed panel with the reservation reference and the amount paid

The reference the guest keeps. DIR- is the prefix every direct booking gets.

The price cannot be gamed from the browser#

The book request names a rate plan, not a price. There is no total in it to tamper with: the server re-reads the plan and computes nightly × nights, applies the promo it resolves itself, and books at that figure. A client that lies about what it was quoted books at the real price or does not book at all.

This is why the quote endpoint can be public without being a pricing oracle you can argue with.

Restrictions bind the widget — but not all of them#

The widget sells the same inventory as everything else, and it closes when distribution closes — but only two of the five restriction types reach it. Both are enforced on the quote and again on the booking:

Restriction Binds the widget
Stop-sell Yes — the plan disappears from the quote for the whole range
Closed to arrival Yes — on the arrival date
Min-stay No
Max-stay No
Closed to departure No

Cảnh báo

A minimum-stay rule does not stop a shorter direct booking. A three-night minimum applied to a rate plan leaves that plan offerable for a one-night stay through the widget, and the booking completes. Only stop-sell and closed-to-arrival are tested.

If a length-of-stay rule matters commercially, close the dates with a stop-sell rather than a min-stay, or expect to catch the short stays at the front desk. Setting a min-stay and assuming the widget honours it is the failure mode here, because it is honoured on the channels, so the rule looks like it is working.

See restrictions for how the rules are built and applied.

Where the booking arrives#

The CRS bookings list with the widget booking at the top, badged hotel-collect, above channel bookings

The same booking, thirty seconds later, in the PMS. It is listed beside the Booking.com rows and differs from them in one visible way: hotel-collect.

  • It is in Reservations → Bookings with a DIR- reference and hotel-collect payment collection — you take the money, not a channel.
  • It has a stay and an open folio carrying the recorded payment, so the front desk works it like any other arrival.
  • It counts as direct in analytics, which is the number the widget exists to move.

Quan trọng

A direct booking raises no booking.new webhook. That event fans out from channel ingest. A widget booking raises folio.payment.taken for the payment and nothing else, so a consumer subscribed to booking.new alone will never hear about your direct business. See webhooks.

What's next#