Integration
Door locks
For whoever builds the bridge between yaReserva and a hotel’s lock system. Here we are the caller: you expose three endpoints and verify our signature.
How it works
yaReserva does not run locks. What it does is notify the hotel’s bridge at three moments, and the bridge talks to Salto KS, TTLock, Dormakaba/Saflok, Assa Abloy Vostio or whichever system is installed.
| Moment | What we ask for |
|---|---|
| Check-in | Issue a key for the room, valid until check-out time. |
| Room move | Revoke the key for the room being left and issue one for the new room. |
| Check-out | Revoke the booking’s live keys. |
The front desk can also issue an extra key or revoke one from the booking screen.
Rule: If your bridge fails, the front desk carries on
Check-in, check-out and room moves go ahead even when there is no key. The front desk sees a warning and can retry from the booking. A bridge that is down cannot leave a guest waiting at the counter.
Setting up the connection
The hotel creates the connection in its panel, under Settings → Locks. It picks a property, the “Webhook bridge” type and your bridge’s base URL, which must be https and resolve to a public IP. There it receives, once and only once, the credential pair.
| Value | Example | Use |
|---|---|---|
| keyId | yrlock_3f9a1c0b7d2e4a6f8b0c1d2e | Public. Travels in every request we make to you. |
| secret | yrlock_sk_9c1f… (64 hex) | Secret. Used to verify the signature. Never travels inside the requests. |
Rule: The secret is shown only once
The panel shows it when the connection is created and when it is rotated, and never again. The hotel can rotate it whenever it wants; the previous one stops working immediately, so your bridge must be able to change it without a deploy.
Next, the hotel maps each room to the id of its lock in the provider’s system, the “lockId”. That is what we send you so you know which door to open.
Note: There is also the Manual type
It calls nobody. The front desk encodes the card on its own encoder and yaReserva simply notes who has a key and until when. It is for hotels whose lock system has no way to integrate.
How to verify our requests
All three calls are POST with Content-Type: application/json to your base URL plus a path. They all carry the same headers.
X-YR-Lock-Key: <keyId>
X-YR-Lock-Timestamp: <segundos Unix, UTC>
X-YR-Lock-Signature: sha256=<hex(HMAC-SHA256(secret, timestamp + "." + cuerpo))>
User-Agent: yaReserva-Locks/1- Look up the secret by X-YR-Lock-Key.
- Reject if the timestamp differs from your clock by more than five minutes. Sync with NTP.
- Compute the HMAC over the exact bytes of the body you received, without re-serializing the JSON, and compare in constant time.
- Answer 401 if anything is off, without processing anything.
const crypto = require('crypto');
function verificar(req, rawBody, secret) {
const ts = req.headers['x-yr-lock-timestamp'];
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const esperada =
'sha256=' +
crypto.createHmac('sha256', secret).update(`${ts}.`).update(rawBody).digest('hex');
const recibida = req.headers['x-yr-lock-signature'] || '';
return (
recibida.length === esperada.length &&
crypto.timingSafeEqual(Buffer.from(recibida), Buffer.from(esperada))
);
}SECRET=yrlock_sk_...
BODY='{"keyId":"yrlock_3f9a1c0b7d2e4a6f8b0c1d2e"}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
curl -X POST https://puente.hotel.mx/locks/ping \
-H "Content-Type: application/json" \
-H "X-YR-Lock-Key: yrlock_3f9a1c0b7d2e4a6f8b0c1d2e" \
-H "X-YR-Lock-Timestamp: $TS" \
-H "X-YR-Lock-Signature: sha256=$SIG" \
--data-binary "$BODY"Careful: Keep the raw body before parsing it
Most frameworks parse the JSON and throw the original bytes away. If you sign over the re-serialized object, the signature stops matching the moment key order or a space changes. Configure your server to keep the raw body on these routes.
Issue a key
{base}/issueWe call it at check-in, on a room move, and when the front desk asks for an extra key.
What we send you
| Name | Type | Required | Description |
|---|---|---|---|
| reference | UUID | Yes | Our id for the key. Use it as the idempotency key: if it arrives twice, return the same key instead of issuing another. |
| lockId | string | Yes | Whatever the hotel put in that room’s mapping. |
| roomNumber | string | Yes | The room number, for your records. |
| guest.name | string | Yes | The guest’s name. |
| guest.email | string | No | May be absent. It is there to send a mobile key and must not be kept for anything else. |
| guest.phone | string | No | Same as the email: may be absent, and only for the mobile key. |
| validFrom | ISO 8601 | Yes | When it starts working. In UTC, RFC 3339. |
| validUntil | ISO 8601 | Yes | Never goes past the departure day at the hotel’s check-out time, in its own time zone. |
Request
{
"reference": "7b0c5d2e-1f3a-4c6b-9d8e-2a1b3c4d5e6f",
"lockId": "SALTO-204",
"roomNumber": "204",
"guest": { "name": "Ana Ruiz", "email": "ana@correo.mx", "phone": "+525512345678" },
"validFrom": "2026-11-20T21:04:00Z",
"validUntil": "2026-11-22T18:00:00Z"
}Response
{
"externalId": "K-889123",
"pinCode": "482913",
"mobileKeyUrl": "https://llave.proveedor.com/k/889123"
}What you return
| Name | Type | Required | Description |
|---|---|---|---|
| externalId | string | Yes | The key’s id in your system, up to 200 characters. It is what we send you when revoking. |
| pinCode | string | No | Keypad PIN, up to 32 characters. We show it to the front desk once and only keep its last four digits. |
| mobileKeyUrl | string | No | Must be https. It is shown to the front desk to share with the guest. |
Careful: Without externalId, we treat it as failed
If the response has no externalId, or carries a mobileKeyUrl that is not https, we treat it as failed and send you a revoke with the externalId if there was one. Always return the id, even if you resolve the key later.
Revoke a key
{base}/revokeWe call it at check-out, on a room move, and when the front desk revokes by hand.
What we send you
| Name | Type | Required | Description |
|---|---|---|---|
| externalId | string | Yes | The id you returned when you issued the key. |
Request
{ "externalId": "K-889123" }Response
200 OK · la llave ya no abre
204 · lo mismo, sin cuerpo
404 · cuenta como revocada: esa llave ya no existe en tu sistemaRevoking twice must be harmless. We read a 404 as success, not as an error: if the key does not exist, it certainly does not open.
Test the connection
{base}/pingWhat the “Test connection” button in the hotel’s panel runs. Answer 200 if the signature is valid and your bridge can reach the lock system.
What we send you
| Name | Type | Required | Description |
|---|---|---|---|
| keyId | string | Yes | The public key of the connection being tested. |
Request
{ "keyId": "yrlock_3f9a1c0b7d2e4a6f8b0c1d2e" }Answer 200 only if you genuinely reach the lock system. A ping that always says yes leaves the hotel believing everything is fine until the first check-in.
Errors
Any response outside 2xx — except the 404 on revoke — is a failure. If you send a JSON body with “message” or “error”, we show it to the front desk trimmed to 200 characters. Do not put personal data or secrets in that text.
{ "message": "cerradura 204 sin batería" }| Situation | What we do |
|---|---|
| No answer within 10 seconds, TLS error or refused connection | Failure, and a warning to the front desk. |
| A 3xx redirect | Failure. We do not follow redirects. |
| A 4xx or a 5xx | Failure, with your “message” on screen. |
| A body over 64 KB or invalid JSON | Failure. |
Retries
Rule: We do not retry on our own
That way a slow bridge does not block the front desk and duplicate keys are avoided. Whoever decides to retry is the front desk, from the booking.
- Failed check-in: the guest goes in anyway and the front desk sees the LOCK_SYSTEM_ERROR warning. It retries with “Issue key” from the booking. Each retry carries a fresh reference; if your system had already issued the previous one, it stays alive until its validUntil.
- Failed check-out: the guest leaves anyway. The key stays marked active until the front desk revokes it or its validUntil arrives, which is check-out time.
- Failed manual revoke: we hand the error back. The front desk can force the revocation, for instance if the card was destroyed. In that case we note it as revoked alongside your system’s error, and the lock may keep opening until validUntil.
- Room move: we revoke the old key first and then issue the new one. If the revocation fails, we issue anyway and warn.
When your system has its own retry towards the lock — a gateway with no network, say — answer 2xx as soon as you accept the order and resolve it afterwards. All we need is the externalId.
Security
- We only call https URLs that resolve to public IPs. Private, loopback, link-local and CGNAT addresses are rejected at connection time too, not just when saving.
- The secret is stored encrypted with AES-256-GCM. If you suspect a leak, ask the hotel to rotate it.
- The full PIN is not stored in yaReserva: only its last four digits, so the front desk can recognize it without being able to read it again.
Integrating a hotel?
Credentials come out of the hotel’s panel, and sandbox ones are created the same day. If the hotel does not have an account yet, start by registering it.