v4/Mosler Direct

Creating Bookings

You drive a guest's whole access lifecycle — grant on booking, revoke on cancel/checkout — by sending booking events to Mosler Direct. One endpoint handles create, update, and cancel; the action field says which.

Events are processed asynchronously: Mosler accepts your event with 202 Accepted and an eventId, then provisions device access in the background. You can poll the event to confirm completion.

v3 → v4 difference: v3 used synchronous REST — POST /api/v3/booking returned the created booking directly. v4 is fully asynchronous: the 202 response only confirms the event was accepted; you must poll the eventId or wait for a push notification to know when access is ready. The reference_id you choose replaces v3's internal booking IDs as the key used across the entire lifecycle — create, update, cancel, and credential retrieval all key off this same value.


Endpoint

POST https://webhook.mosler.in/webhook/t/<your-token>

The token in the URL is the whole credential — no other auth is needed. Prefer not to put the credential in the URL? Use the header alternative: POST /webhook/generic with apikey, X-Company-Id, and X-Site-Id.

Whether you name the site in the body depends on the token's scope: a company-level token carries only your company, so each payload names its own site; a site-level token pins the site in the URL. See identifying the site.

HeaderTypeRequiredDescription
Content-TypestringYesapplication/json
X-Correlation-IdstringNoYour own trace ID, echoed in Mosler logs

Request body

FieldTypeRequiredDescription
reference_idstringYesYour booking identifier. Used for idempotency and to match follow-up events (update, cancel). Reuse the same value across a booking's lifecycle.
actionstringYesLifecycle action — see mapping.
site / site_idstringConditionallyWhich property this booking belongs to. Required on a company-level token URL; ignored on a site-level one. Mosler site _id or your own external_id — see identifying the site.
building / building_idstringNoBuilding reference (Mosler _id or your external_id). Recorded on the event; not used to resolve the room — see building and floor.
floor / floor_idstringNoFloor reference (Mosler _id or your external_id). Recorded on the event; not used to resolve the room — see building and floor.
room / room_numberstringConditionallyRoom name as it appears in the Location API. Required for create / update / checkin / checkout; optional for cancel.
bed / bed_numberstringNoBed name within the room, for shared/dormitory properties.
room_id / bed_idstringNoYour own location ID, if you've attached external IDs. Resolved before name matching — rename-proof.
check_in / start_datestringYesCheck-in date/time. ISO 8601 preferred.
check_out / end_datestringYesCheck-out date/time.
guest.namestringNoGuest full name.
guest.emailstringNoGuest email. Provide email or phone (both recommended).
guest.phonestringNoGuest phone with country code, e.g. +919991234567.
access_typestring | string[]NoCredential type(s) to provision. Default: EKEY. See access types.
card_idstringNoRFID card identifier — required when access_type includes CARD.

Guest contact drives delivery. If you want Mosler to send the credential straight to the guest (see Delivering Access), include guest.phone and/or guest.email.


Identifying the site (company-level URLs)

If you run more than one property you can use one company-level URL for all of them instead of one URL per site. The token then carries only your company, and each payload says which property the booking is for:

{
    "reference_id": "RES-20250718-001",
    "action": "create",
    "site": "6530f9dce0c1bd73ded0d1be",
    "room_number": "101",
    "check_in": "2025-07-18T14:00:00.000Z",
    "check_out": "2025-07-22T11:00:00.000Z"
}

site (alias: site_id) accepts two identifier styles. The shape of the value decides which one is used:

You sendResolved by
Exactly 24 hex characters — a Mosler site _id from the Location APIDirect lookup. The site must belong to your company — another company's id is refused.
Anything elseYour own site external_id, attached to the site and unique within your company.

The two are not tried in sequence: a 24-hex value is only ever looked up as a Mosler _id, so avoid external_id values that happen to be 24 hex characters — they will never match.

Site names are not matched. A human-readable name like "Delhi" will not resolve — use an _id or an external_id.

On a site-level URL the site is already pinned by the token, so any site in the body is ignored. Sending it anyway is harmless, which lets you use the same payload builder for both URL shapes.

Unresolved sites

A site reference Mosler can't match is not an error you have to handle at send time. The event is stored as UNMAPPED, nothing is provisioned, and you still get a 202 — so a property that hasn't been mapped yet never causes your system to disable the webhook:

{
    "success": true,
    "eventId": "a3f9d2e1-84c7-4b56-9f13-0d2e4c8a1b7f",
    "status": "UNMAPPED",
    "message": "Site could not be resolved (site_external_id_not_found) — event stored for review and not processed"
}
Reason in the messageWhat happened
missing_site_refNo site / site_id in the payload, on a company-level URL
site_objectid_not_foundThe 24-hex id doesn't exist
site_cross_companyThe id exists but belongs to another company
site_external_id_not_foundNo site in your company carries that external_id

The event is kept in full. Once the site is mapped, Mosler replays it — nothing is lost and you do not need to resend. If you'd rather catch these yourself, poll the eventId (below) or list events filtered by status.

Building and floor

building / floor (aliases building_id / floor_id) accept the same two identifier styles as site and are always optional. Mosler records them on the event, but room resolution does not use them — room and bed references are already unique within a site, so naming the building or floor cannot change which lock is provisioned. Send them if it makes your payloads easier to trace against your own system; leave them out otherwise.


Action mapping

The action string is normalised — casing and separators (_, -) are ignored.

Your action stringMaps to
create, new, reservationBOOKING_CREATE
cancelBOOKING_CANCEL
checkin, check_in, check-inBOOKING_CHECKIN
checkout, check_out, check-outBOOKING_CHECKOUT
roommove, room_move, roomchangeBOOKING_ROOMMOVE
datechange, date_change, extendBOOKING_DATECHANGE
anything elseBOOKING_UPDATE

Create a booking

curl -X POST https://webhook.mosler.in/webhook/t/<your-token> \
  -H "Content-Type: application/json" \
  -d '{
    "reference_id": "RES-20250718-001",
    "action": "create",
    "room_number": "101",
    "check_in": "2025-07-18T14:00:00.000Z",
    "check_out": "2025-07-22T11:00:00.000Z",
    "guest": {
      "name": "Jane Smith",
      "email": "jane.smith@example.com",
      "phone": "+919991234567"
    },
    "access_type": "EKEY"
  }'
Success — 202 Accepted

The event is validated, stored, and queued. Provisioning happens asynchronously.

{
    "success": true,
    "eventId": "a3f9d2e1-84c7-4b56-9f13-0d2e4c8a1b7f",
    "status": "QUEUED",
    "message": "Event received and queued for processing"
}

Save the eventId.

Validation error — 400 Bad Request
{
    "success": false,
    "errors": [
        "reference_id is required",
        "check_in or start_date is required"
    ],
    "message": "Invalid payload"
}
Duplicate — 202 (idempotent)

If the same reference_id and action arrive within 60 seconds, Mosler returns the original event instead of processing it twice.

{
    "success": true,
    "eventId": "a3f9d2e1-84c7-4b56-9f13-0d2e4c8a1b7f",
    "status": "QUEUED",
    "message": "Duplicate event detected, returning existing event"
}

Update or cancel

Send the same reference_id with a different action. For a cancel, room and date fields are optional — Mosler looks up the booking by reference_id and revokes its access.

curl -X POST https://webhook.mosler.in/webhook/t/<your-token> \
  -H "Content-Type: application/json" \
  -d '{ "reference_id": "RES-20250718-001", "action": "cancel" }'

To change dates or room, send action: "update" (or a date-change/room-move action) with the new values and the original reference_id.


Tracking an event

Provisioning is asynchronous. Poll the event by its eventId to confirm it reached COMPLETED. In production, wait ~5 seconds before the first check.

curl https://webhook.mosler.in/webhook/events/a3f9d2e1-84c7-4b56-9f13-0d2e4c8a1b7f \
  -H "apikey: YOUR_MOSLER_API_KEY"
{
    "success": true,
    "eventId": "a3f9d2e1-84c7-4b56-9f13-0d2e4c8a1b7f",
    "status": "COMPLETED",
    "message": null
}

To see every event for a booking (create, cancel, …) use GET /webhook/events/reference/:referenceId. Full status semantics, retries, and the dead-letter flow are documented in Event Lifecycle.

Once status is COMPLETED, the credential exists. Fetch it via Retrieving Access, or let Mosler push it for you — see Delivering Access.


Common errors

ErrorLikely cause
reference_id is requiredMissing reference_id in body
room or room_number is required for this actionMissing room on a create/update/checkin/checkout
status: UNMAPPED, message Site could not be resolved (…)The payload's site reference didn't resolve — see identifying the site. Returned as 202, not an error status.
site_id is required for this provider (400)A company-level URL was used for a PMS provider (eZee, Hotelogix). Those require a site-level token.
status: FAILED, message Room not foundRoom name doesn't match any room in the site — re-sync via the Location API
status: FAILED, message Site not foundToken/site mismatch — the booking's site isn't configured for this company