Skip to content

Business events

The ingest worker subscribes to newEvent for the chain over GraphQL-over-WebSocket (graphql-transport-ws). It stores each event once in the inbox, keyed by uniqueEventId, then advances the checkpoint (offset + 1). Business events are mapped to internal names:

OPERA moduleName | eventName Internal event Status
RESERVATION | NEW RESERVATION reservation.created live-verified
RESERVATION | UPDATE RESERVATION reservation.changed live-verified
PROFILE | NEW PROFILE profile.created live-verified
PROFILE | UPDATE PROFILE profile.changed live-verified
cancel, check-in/out, rate, availability, block reservation.cancelled, checkin, checkout, rate.changed, availability.changed, block.changed mapped from Oracle’s vocabulary, not yet seen live
anything else ohip.<module>.<event> with the raw payload always ingested, never dropped

The map is configuration (Events:BusinessEventMap), so a correction is a settings change.

Streaming is off by default. Oracle allows one consumer per chain and app key: enable it on exactly one deployment per app key. Replicas of that deployment share the stream through a database lease, and a second consumer is refused with close code 4409.

Terminal window
# Check the connection without the host (prints structure only, never values)
dotnet run --project src/StayFn.Cli -- ohip stream-probe --env sandbox --offset-type highest --seconds 60
# Run the host locally with streaming on
set -a; source .env; set +a
dotnet run --project src/StayFn.Host --launch-profile sandbox-streaming

In any other deployment set STAYFN__Events__Streaming__Enabled=true and, if needed, STAYFN__Events__Streaming__WebSocketUrl (a URL or an env: reference). The client always adds the ?key=<sha256 of the app key> query parameter that the gateway requires, sends a ping every 10 seconds, reconnects hourly with a fresh token, and waits at least 10 seconds between sessions. On the sandbox, events arrive in batches about 2–3 minutes after the change in OPERA.

Any system can push events to POST /api/events/webhook/{source} with a tenant API key that has the events:webhook scope and these headers:

X-StayFn-Signature: sha256=<hex HMAC-SHA256(secret, "<timestamp>.<raw body>")>
X-StayFn-Timestamp: <unix seconds or ISO 8601>
X-StayFn-Event-Id: <unique id, used for deduplication>
X-Hotel-Id: <one of the tenant's hotels>

The secret is the tenant variable webhook.<source>.secret, which can be a plain value in development or a secret reference in production. Bodies are capped at 1 MB. Source oxi accepts OPERA 5 OXI XML.