Innlark
Innlark API
Connect a website, channel manager or accounting tool to one property: read availability and prices, make and manage bookings, and get a webhook when something changes.
Authentication
The property owner creates keys under Settings → Developers.
Send the key with every request, either as a bearer token or in the x-api-key header. A key can read, or read and write. Keep keys on your server; never put them in a web page or an app.
Authorization: Bearer ilk_…
# or
x-api-key: ilk_…Address
Every path below starts with this address.
https://innlark.com/api/v1Integrations built for the first version keep working: the fields they read and send are still accepted.
Money and dates
Amounts are whole numbers in the smallest unit of the property’s currency (paisa for taka, cents for euro), next to a currency field: 350000 with BDT is ৳3,500. Dates are YYYY-MM-DD in the property’s time zone; times are ISO 8601 in UTC.
Endpoints
| Endpoints | Meaning | Key needs |
|---|---|---|
GET /me | The property and the key you are using. | Read only |
GET /availability?checkIn=&checkOut=&adults=&children= | Free rooms and the price of a stay under each rate plan, with nightly prices and any restriction. | Read only |
GET /room-types | Room types with capacity, photos and number of rooms. | Read only |
GET /rate-plans | Rate plans with their cancellation policy and deposit. | Read only |
GET /extras | Extras a booking can include, by code. | Read only |
GET /calendar?from=&to= | Rooms and the stays that hold them, up to 62 days. | Read only |
GET /bookings?status=&checkInFrom=&checkInTo=&updatedSince= | Bookings, newest first; filter by status, arrival date or last change. | Read only |
GET /bookings/{id or code} | One booking by id or code, with the bill and payments. | Read only |
POST /bookings | Make a booking. It waits for the desk to confirm unless the property confirms online bookings at once. | Read and write |
PATCH /bookings/{id or code} | Change dates or notes, confirm, or cancel. | Read and write |
GET /guests?q= | Search guests by name, phone or email. | Read only |
POST /payments | Record money received or refunded for a booking. | Read and write |
Errors
Errors have an HTTP status and a body like this one. Every response has an X-Request-Id header; quote it when you ask us for help.
{
"error": {
"code": "invalid_request",
"message": "Some fields are missing or invalid.",
"details": [{ "path": "phone", "message": "Required" }]
}
}| Status | Meaning |
|---|---|
400 | Something in the request is missing or wrong; details lists the fields. |
401 | No key, or the key is wrong or revoked. |
403 | The key cannot write, or the property is suspended. |
404 | No such booking, room type or other record. |
409 | No room is free, or the booking cannot change in its current state. |
422 | The stay breaks a rule (minimum nights, closed dates) or a plan, extra or promo code is not valid. |
429 | Too many requests. Wait the number of seconds in Retry-After. |
500 | Our mistake. Try again later. |
Rate limit
Up to 600 requests a minute per key. X-RateLimit-Remaining says how many are left this minute.
Pagination
Lists answer with data, hasMore and nextCursor. For the next page, repeat the request with ?cursor= and the nextCursor value. limit sets the page size (up to 200).
GET /bookings?limit=50
→ { "data": [ … ], "hasMore": true, "nextCursor": "aWQ6NDEy" }
GET /bookings?limit=50&cursor=aWQ6NDEyWebhooks
Add a webhook under Settings → Developers and choose its events. We POST JSON to your https address and expect a 2xx answer within 10 seconds. If not, we try again after 1, 5, 30, 120 and 720 minutes. After 50 failures in a row the webhook is switched off. A message can arrive more than once: use its id to skip repeats.
Events
booking.created | New booking |
booking.updated | Booking changed (dates, room, status or notes) |
booking.cancelled | Booking cancelled |
booking.checked_in | Guest checked in |
booking.checked_out | Guest checked out |
payment.received | Payment received |
payment.refunded | Payment refunded |
guest.updated | Guest details changed |
room.housekeeping | Room cleaning status changed |
Headers
Content-Type: application/json
Innlark-Event: booking.created
Innlark-Delivery: 1532
Innlark-Signature: t=1793520902,v1=5f0c…e9a1Message body
{
"id": "evt_9mQ4cT2xLw8vR1pA",
"event": "booking.created",
"created_at": "2026-11-01T08:15:02.120Z",
"property_id": 1,
"data": {
"booking": {
"id": 412,
"code": "7KQ2MX",
"status": "pending",
"source": "partner",
"checkIn": "2026-12-20",
"checkOut": "2026-12-22",
"nights": 2,
"adults": 2,
"children": 0,
"roomNumber": "204",
"ratePlan": { "name": "Bed and breakfast", "kind": "with_breakfast", "cancellationPolicy": "flexible", "freeCancelDays": 2 },
"guest": { "id": 88, "name": "Nadia Rahman", "phone": "+8801711000111", "email": "nadia@example.com" },
"currency": "BDT",
"bill": { "currency": "BDT", "room": 700000, "extras": 0, "discount": 0, "taxesAdded": 157500, "taxesIncluded": 0, "total": 857500, "paid": 0, "balance": 857500, "taxes": [] }
}
}
}Checking the signature
Innlark-Signature holds t, the time in Unix seconds, and v1, the HMAC-SHA256 of t, a full stop and the raw body, made with your webhook’s signing secret. Compute it yourself, compare in constant time and refuse times more than five minutes old.
Node.js
import crypto from 'node:crypto';
// Express: app.post('/innlark', express.raw({ type: 'application/json' }), handler)
export function verifyInnlark(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(',').map((p) => p.trim().split('=', 2)));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const given = Buffer.from(parts.v1 ?? '');
return given.length === expected.length && crypto.timingSafeEqual(given, Buffer.from(expected));
}
function handler(req, res) {
if (!verifyInnlark(req.body.toString('utf8'), req.get('Innlark-Signature') ?? '', process.env.INNLARK_WEBHOOK_SECRET)) {
return res.sendStatus(400);
}
const event = JSON.parse(req.body);
// event.id, event.event, event.data …
res.sendStatus(200);
}PHP
<?php
$body = file_get_contents('php://input');
$header = $_SERVER['HTTP_INNLARK_SIGNATURE'] ?? '';
$secret = getenv('INNLARK_WEBHOOK_SECRET');
$parts = [];
foreach (explode(',', $header) as $part) {
[$k, $v] = array_pad(explode('=', trim($part), 2), 2, '');
$parts[$k] = $v;
}
$t = (int) ($parts['t'] ?? 0);
$expected = hash_hmac('sha256', $t . '.' . $body, $secret);
if (!$t || abs(time() - $t) > 300 || !hash_equals($expected, $parts['v1'] ?? '')) {
http_response_code(400);
exit;
}
$event = json_decode($body, true);
// $event['id'], $event['event'], $event['data'] …
http_response_code(200);Example: find a room and book it
# 1. Prices and free rooms for two adults, 20–22 December
curl -s "https://innlark.com/api/v1/availability?checkIn=2026-12-20&checkOut=2026-12-22&adults=2" \
-H "Authorization: Bearer $INNLARK_KEY"
# 2. Book the first room type under its bed-and-breakfast plan
curl -s -X POST "https://innlark.com/api/v1/bookings" \
-H "Authorization: Bearer $INNLARK_KEY" -H "Content-Type: application/json" \
-d '{"roomTypeId": 1, "checkIn": "2026-12-20", "checkOut": "2026-12-22", "adults": 2,
"name": "Nadia Rahman", "phone": "+8801711000111", "email": "nadia@example.com",
"ratePlanCode": "BB", "extras": [{"code": "PICKUP", "quantity": 1}], "guestLocale": "bn"}'
# 3. Cancel it
curl -s -X PATCH "https://innlark.com/api/v1/bookings/7KQ2MX" \
-H "Authorization: Bearer $INNLARK_KEY" -H "Content-Type: application/json" \
-d '{"action": "cancel", "cancelReason": "Guest changed plans"}'