Developers
The ikan partner API
Create inquiries, read bookings, and receive real-time HMAC-signed webhooks — the same data plane our own portal runs on, scoped to your organisation. Below is everything you need to integrate; access is granted by our team (see the foot of this page).
Authentication
Every request carries an organisation-scoped API key as a bearer token:
Authorization: Bearer ikan_live_xxxxxxxx…Keys are minted by ikan for your organisation and shown to you exactly once. They are never self-served from this page — there is no public key-generation form, by design. A key resolves your organisation automatically, so tenanting is decided by the key, never by request bodies. Keys carry a subset of these scopes, and each endpoint demands one:
inquiries:readinquiries:writebookings:readbookings:approvewebhooks:manage
Every response stamps x-ikan-api-version: 2026-07-11. Keys never grant access to ops or admin surfaces, and never expose supplier net rates or ikan margin — responses carry client-facing figures only.
Endpoints
| Method | Path | Scope | What it does |
|---|---|---|---|
| POST | /api/v1/partner/inquiries | inquiries:write | Create an inquiry (the same NAQ shape the portal uses: date ranges, adults/children + ages, office anchor). Returns { ref }. |
| GET | /api/v1/partner/inquiries?since=&limit=&cursor= | inquiries:read | List your organisation’s inquiries, newest first. `since` filters on updatedAt; paginate with nextCursor. |
| GET | /api/v1/partner/inquiries/{ref} | inquiries:read | Fetch one inquiry by its INQ-… reference (includes shortlist + booking refs). |
| GET | /api/v1/partner/bookings?since=&limit=&cursor= | bookings:read | List bookings, newest first. Client-facing pricing only. |
| GET | /api/v1/partner/bookings/{ref} | bookings:read | Fetch one booking by its ICH-… (or pre-confirmation) reference. |
| POST | /api/v1/partner/bookings/{ref}/approve | bookings:approve | Approve a booking awaiting client approval. 409 when it is not in an approvable state. |
Rate limits
Per key, fixed window:
- Reads — 120 requests / 60s
- Writes & approvals — 20 requests / 60s
Over-limit responses return HTTP 429 with a Retry-After header. Errors use a consistent envelope: { "error": { "code", "message" } }.
Example
# Create an inquiry (org resolved from your key — never the body)
curl -X POST https://ikan-residences.com/api/v1/partner/inquiries \
-H "Authorization: Bearer ikan_live_xxxxxxxx…" \
-H "Content-Type: application/json" \
-d '{
"contactName": "Priya Rao",
"contactEmail": "priya@acme.com",
"assigneeName": "Sam Traveler",
"assigneeEmail": "sam@acme.com",
"city": "Bengaluru",
"checkIn": "2026-09-01",
"checkOut": "2026-10-15",
"dateRanges": [{ "checkIn": "2026-09-01", "checkOut": "2026-10-15" }],
"guests": 1,
"budgetMin": 90000,
"budgetMax": 160000
}'Webhooks
Rather than poll, register an HTTPS endpoint and ikan will POST a signed JSON event to it whenever something changes. Available events:
inquiry.receivedshortlist.sentquote.readybooking.confirmedbooking.amendedbooking.cancelledbooking.status_changedstay.checked_instay.checked_outstatement.issuedrefund.processed
Each delivery carries an x-ikan-signature header of the form t=<unix>,v1=<hmac> — an HMAC-SHA256 of `${t}.${rawBody}` keyed with your endpoint's signing secret. Verify it against the raw request body (before JSON parsing), and reject timestamps outside a five-minute window to close replay:
// Node.js — verify the x-ikan-signature header on a webhook delivery
const crypto = require('crypto');
function verifyIkanSignature(secret, rawBody, header, toleranceMs = 5 * 60 * 1000) {
const m = /^t=(\d+),v1=([a-f0-9]+)$/.exec((header || '').trim());
if (!m) return false;
const t = Number(m[1]);
if (Math.abs(Date.now() - t * 1000) > toleranceMs) return false; // replay window
const expected = crypto.createHmac('sha256', secret)
.update(t + '.' + rawBody) // sign the timestamp WITH the raw body
.digest('hex');
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(m[2], 'hex');
return a.length === b.length && crypto.timingSafeEqual(a, b); // never ===
}A non-2xx response (or a timeout) is retried on a backoff ladder — 1m → 5m → 30m → 2h → 6h — then the delivery is marked exhausted. Each POST also carries x-ikan-event and x-ikan-delivery (a stable id you can use as an idempotency key). Endpoints must be public https:// URLs; private or internal addresses are refused and redirects are never followed. A client-organisation admin can self-serve endpoints from their portal settings.
Test mode
A ikan_test_… key resolves against a sandbox organisation with demo data, so you can build and verify your integration end-to-end before touching live inventory. Test-mode inquiries and webhooks never mix with production data.
Honesty
We describe only what the API actually does today. Where an integration depends on an external gateway that isn't configured, the platform degrades cleanly and records state rather than failing — it never pretends a step happened that didn't.
Request access
There is no self-serve signup — we provision keys per organisation after a short onboarding. Tell us which events you need and we'll get you a live and a test key.