Agent skill
Hostaway Webhooks Skill
Receive and authenticate Hostaway webhooks. Use when setting up a Hostaway (vacation rental software) webhook endpoint, verifying the HTTP Basic auth login and password, debugging 401s, or handling events like reservation.created, reservation.updated, message.received, task.created and task.updated.
Install this skill
npx skills add hookdeck/webhook-skills --skill hostaway-webhooks
Hostaway sends a webhook when something changes in your account: a reservation is created or updated, a guest message arrives, a task changes. Webhooks are not signed. If you set a login and password on the webhook (Settings > Integrations, unified webhook, or the legacy reservation and message webhooks), Hostaway sends them as HTTP Basic auth, and your endpoint authenticates the request by checking the Authorization header.
Set a login and password on the Hostaway webhook. Webhooks without them send nothing to verify.
When to Use This Skill
- How do I receive Hostaway webhooks?
- How do I verify a Hostaway webhook? (HTTP Basic auth, there is no signature)
- Why does my Hostaway webhook endpoint return 401?
- How do I handle
reservation.created,reservation.updatedormessage.receivedevents? - Which HTTP method does Hostaway use for webhooks?
Verification (core)
const crypto = require('crypto');
const sha = (s) => crypto.createHash('sha256').update(s).digest(); // equal-length buffers
const safeEqual = (a, b) => crypto.timingSafeEqual(sha(a), sha(b));
function verifyHostawayRequest(headers, user = process.env.HOSTAWAY_WEBHOOK_USERNAME,
pass = process.env.HOSTAWAY_WEBHOOK_PASSWORD) {
if (!user || !pass) return false; // never accept when unconfigured
const m = /^basic +([A-Za-z0-9+/]+={0,2})$/i.exec((headers.authorization || '').trim());
if (!m) return false; // scheme is case-insensitive
const buf = Buffer.from(m[1], 'base64');
if (buf.toString('base64') !== m[1]) return false; // strict: no junk after valid base64
const text = buf.toString('utf8');
const i = text.indexOf(':'); // split on the FIRST colon
if (i === -1) return false;
const userOk = safeEqual(text.slice(0, i), user); // evaluate both, no short-circuit
const passOk = safeEqual(text.slice(i + 1), pass);
return userOk && passOk;
}
- Header:
Authorization: Basic base64(username:password), built from the login and password you set on the webhook. Both are optional in Hostaway; without them there is nothing to verify. - Missing or malformed header, or a mismatch: return
401. Check the credential first, for every HTTP method, before reading the body. - Compare the username and the password each in constant time. Node:
crypto.timingSafeEqualon equal-length buffers (SHA-256 digests here). Python:hmac.compare_digeston bytes (encode both sides;strraises on non-ASCII). Decode strictly (validate=Truein Python) so valid credentials followed by junk are rejected. - A static shared credential authenticates the sender, not the body. There is no signature and no timestamp, so it gives no replay protection. Use HTTPS only, and rotate the login and password in Hostaway if they leak.
- Hookdeck never sends a 401 challenge (no
WWW-Authenticateround trip), so the credentials must arrive with every request. Don't rely on the sender retrying with credentials after a challenge. - Read the body raw (
express.raw({ type: '*/*' })) so any content type works, and parse it only after the check.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
HTTP Methods and Body
Hostaway's docs don't name the HTTP method or the body format. The examples therefore accept GET, POST, PUT, PATCH and DELETE on the webhook route (Express app.all, a Next.js handler per method, FastAPI api_route), check the credential first for every method, and handle an empty body. Hookdeck's Hostaway source pre-selects POST without managing the method, so any other method the sender uses must also be allowed on the source.
Event Types
The unified webhook payload is not documented field by field, so treat the payload below as illustrative and read the event name defensively (event, eventType, or object + action), logging unknown shapes.
| Event | Sent When |
|---|---|
reservation.created | A reservation is created |
reservation.updated | A reservation changes |
message.received | A guest message arrives |
task.created | A task is created |
task.updated | A task changes |
{ "event": "reservation.created", "data": { "id": 1001, "listingMapId": 2002, "guestName": "Jane Doe", "status": "new" } }
Hostaway times out after 20 seconds and retries transient failures up to 3 times, so acknowledge quickly and process idempotently. Only ports 80 and 443 are reachable.
Environment Variables
HOSTAWAY_WEBHOOK_USERNAME=your-webhook-login # the login you set on the Hostaway webhook
HOSTAWAY_WEBHOOK_PASSWORD=your-webhook-password # the password you set on the Hostaway webhook
Local Development
Hostaway can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 hostaway --path /webhooks/hostaway
Use the printed URL as the webhook URL in Hostaway.
Resources
- references/overview.md - What Hostaway webhooks are, events and payload shape
- references/setup.md - Set the webhook, login and password, and allowed methods
- references/verification.md - Basic auth check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests