Skip to content

Booking page for developers

Three things you can do from your own website or software. Each has its own requirement, so you only need what you use.

I want toIt needs
Show the booking page on my own websiteThe Website plugin add-on, and your website listed under Allowed websites (up to 2).
Hear about each booking as it happensA plan with webhooks (Pro and up), or the API access add-on (which includes 2 webhooks). Booking events are one of the things a webhook can send.
Read or manage bookings from my softwareAn API key: included with the Business and Agency plans, or the API access add-on on other plans.

See the add-ons on the Pricing page. Customers always book on our page; we never ask your visitors for anything beyond the booking form.

1. Put the booking page on your website

Open your booking page in the studio, go to Website, add your site under Allowed websites, then paste one line where you want the button or the page:

<script src="https://freeqrstudio.in/embed.js"
        data-page="your-page"
        data-mode="button"
        data-text="Book now"
        async></script>

It works on any website that lets you add a script, such as WordPress, Wix, Shopify or plain HTML. The studio also has ready-made pieces to copy.

AttributeDefaultWhat it does
data-pagerequiredThe short name in your booking page's address (the part after /book/).
data-modebuttonbutton opens the page over your site. inline puts the page where the tag is. floating puts a round button in the corner. none adds no button, so you open it from your own (see below).
data-textBook nowThe words on the button.
data-color#6d4affThe button colour, as #rrggbb.
data-shaperoundedrounded, pill or square.
data-sizems, m or l.
data-stylesolidsolid, outline or 3d.
data-iconnoneUp to 4 characters shown before the words, such as an emoji.

If your website is not in the Allowed websites list, the booking page will not open inside it: your visitors see an empty box. Add the exact address, such as https://www.yourshop.com.

Know when someone books

When a visitor finishes a booking, the page tells your site with a browser event. Use it to count conversions in your own analytics:

window.addEventListener("qrbooking:booked", (e) => {
  // e.detail.ref is the booking reference, e.detail.page is your page's short name
  gtag("event", "booking", { ref: e.detail.ref });
});

Open it from your own button

<script src="https://freeqrstudio.in/embed.js" data-page="your-page" data-mode="none" async></script>
<button onclick="QRBooking.open()">Reserve a table</button>

With data-mode="none" the script adds no button of its own, only QRBooking.open() and QRBooking.close(), so any button, link or menu item on your site can open the booking page over it.

Language

If you switched on Hindi or Malayalam (part of Booking extras), a visitor can pick the language on the page, and the page starts in their browser's language. To link straight to one, add ?lang=hi or ?lang=ml to the page address.

2. Hear about each booking: webhooks

In Dashboard, Integrations, add a webhook address and tick the booking events. We send a signed message to your address each time:

These messages contain the customer's name, phone and email, so they are off until you tick them.

POST your-address
Content-Type: application/json
X-QRStudio-Event: booking.created
X-QRStudio-Delivery: dl_…
X-QRStudio-Signature: t=1762080000,v1=<hex>

{
  "id": "evt_…",
  "type": "booking.created",
  "created": "2026-11-02T09:30:00.000Z",
  "data": {
    "booking": { "ref": "BK-7K3M9", "page_id": "…", "service": "Haircut", "person": "Asha",
                 "start": 1762418400, "end": 1762421100, "when": "Thu 6 Nov, 11:30 am",
                 "price": 600, "deposit": 100 },
    "customer": { "name": "Priya Nair", "phone": "+919876543210", "email": "[email protected]" }
  }
}

Times are in seconds since 1970 (UTC). booking.moved adds previous_start, and booking.cancelled adds cancelled_by.

Check a message is really from us

The signature is an HMAC-SHA256 of <t>.<exact body> with your webhook's secret. Reject anything older than 5 minutes.

import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody is the exact text of the request (do not parse it first).
function isFromUs(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.trim().split("=")));
  const t = Number(parts.t);
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return false; // too old
  const expected = createHmac("sha256", secret).update(t + "." + rawBody).digest("hex");
  const a = Buffer.from(expected), b = Buffer.from(parts.v1 || "");
  return a.length === b.length && timingSafeEqual(a, b);
}

Answer with any success code (200 to 299) quickly, and do the real work afterwards. If your address does not, we try again after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours (six tries in all), then give up on that message. Every try carries the same id, so ignore one you have already handled. After 25 failures in a row the webhook is paused and you are emailed.

3. Read and manage bookings from your software

Make a key in Dashboard, Integrations and send it with every call, from your server, never from a web page. See the codes API for how keys work; the same key reaches these addresses too.

curl "https://freeqrstudio.in/api/bookings?when=upcoming" \
  -H "Authorization: Bearer qrs_your_key_here"
CallAddressWhat it does
GET/api/bookings?when=upcomingYour bookings, soonest first. Use when=past for the earlier ones, latest first. Up to 300 are sent.
POST/api/bookings/markMark a booking done or not done. Send { "source": "page", "responseId": 123, "done": true }.
POST/api/bookings/cancelCancel a booking and free its time. Send { "source": "page", "responseId": 123 }. Add "notify": true and "message": "…" to email the customer.
GET/api/booking-pagesYour booking pages: id, address, whether it is live or a draft, and its name.
GET/api/booking-pages/{id}One page with its full setup: services, people, hours, rules and design.
PATCH/api/booking-pages/{id}Change a page (config, design, status live or draft, address). The same checks as the studio apply, including plan and add-on limits.
DELETE/api/booking-pages/{id}Delete a page. Its address stops working. Past bookings stay in your list.

Each booking in the list has responseId (use it to mark or cancel), slot (the date and time in India time), the customer's name, phone and email, and details: the service, who it is with, and the customer's note. The note also holds their answers to your own questions, the number of people, any add-ons and the offer code they used. Bookings made on your forms with a time slot are in the same list, marked with source.

Limits and errors

What is not offered

There is no call to make a booking from your own form. If you want your own look, use the Your code tab in the studio (your own HTML and CSS around our booking form), or the embed above. This keeps double bookings and spam out, because every booking goes through the same time checks.

Something missing? Tell us at [email protected]. The codes API is documented separately.