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 TypeDescriptionInnKeeper Action
RESERVATION_CREATEDNew reservation madeCreate housekeeping schedule
GUEST_CHECKOUTGuest checks outCreate housekeeping report, mark DIRTY
ROOM_STATUS_CHANGEDRoom status updatedSync room status in InnKeeper
ROOM_MAINTENANCE_REQUIREDMaintenance neededCreate maintenance request
HOUSEKEEPING_STATUS_CHANGEDCleaning status updatedSync 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/reservations

Room Status Events:

https://xyz.convex.site/api/v1/webhooks/oracle/rooms

Housekeeping Events:

https://xyz.convex.site/api/v1/webhooks/oracle/housekeeping

Guest Request Events:

https://xyz.convex.site/api/v1/webhooks/oracle/guest-requests

Step 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:

x-oracle-signature: sha256=3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b
x-oracle-timestamp: 1640995200

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:

  1. Updates room 305 status to DIRTY
  2. Creates housekeeping report
  3. Assigns to available housekeeper
  4. Notifies assigned staff via mobile app
  5. 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:

  1. Updates room 412 to OUT_OF_ORDER
  2. Creates maintenance request
  3. Blocks future reservations
  4. 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:

  1. Go to https://webhook.site
  2. Copy your unique URL
  3. Configure as webhook endpoint in Oracle (temporarily)
  4. Inspect incoming webhooks
  5. 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

Next Steps

InnKeeper API Documentation | InnKeeper