Developers
The same API the application runs on.
Every screen in Data Accommodation is built on a versioned REST API. Your systems can use it too — to sync a data warehouse, connect a door-lock or accounting system, or automate the work your team does by hand.
Overview
The API speaks JSON over HTTPS under the /api/v1 prefix. The full, always-current reference — every endpoint, parameter and response schema — is generated from the running code as OpenAPI and published at the API reference, with the raw specification alongside it.
Every response carries an x-request-id header. Send your own x-request-id to correlate calls with your logs; quote it when you contact support.
Authentication
Server-to-server integrations authenticate with an API key sent as a bearer token. Keys start with hos_, are shown once when created, and are stored only as a SHA-256 hash.
- Scopes. A key carries an explicit list of permissions — the same permissions used by staff roles, such as
reservations:read— and can do nothing else. - Property pinning. A key can be limited to a single property. Select the property for property-level calls with the
x-property-idheader. - Expiry and revocation. Keys can be given an expiry date and revoked at any time; last use is recorded. Actions taken with a key are written to the audit log as that key.
curl https://business.dataaccommodation.com/api/v1/reservations?page=1&pageSize=50 \
-H "Authorization: Bearer hos_…" \
-H "x-property-id: <property id>"API keys are created by an organisation administrator under Settings. Included in the Enterprise plan.
Idempotency
Send an Idempotency-Key header on any POST, PUT, PATCH or DELETE to make it safe to retry — essential for payments and anything that moves money.
- Repeating a request with the same key and the same body returns the original status and response, with the header
idempotent-replayed: true. Nothing is executed twice. - Reusing a key with a different body is refused with
409and the codeIDEMPOTENCY_KEY_REUSED. - If the first request is still running, the duplicate receives
409 IDEMPOTENCY_IN_PROGRESSwithretrySafe: true. If the first request failed, the key is released so you can retry.
curl -X POST https://business.dataaccommodation.com/api/v1/folios/<folio id>/payments \
-H "Authorization: Bearer hos_…" \
-H "x-property-id: <property id>" \
-H "Idempotency-Key: 6f1c2f0e-8f7e-4a57-9d8b-2b1d3c1e9a44" \
-H "Content-Type: application/json" \
-d '{ "method": "CASH", "amount": "4500.00" }'Endpoint paths and bodies in these examples are illustrative; the API reference is authoritative.
Pagination
List endpoints that can return many records are paginated with page (from 1) and pageSize query parameters, and return the rows with the totals you need to walk every page:
{
"data": [ … ],
"page": 1,
"pageSize": 50,
"total": 214,
"totalPages": 5
}Smaller collections — room types, outlets, tax rules — are returned as a plain array.
Errors
Errors use standard HTTP status codes and a consistent body: a stable machine-readable code, a message written for the person who will read it, field-level details for validation failures, a retrySafe flag and the requestId.
{
"statusCode": 422,
"code": "VALIDATION_ERROR",
"message": "The request contains invalid fields. Nothing was saved. Fix the highlighted fields and retry.",
"details": [{ "path": "checkOut", "message": "Check-out must be after check-in" }],
"retrySafe": false,
"requestId": "…"
}Conflicts caused by a concurrent change return 409 with retrySafe: true: nothing was saved, and the same request can be sent again.
Rate limits
Requests are rate-limited per client IP address over a one-minute window. By default the general limit is 600 requests per minute; sign-in, password, invitation and public booking endpoints have a much smaller bucket. Exceeding a limit returns:
{ "statusCode": 429, "code": "RATE_LIMITED", "message": "Too many requests. Wait a minute and retry.", "retrySafe": true }Webhooks
Register an HTTPS endpoint and choose the events it should receive. Each delivery is a JSON POST containing the event id, type, time, organisation, property and data. Deliveries are at-least-once: use the event id to ignore duplicates.
- Signed.
x-hos-signatureisv1=followed by the hex HMAC-SHA256 of`${timestamp}.${rawBody}`, keyed with your endpoint secret (whsec_…, shown once).x-hos-timestampcarries the Unix time in seconds; reject deliveries more than five minutes old. - Identified.
x-hos-eventnames the event,x-hos-event-idis stable across retries, andx-hos-deliveryidentifies the delivery. - Retried. Any non-2xx response or timeout is retried with exponential back-off, capped at six hours between attempts, up to eight attempts. Recent deliveries — status, response code and last error — are visible per endpoint.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(rawBody, headers, secret) {
const ts = headers['x-hos-timestamp'];
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = 'v1=' + createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex');
const given = String(headers['x-hos-signature'] ?? '');
return given.length === expected.length && timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}Included in the Business and Enterprise plans. Webhooks are managed by an organisation administrator under Settings.
Event types
66 event types can be delivered to webhooks today, grouped by the part of the hotel they come from:
- digital checkin
digital_checkin.submitted- event booking
event_booking.confirmed- feedback
feedback.received- folio
folio.charge_postedfolio.charge_voidedfolio.closed- guest
guest.checked_inguest.checked_outguest.createdguest.mergedguest.request_created- housekeeping
housekeeping.completedhousekeeping.task_assignedhousekeeping.task_completedhousekeeping.task_createdhousekeeping.task_inspectedhousekeeping.task_started- inventory
inventory.expiringinventory.movementinventory.stock_low- invoice
invoice.createdinvoice.issuedinvoice.paidinvoice.voided- kds
kds.ticket_readykds.ticket_updated- maintenance
maintenance.resolvedmaintenance.ticket_assignedmaintenance.ticket_createdmaintenance.ticket_resolvedmaintenance.ticket_verified- night audit
night_audit.completed- payment
payment.completedpayment.createdpayment.failedpayment.refunded- pos
pos.order_acceptedpos.order_cancelledpos.order_createdpos.order_deliveredpos.order_folio_reversedpos.order_out_for_deliverypos.order_paidpos.order_preparingpos.order_readypos.order_room_chargedpos.order_sentpos.order_servedpos.order_voided- procurement
procurement.grn_receivedprocurement.po_approved- reservation
reservation.cancelledreservation.createdreservation.modifiedreservation.no_showreservation.reinstatedreservation.room_assignedreservation.updated- room
room.blockedroom.status_changedroom.unblocked- task
task.assignedtask.completedtask.createdtask.escalated- user
user.invited
Building an integration?
Tell us what you are connecting and we will help you design it against the API.