Delivering Access
Status: Planned (v4). Access delivery is being built. This page describes the intended design so you can plan your integration. Until it ships, retrieve credentials by polling — see Retrieving Access.
Retrieving Access is the pull model — you ask Mosler for a credential when you need it. Delivery is the push model — Mosler sends the credential to you, or directly to the guest, the moment provisioning finishes. No polling required.
There are two delivery modes; you can use either or both.
Mode 1 — Webhook event caller
Register a callback URL for your company/site. When the worker finishes provisioning (or revoking) access, Mosler POSTs a signed event to that URL. Your endpoint records the credential and replies 2xx.
Booking event COMPLETED
│
▼
Mosler ──POST {signed access.provisioned}──► https://your-system/mosler/callback
◄────────────── 200 OK ──────────────
Event payload
| Field | Type | Description |
|---|---|---|
schemaVersion | number | Payload contract version, currently 1. Pin or range-check this — new optional fields may be added under the same major. |
event | string | access.provisioned, access.revoked, or access.failed |
referenceId | string | Your booking reference |
occurredAt | string | ISO 8601 UTC timestamp |
booking | object | { id, status, startDate, endDate } |
access[] | array | Same per-device shape as GET …/access — type, passcode/cardId, device/room/bed, validity. Present on access.provisioned / access.revoked. |
error | object | Only on access.failed: { code, message } describing why provisioning failed (e.g. gateway offline, no device bound, room mismatch). Replaces access[]. |
{
"schemaVersion": 1,
"event": "access.provisioned",
"referenceId": "RES-20250718-001",
"occurredAt": "2025-08-01T08:30:02.114Z",
"booking": {
"id": "6576fe78632cfff91c62a3c2",
"status": "active",
"startDate": "2025-08-01T08:30:00.000Z",
"endDate": "2025-08-05T05:30:00.000Z"
},
"access": [
{
"deviceName": "Room 101 Door",
"roomNumber": "101",
"bedNumber": null,
"type": "passcode",
"passcode": "482910",
"validFrom": "2025-08-01T08:30:00.000Z",
"validUntil": "2025-08-05T05:30:00.000Z"
}
]
}
An access.failed event carries an error object instead of access[]:
{
"schemaVersion": 1,
"event": "access.failed",
"referenceId": "RES-20250718-001",
"occurredAt": "2025-08-01T08:30:02.114Z",
"booking": {
"id": "6576fe78632cfff91c62a3c2",
"status": "active",
"startDate": "2025-08-01T08:30:00.000Z",
"endDate": "2025-08-05T05:30:00.000Z"
},
"error": {
"code": "NO_DEVICE_BOUND",
"message": "No lock is mapped to room 101 at this site."
}
}
Verifying the signature
Each call carries an X-Mosler-Signature header: an HMAC-SHA256 of the raw request body, keyed with the callback secret issued alongside your callback URL. Recompute it and compare before trusting the payload.
import crypto from 'crypto';
function isValid(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody) // the exact bytes received
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
}
Delivery guarantees
- Retries: non-
2xxresponses (and timeouts) are retried with exponential backoff. After the final attempt the event is dead-lettered and surfaced for manual replay. - Idempotency: retries reuse the same payload. De-duplicate on (
referenceId,event,occurredAt) — your endpoint may receive a delivery more than once. - Ordering: not guaranteed under retries. Treat
access.revokedas authoritative over an earlieraccess.provisionedfor the same booking by comparingoccurredAt.
Verifying your endpoint before go-live
Before you take a real booking, confirm your callback URL and signature check work end-to-end. Fire a signed test.ping at your registered URL:
POST /api/v4/callbacks/test
curl -X POST https://api.mosler.io/api/v4/callbacks/test \
-H "apikey: YOUR_API_KEY"
Mosler delivers a test.ping event to your registered callback URL — signed with the same X-Mosler-Signature scheme as real events — and returns the HTTP result it observed from your endpoint:
{
"delivered": true,
"url": "https://your-system/mosler/callback",
"responseStatus": 200,
"signatureScheme": "hmac-sha256"
}
The test.ping body matches the standard envelope so your verifier exercises the real code path:
{
"schemaVersion": 1,
"event": "test.ping",
"occurredAt": "2025-08-01T08:30:02.114Z",
"message": "Mosler callback verification ping."
}
No booking is created. Use this to validate your endpoint and signature verification without touching a real reservation.
Mode 2 — Direct guest messaging
Let Mosler deliver the credential straight to the guest — no callback handling, no app on your side. When provisioning completes, Mosler sends the passcode (or a mobile-key link) to the guest over WhatsApp and/or email using Mosler templates.
To enable it, include guest contact details on the booking and the property's messaging preference is configured in the Mosler Admin portal:
{
reference_id: "RES-20250718-001",
action: "create",
room_number: "101",
guest: {
name: "Jane Smith",
email: "jane.smith@example.com", // → email delivery
phone: "+919991234567" // → WhatsApp/SMS delivery
},
access_type: "PASSCODE"
}
The guest receives a Mosler-branded message with their PIN (or a link to open their mobile key) and the stay window. Revocation on checkout/cancel can likewise notify the guest that access has ended.
Choosing a mode
| Webhook event caller | Direct guest messaging | |
|---|---|---|
| Who receives the credential | Your system | The guest |
| You build | A callback endpoint | Nothing — just send guest contact |
| Best when | You own the guest experience/app | You want Mosler to handle delivery |
You can combine them: receive the callback for your records and have Mosler message the guest.
Setup
Both modes are configured per company/site in the Mosler Admin portal — callback URL + secret for Mode 1, messaging channel preferences for Mode 2. Contact your Mosler representative to enable delivery for your account while this feature is rolling out.