Automatic keypad code management via Smoobu webhooks. Integrates Smoobu (rental booking platform) with The Keys Cloud (smart lock management). When guests book via Smoobu, the system automatically generates PIN codes for keypad locks, sends SMS/email notifications, and manages code lifecycle (create on booking, update on change, delete on cancellation).
The webhook endpoints run on Cloudflare Workers (TypeScript, in workers/). Admin tools remain as PHP on a VPS.
- β Real-time sync - No polling, instant updates via webhooks
- β Event-driven - Only processes what changes (Reservation Created, Updated, Cancelled)
- β SMS notifications - Guest receives PIN via SMS (SerwerSMS or BudgetSMS)
- β Email messages - Multilingual messages sent to guests (EN/DE/PL/RU/UA)
- β ElevenLabs AI Agent Integration - Forwards conversation summaries to Pushover
- β Structured logging - JSON logs with 92-day retention via Cloudflare Workers Logs
- β Manual Sync Tool - Recovery tool with dry-run and smart linking (VPS)
- β PIN preservation - Never changes existing codes
- β Apartment changes - Handles booking moves between apartments
- β Auto cleanup - Cancelled bookings removed immediately
Cloudflare Workers (webhooks):
- Cloudflare account with Workers enabled
- Node.js 18+ and npm (for local development)
- Wrangler CLI
VPS (admin tools only):
- PHP 7.4+ with PDO, cURL, JSON extensions
- MySQL 5.7+ database
API credentials (both):
- Smoobu API key
- The Keys API credentials
- SMS Provider credentials (SerwerSMS or BudgetSMS)
- ElevenLabs webhook secret (for Pushover integration)
- Pushover API token and user key
cd workers
npm installConfigure workers/wrangler.toml with your apartment/lock mappings:
[vars]
APARTMENT_LOCKS = '{"123456":3733,"123457":3723}'
LOCK_ACCESSOIRES = '{"3733":"OXe37UIa","3723":"SLORUV6s"}'
DIGICODE_PREFIXES = '{"3733":"28","3723":"18"}'
DEFAULT_TIMES = '{"check_in_hour":"15","check_in_minute":"0","check_out_hour":"12","check_out_minute":"0"}'
SMS_PROVIDER = "serwersms"
PIN_LENGTH = "4"Add secrets in the Cloudflare dashboard (Workers & Pages > thekeys > Settings > Variables and Secrets):
| Secret | Description |
|---|---|
THEKEYS_USERNAME |
The Keys Cloud login |
THEKEYS_PASSWORD |
The Keys Cloud password |
SMOOBU_API_KEY |
Smoobu API key |
ELEVENLABS_WEBHOOK_SECRET |
ElevenLabs webhook HMAC secret |
PUSHOVER_USER_KEY |
Pushover user key |
PUSHOVER_API_TOKEN |
Pushover app token |
SERWERSMS_API_TOKEN |
SerwerSMS Bearer token |
BUDGETSMS_USERNAME |
BudgetSMS username (if using BudgetSMS) |
BUDGETSMS_USERID |
BudgetSMS user ID (if using BudgetSMS) |
BUDGETSMS_HANDLE |
BudgetSMS handle (if using BudgetSMS) |
WEBHOOK_SECRET |
Optional: Smoobu HMAC secret |
Deploy by connecting the GitHub repo to Cloudflare. Set Root directory to workers/ in the build configuration.
Configure Smoobu webhook:
- Login to Smoobu β Settings β API & Webhooks
- Add webhook URL:
https://thekeys.<your-subdomain>.workers.dev/webhook - Events: Reservation Created, Updated, Cancelled
Configure ElevenLabs webhook:
- Set Post-call Webhook URL:
https://thekeys.<your-subdomain>.workers.dev/pushover
The admin tools still need PHP and MySQL on a VPS.
Database:
CREATE DATABASE thekeys;
CREATE TABLE webhook_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
event_type VARCHAR(50),
booking_id INT,
payload JSON,
processed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE sync_history (
id INT AUTO_INCREMENT PRIMARY KEY,
booking_id INT,
code_id INT,
operation VARCHAR(20),
success BOOLEAN,
error_message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);Configuration: Copy config.example.php to config.php and fill in your credentials.
Upload to your server: manual_sync.php, lock_migration.php, pipe.php, TheKeysAPI.php, SmoobuWebhook.php, config.php, languages/.
Smoobu POST β Workers /webhook β smoobu.ts β thekeys.ts (create/update/delete PIN)
β sms.ts (SerwerSMS or BudgetSMS)
β Smoobu API (message to guest)
ElevenLabs POST β Workers /pushover β pushover.ts β Pushover API (notification)
workers/
src/
index.ts # Router: /webhook and /pushover
smoobu.ts # Smoobu webhook handler (booking lifecycle)
thekeys.ts # The Keys Cloud API client (JWT auth, CRUD)
pushover.ts # ElevenLabs β Pushover forwarder
sms.ts # SerwerSMS + BudgetSMS dispatch
types.ts # TypeScript type definitions
languages/ # Localized message templates (en/de/pl/ru/ua)
test/ # Vitest test suite
wrangler.toml # Cloudflare Workers config
index.tsβ RoutesPOST /webhookto the Smoobu handler andPOST /pushoverto the Pushover handler. Returns 200 on errors to prevent webhook retries.smoobu.tsβ Main business logic. Routes events (reservation.new,reservation.updated,reservation.cancelled), manages PIN lifecycle, dispatches SMS and guest message notifications.thekeys.tsβ API client for The Keys Cloud. JWT auth, CRUD on lock codes via form-encoded POST requests. PIN stored in code description asSmoobu#{bookingId}.pushover.tsβ Validates ElevenLabs HMAC signature, extracts caller ID and transcript summary, forwards as Pushover notification.sms.tsβ SMS dispatch via SerwerSMS (Bearer token, UTF for Cyrillic) or BudgetSMS (query params). Handles Polish diacritic transliteration.
manual_sync.phpβ Web UI to recover missed bookings from Smoobu API. Supports dry-run mode, name-based matching for existing codes (with prefix handling), and linksSmoobu#IDto manual codes. Sends guest notifications for new codes and date updates.lock_migration.phpβ Lock hardware replacement tool; migrates codes and notifies guests.pipe.phpβ Email logging endpoint for IFTTT triggers.config.phpβ Runtime config for admin tools (gitignored; copy fromconfig.example.php).
New Booking in Smoobu β Webhook to your URL β webhook.php β SmoobuWebhook.php
- Log to database
- Check idempotency (prevent duplicates)
- Process based on event type:
reservation.new: Generates random 4-digit PIN (prepends prefix), creates code in The Keys, sends multilingual message and SMS to guest, logs operation.reservation.updated: Finds existing code (bySmoobu#IDor Guest Name fallback), updates dates/times if changed. Preserves existing PIN. Dispatches notifications for date changes or new creations.reservation.cancelled: Finds existing code and deletes only if checkout date has passed. Prevents deletions during active stays.
Automatic SMS sent via SerwerSMS or BudgetSMS to the guest's phone with PIN code and check-in/out details.
SerwerSMS uses Bearer token auth + utf=true for Cyrillic. BudgetSMS uses username/userid/handle query params and auto-detects Unicode.
Multilingual email sent to guest with building/lobby/apartment door codes, check-in/out times, parking info, and contact phone.
pushover.php is a webhook endpoint to receive conversation summaries from ElevenLabs AI Agents and forward them as Pushover notifications.
- Configure ElevenLabs: Set the Post-call Webhook URL to
https://your-domain.com/thekeys/pushover.php. Copy the Webhook Secret. - Configure
config.php:'elevenlabs' => [ 'webhook_secret' => 'your_elevenlabs_secret' ], 'pushover' => [ 'user_key' => 'your_pushover_user_key', 'api_token' => 'your_pushover_app_token' ]
apartment_locksβ Maps Smoobu apartment ID β The Keys lock ID.lock_accessoiresβ Maps lock ID β accessoire string ID (e.g.,"OXe37UIa").β οΈ IMPORTANT: You MUST use the string ID from the API response fieldaccessoire.id_accessoire, not a numeric ID.digicode_prefixesβ Maps lock ID β 2-digit PIN prefix. The full PIN is prefix + 4-digit random code.
- Lock ID: URL shows
/compte/serrure/{LOCK_ID}/view_partageon app.the-keys.fr. - Accessoire STRING ID: Use TheKeysAPI to list codes, check
accessoire.id_accessoirefield.
| API | Auth | Format |
|---|---|---|
| The Keys Cloud | JWT (login β token) | Form-encoded POST |
| Smoobu | Api-Key header |
JSON |
| ElevenLabs | HMAC Signature | JSON |
| SerwerSMS | Bearer token | Form-encoded POST with utf=true |
| BudgetSMS | username+userid+handle params |
GET query string |
| Pushover | token + user keys |
Form-encoded POST |
cd workers
# Run tests
npm test
# Type check
npx tsc --noEmit
# Local dev server
npx wrangler dev
# Tail production logs
npx wrangler tailTest locally (simulate Smoobu event):
curl -X POST http://localhost:8787/webhook \
-H "Content-Type: application/json" \
-d '{"action":"newReservation","data":{"id":12345,"guest-name":"Test Guest","arrival":"2026-05-01","departure":"2026-05-03","apartment":{"id":"123456"},"language":"en"}}'Test Pushover endpoint:
curl -X POST http://localhost:8787/pushover \
-H "Content-Type: application/json" \
-d '{"type":"post_call_transcription","data":{"analysis":{"transcript_summary":"Test"},"agent_name":"Test Agent","agent_id":"abc","conversation_id":"123"}}'php -l manual_sync.php
php -l lock_migration.php-- Check Recent Webhooks
SELECT * FROM webhook_logs ORDER BY created_at DESC LIMIT 10;
-- Check Sync Operations
SELECT * FROM sync_history ORDER BY created_at DESC LIMIT 10;
-- Count Operations by Type
SELECT operation, COUNT(*) as count FROM sync_history GROUP BY operation;config.phpis gitignored (contains credentials).- Optional IP Whitelist (
webhook.ip_whitelist) and Webhook Secret (webhook.secret). - All webhook activity is logged to
logs/webhook.logand thewebhook_logsdatabase table.
Webhook Not Working / Receiving Events:
- Check Smoobu webhook configuration and verify URL is publicly accessible.
- Check
logs/webhook.logandwebhook_logstable. - Test manually with curl.
Codes Not Creating:
- Check The Keys credentials in
config.php. - Verify apartment/lock mappings and ensure accessoire IDs are STRINGS.
- Check
sync_historytable for errors.
Messages Not Sending:
- Verify Smoobu / SerwerSMS API tokens.
- Check if arrival date is in future (messages only sent to future guests).
- Check logs for HTTP status codes.
MIT License - see repository for details.
Workers logs: npx wrangler tail or Cloudflare dashboard.
Admin tool logs: logs/webhook.log, webhook_logs, and sync_history database tables.
Note for AI Assistants (Claude, Gemini, etc.): This repository contains webhook integrations. Workers code is in workers/ (TypeScript, Cloudflare Workers). Admin tools are plain PHP β do not add a build system to the PHP files.