Webhook Setup Guide
Configure Oracle OHIP webhooks to receive real-time event notifications from your Oracle PMS system. This enables bidirectional synchronization between Oracle and InnKeeper.
Overview
Oracle OHIP webhooks send HTTP POST requests to your InnKeeper endpoints when events occur in your Oracle system (reservations, room status changes, housekeeping updates, etc.).
Supported Events
| Event Type | Description | InnKeeper Action |
|---|---|---|
| RESERVATION_CREATED | New reservation made | Create housekeeping schedule |
| GUEST_CHECKOUT | Guest checks out | Create housekeeping report, mark DIRTY |
| ROOM_STATUS_CHANGED | Room status updated | Sync room status in InnKeeper |
| ROOM_MAINTENANCE_REQUIRED | Maintenance needed | Create maintenance request |
| HOUSEKEEPING_STATUS_CHANGED | Cleaning status updated | Sync housekeeping progress |
Quick Setup (10 minutes)
Step 1: Get Your Webhook Secret
Contact your InnKeeper account manager to generate a webhook secret:
Webhook Secret: whsec_abc123def456ghi789
⚠️ Store securely - this is used to verify webhook authenticity.
Step 2: Configure Webhook URLs in Oracle
In your Oracle OHIP dashboard, add these webhook endpoints:
Reservation Events:
https://xyz.convex.site/api/v1/webhooks/oracle/reservationsRoom Status Events:
https://xyz.convex.site/api/v1/webhooks/oracle/roomsHousekeeping Events:
https://xyz.convex.site/api/v1/webhooks/oracle/housekeepingGuest Request Events:
https://xyz.convex.site/api/v1/webhooks/oracle/guest-requestsStep 3: Configure Secret in Convex
Add your webhook secret to Convex environment variables:
# Via Convex Dashboard ORACLE_WEBHOOK_SECRET=whsec_abc123def456ghi789 # Or via CLI npx convex env set ORACLE_WEBHOOK_SECRET whsec_abc123def456ghi789
Step 4: Test the Integration
Send a test webhook from Oracle or use this curl command:
curl -X POST https://xyz.convex.site/api/v1/webhooks/oracle/reservations \
-H "Content-Type: application/json" \
-H "x-oracle-timestamp: $(date +%s)" \
-d '{
"eventType": "GUEST_CHECKOUT",
"eventId": "evt_test_001",
"hotelId": "hotel_001",
"reservationId": "res_12345",
"roomNumber": "305",
"guestId": "guest_67890",
"timestamp": "2026-02-16T14:00:00Z"
}'Expected response:
{
"success": true,
"data": {
"message": "Webhook processed successfully",
"eventId": "evt_test_001",
"eventType": "GUEST_CHECKOUT"
}
}Webhook Security
HMAC Signature Verification
Every webhook includes a signature to verify authenticity:
Headers:
Verification Algorithm:
signature = HMAC-SHA256(webhook_secret, timestamp + "." + payload)
Example in Node.js
const crypto = require('crypto');
function verifyWebhook(payload, signature, timestamp, secret) {
// Check timestamp (must be within 5 minutes)
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > 300) {
throw new Error('Timestamp outside acceptable window');
}
// Compute expected signature
const signedPayload = `${timestamp}.${payload}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// Compare signatures (timing-safe)
const actualSignature = signature.replace('sha256=', '');
if (!crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(actualSignature)
)) {
throw new Error('Invalid signature');
}
return true;
}Replay Attack Prevention
Webhooks include timestamps to prevent replay attacks:
- Reject webhooks older than 5 minutes
- Store processed event IDs for 24 hours
- Return cached response if duplicate detected
Event Handling Examples
Guest Checkout Event
When Oracle sends a checkout event:
{
"eventType": "GUEST_CHECKOUT",
"eventId": "evt_checkout_12345",
"hotelId": "hotel_001",
"reservationId": "res_67890",
"roomNumber": "305",
"guestId": "guest_abc",
"checkoutTime": "2026-02-16T11:00:00Z",
"timestamp": "2026-02-16T11:05:00Z"
}InnKeeper automatically:
- Updates room 305 status to
DIRTY - Creates housekeeping report
- Assigns to available housekeeper
- Notifies assigned staff via mobile app
- Sends response to Oracle
Room Status Change
Oracle notifies InnKeeper of room status changes:
{
"eventType": "ROOM_STATUS_CHANGED",
"eventId": "evt_status_789",
"hotelId": "hotel_001",
"roomNumber": "412",
"status": "OUT_OF_ORDER",
"reason": "AC maintenance required",
"timestamp": "2026-02-16T09:30:00Z"
}InnKeeper actions:
- Updates room 412 to
OUT_OF_ORDER - Creates maintenance request
- Blocks future reservations
- Notifies maintenance team
Testing Webhooks
Local Testing with ngrok
# 1. Install ngrok brew install ngrok # macOS # 2. Start your local server npm run dev # InnKeeper API on localhost:3000 # 3. Create tunnel ngrok http 3000 # 4. Use ngrok URL in Oracle # Example: https://abc123.ngrok.io/api/v1/webhooks/oracle/reservations # 5. View requests in ngrok dashboard open http://localhost:4040
Using Webhook.site
For development testing:
- Go to https://webhook.site
- Copy your unique URL
- Configure as webhook endpoint in Oracle (temporarily)
- Inspect incoming webhooks
- Copy payloads for testing
Best Practices
✅ DO:
- Respond quickly (under 2 seconds)
- Return 200 even if queuing for later
- Validate signatures every time
- Handle duplicates with idempotency
- Log everything for debugging
- Monitor metrics (processing time, errors)
- Test thoroughly
❌ DON'T:
- Block responses on slow operations
- Trust blindly (always verify)
- Ignore timestamps
- Store secrets in code
- Skip error handling
- Hardcode IDs
- Log sensitive data (PII, payment info)
Troubleshooting
401 Unauthorized - Invalid Signature
Error: "Webhook verification failed"
Solutions:
- Verify webhook secret matches exactly
- Check signature format:
sha256=<hash> - Ensure payload matches what was signed
- Verify timestamp is within 5-minute window
- Check for trailing whitespace in secret
Webhook Not Received
Checklist:
- Verify URL is publicly accessible (not localhost)
- Check firewall allows Oracle IP ranges
- Ensure HTTPS endpoint (required by Oracle)
- Test endpoint manually with curl
- Check Oracle webhook configuration
- Review Oracle webhook delivery logs
Duplicate Events
Problem: Same event processed multiple times
Solutions:
- Implement idempotency using
eventId - Store processed event IDs in database/cache
- Return cached response for duplicates
- Don't rely on network-level deduplication