Hospitally Connect · API preview · v1

Every room, booking and request,
one API away.

A REST and webhook API that covers the hotel day. Use it to connect a guest-chat app, a CRS, locks or your own tools to live rooms and bookings.

Preview specification. This is the proposed design of the Hospitally Connect API. Endpoints, fields and event names may change before general availability. The guest-chat connector is a concept design and is not affiliated with or endorsed by Marriott International or any chat provider.

Overview

Base URL https://api.hospitally.app/v1. Requests and responses are JSON and timestamps are ISO-8601 in UTC. Every resource belongs to a property_id, which is set by your token's scope. Every write returns the resulting event ID so you can reconcile against the webhook stream.

ResourceWhat it covers
reservationsBookings, stays, check-in and out, extensions, late check-out
roomsRoom status, assignment, moves, out-of-order
housekeepingTasks, boards, inspections
inventoryItems, par levels, purchase orders
groupsBlocks, pickup, rooming lists, releases
loyaltyMember arrivals and recognition
chatThreads and messages mirrored from connected chat platforms

Authentication

Hospitally uses OAuth 2.0 client credentials. Tokens are scoped per property and per capability (for example reservations:write or chat:read) and last one hour.

curl -X POST https://auth.hospitally.app/oauth/token \
  -d grant_type=client_credentials \
  -d client_id=$CLIENT_ID -d client_secret=$CLIENT_SECRET \
  -d scope="reservations:write rooms:write chat:write"

Send the token as Authorization: Bearer <token>. Writes accept an Idempotency-Key header, and a retried key within 24 hours returns the original result.

Webhooks & signatures

Subscribe an HTTPS endpoint to any event type. Each delivery carries a Hospitally-Signature header, t=<unix>,v1=<hex>, where v1 is HMAC-SHA256 of t + "." + body keyed by your endpoint secret. Reject anything older than five minutes. Deliveries retry with exponential backoff for 24 hours.

import crypto from "node:crypto";

export function verify(req, secret) {
  const [t, v1] = req.headers["hospitally-signature"].split(",").map((p) => p.split("=")[1]);
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${req.rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

Guest-chat connector

The connector links a brand's guest-chat app to live bookings. Inbound messages go to Hospitally. Hospitally matches each one to a reservation, classifies the intent and proposes an action. When staff approve, the action executes and the reply posts back to the thread.

HOOKchat.message.created

Sent by the chat platform (or its adapter) to Hospitally for every guest message.

{
  "type": "chat.message.created",
  "id": "evt_01J9W3K6QX",
  "created_at": "2026-09-29T11:03:12Z",
  "data": {
    "thread_id": "thr_8841",
    "channel": "brand_app",
    "guest": { "loyalty_number": "XXXX4471", "confirmation": "H48221" },
    "text": "Our meetings ran over, can we extend by one more night?"
  }
}
HOOKchat.intent.detected

Emitted by Hospitally after matching and classifying a message. It includes a feasibility check and the proposed call.

{
  "type": "chat.intent.detected",
  "data": {
    "thread_id": "thr_8841",
    "reservation_id": "H48221",
    "intent": "extend_stay",
    "confidence": 0.97,
    "feasible": true,
    "proposal": {
      "method": "POST",
      "path": "/v1/reservations/H48221/extend",
      "body": { "nights": 1, "keep_room": true, "rate_plan": "BAR" }
    },
    "draft_reply": "You're all set for one more night in room 318."
  }
}
POST/v1/chat/threads/{thread_id}/messages

Posts a staff reply back to the guest thread through the connected platform.

{ "text": "You're all set for one more night in room 318.", "author": "front_desk" }

Supported intents: late_checkout, extend_stay, early_checkin, room_move, amenity_request, billing_question and other. Policy rules can auto-approve by intent, tier and forecast occupancy.

Reservations

GET/v1/reservations?status=arriving&date=2026-09-29

Lists reservations filtered by status (arriving, in_house, due_out, checked_out), date, group or loyalty tier. Cursor-paginated.

GET/v1/reservations/{id}

Returns one reservation with guest, room, rate, folio summary and preferences.

{
  "id": "H48221",
  "status": "in_house",
  "guest": { "name": "Marcus Chen", "tier": "gold", "preferences": ["high_floor"] },
  "room": { "number": "318", "type": "K" },
  "arrival": "2026-09-27", "departure": "2026-09-30",
  "rate_plan": "BAR", "group_code": null
}
PATCH/v1/reservations/{id}

Updates mutable fields such as departure_time (late check-out), preferences or notes.

{ "departure_time": "14:00", "reason": "guest_chat" }
POST/v1/reservations/{id}/extend

Adds nights and keeps the same room when it is available. Returns 409 room_unavailable with alternatives when it is not.

// request
{ "nights": 1, "keep_room": true, "rate_plan": "BAR" }
// 200 response
{ "id": "H48221", "departure": "2026-10-01", "room": "318", "rate_total": 309.00, "event_id": "evt_01J9W3P2" }
POST/v1/reservations/{id}/check-in

Checks the guest in. If room is omitted, Hospitally assigns the best ready room for the guest's type and preferences, then triggers mobile-key issuance.

POST/v1/reservations/{id}/check-out

Closes the stay, settles or routes the folio, and marks the room dirty for housekeeping.

Rooms

GET/v1/rooms?status=vacant_dirty&floor=12

Lists rooms with housekeeping status (dirty, in_progress, clean, inspected, out_of_order) and occupancy.

POST/v1/rooms/assign

Assigns or moves a reservation to a room. When rush is true, Hospitally picks the next room to turn and moves it to the top of the attendant's board.

// request
{ "reservation_id": "H48213", "room": "1412", "rush": false, "reason": "early_checkin" }
// 200 response
{ "reservation_id": "H48213", "room": "1412", "key_status": "issued", "event_id": "evt_01J9W41B" }
PATCH/v1/rooms/{number}

Sets housekeeping status or out-of-order windows.

{ "status": "inspected", "inspected_by": "usr_22" }

Housekeeping

POST/v1/housekeeping/tasks

Creates a task such as an amenity delivery, a turndown or an extra clean, with an SLA. The task is routed to the attendant on that floor.

{ "room": "1107", "type": "amenity", "note": "2 towels, 1 crib", "sla_minutes": 15 }
GET/v1/housekeeping/boards?date=2026-09-29

Returns attendant boards with assigned rooms, credits and progress.

Inventory

GET/v1/inventory/items

Returns items with on-hand quantity, par, per-occupied-room usage and tonight's forecast need.

POST/v1/inventory/purchase-orders

Raises a purchase order to a supplier. Deliveries emit inventory.delivered.

{ "item_id": "btw", "quantity": 820, "supplier_id": "sup_linen_01" }

Groups

GET/v1/groups/{code}

Returns block, pickup by night, cutoff, rate and master account.

POST/v1/groups/{code}/rooming-list

Imports a rooming list (JSON or CSV). Hospitally fuzzy-matches names to existing reservations and creates the rest.

POST/v1/groups/{code}/release

Releases unsold rooms from the block back to general inventory.

{ "rooms": 10, "nights": ["2026-10-01", "2026-10-02"] }

Loyalty

GET/v1/loyalty/arrivals?min_tier=gold

Returns elite arrivals with preferences and recognition state (note, amenity, upgrade).

POST/v1/loyalty/recognitions

Records a recognition action for reporting and credit sync.

{ "reservation_id": "H48213", "type": "upgrade", "to_room": "2304" }

Event types

EventWhen
reservation.created / .updated / .cancelledBooking lifecycle
reservation.checked_in / .checked_outArrival and departure
room.status_changedAny housekeeping status change
room.assignedAssignment or move
housekeeping.task.completedTask done, with duration versus SLA
inventory.below_need / inventory.deliveredStock against forecast
group.pickup_changed / group.cutoff_approachingBlocks
forecast.house_fullA night crosses 100% forecast
chat.message.created / chat.intent.detectedGuest chat

Errors & limits

Errors use standard HTTP codes with a JSON body: {"error": {"code": "room_unavailable", "message": "…", "alternatives": [...]}}. The rate limit is 600 requests per minute per property. 429 responses include Retry-After.

CodeMeaning
400 invalid_requestMalformed body or parameters
401 unauthorized / 403 insufficient_scopeToken missing, expired or under-scoped
404 not_foundUnknown reservation, room or thread
409 room_unavailable / 409 house_fullInventory conflict; alternatives included
422 policy_blockedThe property's rules block the action, for example no late check-out on house-full days

Try the chat connector in the demo