Booking API¶
Show live availability and prices from Zitlin on your own website, then finish the booking in one of two ways:
- Send the guest to the booking page. Your website shows the calendar and a Book button, and the booking engine handles the guest's details and payment. See Sending guests to the booking page.
- Take payment yourself. Your website charges the guest with your own payment gateway, then records the paid booking in Zitlin. See How a paid booking works.
This page is for the developer building the website. If you just want a booking page without building anything, use the booking engine on its own.
| Base URL | https://server.zitlin.com/pub/api/v1 |
| Format | JSON. Dates are YYYY-MM-DD, money is a string such as "1000.00" |
| Schema | GET /openapi.json (OpenAPI, no key needed) |
Getting a key¶
Write to support, and name the property and how the website will take bookings. Zitlin issues a key, which is shown once and can't be retrieved later. Send it on every call:
The key identifies the property, so no property id appears in any URL. There are two kinds:
| Key | Can use | For a website that |
|---|---|---|
| Read-only | /availability/ and /quote/ |
Shows prices and sends guests to the booking page |
| Booking | Everything on this page | Takes payment itself |
A read-only key gets a 403 from every /bookings/ endpoint. If the website later starts
taking payment, support can upgrade the same key.
Call this API from your server only
Keep either kind of key out of browser and app code. Browsers are blocked from calling this API directly. If a key leaks, ask support to revoke it and issue a new one.
Test on a test property
Ask for a key on a test property while you build. A key for a live hotel creates real bookings that the hotel will see and prepare for.
Sending guests to the booking page¶
Your website needs only a read-only key, GET /availability/,
GET /quote/ or both, and no payment setup of its own.
Once the guest has chosen, send them to one of these links. Neither link needs a key, and each opens in the guest's browser.
| Where | Field | Use it when |
|---|---|---|
/quote/ |
book_url on each room type |
You have already quoted the stay. It is ready to use |
/availability/ |
book_url_template |
Your Book button sits on the calendar. Fill in the stay yourself |
Fill in the template's {check_in}, {check_out}, {adults}, {children} and
{room_type_id}:
https://server.zitlin.com/pub/engine/your-hotel/?checkin={check_in}&checkout={check_out}&adults={adults}&children={children}&room={room_type_id}&src=website
The guest lands on the booking page with their dates and party filled in and the chosen room listed first. They pick a rate, tap Book, enter their details and pay any deposit there.
These are ordinary booking-engine bookings:
- They arrive On hold on the calendar for the hotel to accept, and any deposit follows the hotel's booking engine settings.
- Zitlin emails the guest and the hotel, so your website sends nothing.
POST /bookings/andGET /bookings/{reference}/don't apply, because your website has noreferencefor them.
Both fields are null when the booking page can't take the booking: the hotel's booking engine
is off or needs its payment setup finished, or the property can't take more bookings this
month. book_url is also null for a room type with nothing left. Hide the Book button
when you get null.
Without the API¶
If your website doesn't need to show live prices, you don't need a key or any of the endpoints on this page. Add a date picker and open the same link with the guest's dates:
https://server.zitlin.com/pub/engine/your-hotel/?checkin=2026-12-01&checkout=2026-12-03&adults=2&children=0&src=website
The guest sees the prices and available rooms on the booking page. Leave out room, or add
it if you already know the room type's id.
How a paid booking works¶
For a website that charges the guest itself. This needs a booking key.
| Step | Call | Why |
|---|---|---|
| 1. Show the calendar | GET /availability/ |
Availability and nightly prices |
| 2. Guest picks dates | GET /quote/ |
Rates and exact totals for that stay |
| 3. Guest enters their details | ||
| 4. Just before charging | POST /bookings/check/ |
Would this booking go through? |
| 5. Charge the guest | Your gateway | |
| 6. Record the booking | POST /bookings/ |
Creates the booking in Zitlin |
Your website doesn't need the hotel's booking engine switched on for this.
Don't skip step 4. If it returns available: false or an error, don't charge
the guest. Only step 6 happens after the money has moved, so step 4 is your last
chance to avoid a refund.
Rules to build around¶
A paid booking is accepted even if the room has gone. If the last room sells
between steps 4 and 6, POST /bookings/ still creates the booking and returns
overbooked: true. The booking has no room assigned, is tagged Overbooked,
and the hotel is emailed so they can arrange a room or contact the guest.
A 409 or 422 from POST /bookings/ means the guest has no booking. If you
have already charged them, refund them.
reference must be unique for each order. Use your order id. Sending the same
reference again returns the original booking with a 200 and creates nothing, so
retry freely after a timeout or a 5xx. The rest of the request isn't compared,
so reusing a reference for a different order returns the old booking.
One room per call. For a two-room order, make two calls with two references,
such as order_123-1 and order_123-2.
A payment is required. payment.amount must be above zero and no more than the
total. It can be a partial prepayment, and the rest is the balance due at the
property.
Show levies to the guest. Charges like Green Tax or a city tax are not in any
total. They are billed at the property, and listed in
levies_due_at_property.
You email the guest. Zitlin sends the guest nothing. Send your own confirmation,
and include status_url if you want them to have a live status page.
Cancellations and refunds stay with the hotel. A cancelled booking shows as
cancelled on GET /bookings/{reference}/. Zitlin doesn't
refund the guest or notify your website.
Rounding
total is what you should charge. The hotel's invoice can differ from it by
up to a cent per night when a night's tax isn't a whole number of cents, such
as a price with decimals or compounding taxes. A fully prepaid guest can
therefore show a balance of a few cents on the invoice.
GET /availability/¶
The calendar: every room type sold online, night by night.
| Parameter | Required | |
|---|---|---|
date_from |
Yes | First night |
date_to |
Yes | Last night, inclusive. At most 92 nights per call |
adults |
No | Party size to price, 1–20. Default 2 |
{
"currency": "INR",
"check_in_time": "14:00",
"check_out_time": "11:00",
"accepting_bookings": true,
"book_url_template": "https://server.zitlin.com/pub/engine/your-hotel/?checkin={check_in}&checkout={check_out}&adults={adults}&children={children}&room={room_type_id}&src=website",
"room_types": [
{
"room_type_id": 12,
"name": "Deluxe",
"photo_url": "https://...",
"max_occupancy": 3,
"nights": [
{
"date": "2026-12-01",
"available": 4,
"open": true,
"closed_to_arrival": false,
"closed_to_departure": false,
"min_stay": 1,
"max_stay": 30,
"price": "5500.00"
}
]
}
]
}
| Field | |
|---|---|
accepting_bookings |
If false, the property can't take new bookings this month. Don't offer booking |
book_url_template |
The link to the booking page, or null if it can't take the booking |
available |
Rooms left that night |
open |
False if this party can't have the room, or the night is past the booking window |
closed_to_arrival, closed_to_departure |
The guest can't check in, or check out, on this date |
min_stay, max_stay |
Nights allowed for a stay arriving on this date |
price |
That night's price including tax, at the room type's own rate. null if this party can't have the room |
A night can be sold only if available is above 0 and open is true. Dates outside
the bookable window are left out, and room_types is empty if nothing is left.
Cache this for about a minute rather than calling it on every page view.
/quote/ and /bookings/check/ give the final
answer.
GET /quote/¶
The rates and exact totals for one stay.
| Parameter | Required | |
|---|---|---|
check_in, check_out |
Yes | The stay |
adults |
No | 1–20. Default 2 |
children |
No | 0–6. Default 0. Children don't change the price |
{
"currency": "INR",
"accepting_bookings": true,
"room_types": [
{
"room_type_id": 12,
"name": "Deluxe",
"photo_url": "https://...",
"max_occupancy": 3,
"available": 4,
"rates": [
{
"rate_plan_id": null,
"name": "Room only",
"meal_plan": "Room only",
"total": "11000.00",
"tax": "1000.00"
},
{
"rate_plan_id": 7,
"name": "With breakfast",
"meal_plan": "Breakfast included",
"total": "12100.00",
"tax": "1100.00"
}
],
"book_url": "https://server.zitlin.com/pub/engine/your-hotel/?checkin=2026-12-01&checkout=2026-12-03&adults=2&children=0&room=12&src=website"
}
],
"levies_due_at_property": [
{"name": "Green Tax", "amount": "6.00", "per_guest": true, "per_adult": false}
]
}
totalincludes tax. Pass it back unchanged asexpected_total.rate_plan_idisnullfor the room type's own rate. Pass the id back if the guest picks a plan.- Room types the party doesn't fit are left out. A room type with nothing left has
available: 0, noratesand nobook_url. book_urlsends the guest to the booking page with this stay filled in. You can ignore it if you take payment yourself.- Each levy's
amountis per night: per guest or per adult as flagged, or per room if neither is set.
POST /bookings/check/¶
Would this exact booking go through? Takes the booking body and writes nothing. Call it just before charging the guest.
If available is false, reason says why, such as no rooms left or a minimum stay.
Don't charge the guest. It returns the same errors as POST /bookings/.
POST /bookings/¶
Records a booking the guest has paid for.
{
"reference": "order_123",
"room_type_id": 12,
"rate_plan_id": null,
"check_in": "2026-12-01",
"check_out": "2026-12-03",
"adults": 2,
"children": 0,
"expected_total": "11000.00",
"payment": {"amount": "3000.00", "currency": "INR"},
"guest": {
"name": "Jane Smith",
"email": "[email protected]",
"phone_country_code": "91",
"phone": "98424 01726",
"country": "IN",
"address": "12 Lake Road",
"city": "Jaipur",
"state": "Rajasthan",
"postal_code": "302001",
"notes": "Late arrival"
}
}
| Field | Required | |
|---|---|---|
reference |
Yes | Your order id. Up to 100 characters, no / or control characters |
room_type_id |
Yes | From the quote |
rate_plan_id |
No | From the quote. Leave out for the room type's own rate |
check_in, check_out |
Yes | |
adults |
Yes | 1–20 |
children |
No | 0–6. Default 0 |
expected_total |
Yes | The total from the quote |
payment.amount |
Yes | What you charged. Above zero, no more than the total, up to 2 decimals |
payment.currency |
Yes | Must be the property's currency |
guest.name |
Yes | Up to 255 characters |
guest.email |
Yes | Up to 254 characters |
guest.phone_country_code |
Yes | Such as 91 or +1 |
guest.phone |
Yes | 4–15 digits. Spaces and dashes are fine |
guest.country |
Yes | Two-letter code, such as IN |
guest.address |
No | Up to 500 characters |
guest.city |
No | Up to 200 characters |
guest.state |
No | Up to 100 characters |
guest.postal_code |
No | Up to 64 characters |
guest.notes |
No | Up to 1,000 characters. Shown to the hotel on the booking |
If a guest with this email has stayed before, the booking is added to their existing record, and their saved details aren't changed.
Returns 201 for a new booking, or 200 if the reference already exists:
{
"reference": "order_123",
"booking_reference": "K7M2PQX",
"folio_number": 1042,
"status": "confirmed",
"status_url": "https://server.zitlin.com/pub/engine/status/...",
"total": "11000.00",
"paid": "3000.00",
"balance": "8000.00",
"overbooked": false
}
| Field | |
|---|---|
booking_reference |
The reference to give the guest |
folio_number |
The booking number the hotel sees |
status_url |
A page the guest can open to see their booking's status |
total, paid, balance |
As recorded at booking. They exclude levies, and later payments or changes at the hotel aren't reflected |
overbooked |
True if the room wasn't available when the booking arrived |
GET /bookings/{reference}/¶
Where a booking stands now. Returns the same shape as POST /bookings/, with status
as one of confirmed, on_hold, cancelled or no_show. Returns 404 for an
unknown reference.
Errors¶
Errors look like this. Branch on the status and code, because detail is written for
people and may change.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | INVALID_REQUEST |
The body isn't valid JSON | Fix the request |
| 401 | The key is wrong or revoked, or the property is inactive or restricted | Write to support | |
| 403 | KEY_READ_ONLY |
A read-only key was used on a /bookings/ endpoint |
Use a booking key, or write to support to upgrade this one |
| 404 | Unknown reference | ||
| 409 | PRICE_CHANGED |
The price moved, or the rate is no longer offered | Quote again. If already charged, refund |
| 422 | INVALID_REQUEST |
Bad input or dates, wrong currency, party too large, or the room type isn't sold online | Fix the request. If already charged, refund |
| 429 | Too many calls | Slow down and cache |
For a 422 from a badly formed field, detail is a list showing which field failed.
Limits¶
We apply limits to prevent abuse. If you get a 429 or expect heavy traffic, write to support
for more information.