Create a folder whose ZIP root contains hoteldesk-plugin.json. IDs use lowercase letters, numbers and hyphens; versions use semantic versioning.
{
"id": "my-hotel-plugin",
"name": "My Hotel Plugin",
"version": "1.0.0",
"api": 2,
"main": "main.js",
"icon": "assets/icon.svg",
"minAppVersion": "0.1.1",
"capabilities": ["hooks", "storage", "hotel-info", "rpc"],
"hooks": ["app:ready"],
"web": { "enabled": true },
"contributes": {
"navigation": [{
"id": "overview",
"page": "overview",
"icon": "package",
"surfaces": ["desktop", "web"],
"title": { "en": "Overview", "fr": "Aperçu", "ro": "Prezentare" }
}],
"pages": [{
"id": "overview",
"type": "document",
"entry": "pages/overview.html",
"surfaces": ["desktop", "web"],
"title": { "en": "Overview", "fr": "Aperçu", "ro": "Prezentare" }
}]
}
}Create main.js for trusted sandboxed backend logic:
module.exports.activate = async function activate(hoteldesk) {
await hoteldesk.storage.set('activatedAt', new Date().toISOString());
hoteldesk.hooks.on('app:ready', async ({ version }) => {
const hotel = await hoteldesk.system.hotelInfo();
console.log(`${hotel.name} runs HotelDesk ${version}`);
});
hoteldesk.rpc.handle(async (action, payload) => {
if (action === 'hello') {
return { message: `Hello ${String(payload.name || 'guest')}` };
}
throw new Error('Unsupported plugin action.');
});
};Create pages/overview.html:
<!doctype html>
<html><body>
<article class="hd-card">
<h1>My plugin</h1>
<button id="hello">Run action</button>
<output id="result"></output>
</article>
<script>
document.querySelector('#hello').addEventListener('click', async () => {
const result = await window.HotelDeskPlugin.invoke('hello', { name: 'HotelDesk' });
document.querySelector('#result').textContent = result.message;
});
</script>
</body></html>ZIP the folder contents, not the parent folder, then install it from Plugins → Install ZIP.