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/v1

Integrations 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

EndpointsMeaningKey needs
GET /meThe 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-typesRoom types with capacity, photos and number of rooms.Read only
GET /rate-plansRate plans with their cancellation policy and deposit.Read only
GET /extrasExtras 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 /bookingsMake 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 /paymentsRecord 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" }]
  }
}
StatusMeaning
400Something in the request is missing or wrong; details lists the fields.
401No key, or the key is wrong or revoked.
403The key cannot write, or the property is suspended.
404No such booking, room type or other record.
409No room is free, or the booking cannot change in its current state.
422The stay breaks a rule (minimum nights, closed dates) or a plan, extra or promo code is not valid.
429Too many requests. Wait the number of seconds in Retry-After.
500Our 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=aWQ6NDEy

Webhooks

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.createdNew booking
booking.updatedBooking changed (dates, room, status or notes)
booking.cancelledBooking cancelled
booking.checked_inGuest checked in
booking.checked_outGuest checked out
payment.receivedPayment received
payment.refundedPayment refunded
guest.updatedGuest details changed
room.housekeepingRoom cleaning status changed

Headers

Content-Type: application/json
Innlark-Event: booking.created
Innlark-Delivery: 1532
Innlark-Signature: t=1793520902,v1=5f0c…e9a1

Message 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"}'