Connecting to OHIP
What you need from the OHIP Developer Portal
Section titled “What you need from the OHIP Developer Portal”Create (or open) an application in the Oracle Hospitality Developer Portal and note, for the environment you want to use:
| Value | Where it goes | Notes |
|---|---|---|
| Gateway URL | OHIP_<ENV>_GATEWAY_URL |
For example https://<host>/. No trailing path. |
| Application key | OHIP_<ENV>_APP_KEY |
Sent as x-app-key on every call. |
| OAuth client id and secret | OHIP_<ENV>_CLIENT_ID, OHIP_<ENV>_CLIENT_SECRET |
Basic auth on POST /oauth/v1/tokens. |
| Enterprise id | OHIP_<ENV>_ENTERPRISE_ID |
Client-credentials (“OCIM”) environments. Setting it selects the client-credentials grant. |
| Scope | OHIP_<ENV>_SCOPE |
Client-credentials only. Default urn:opc:hgbu:ws:__myscopes__. |
| Integration user and password | OHIP_<ENV>_USERNAME, OHIP_<ENV>_PASSWORD |
Password (“SSD”) environments only. Leave empty for client-credentials. |
| Hotel code | OHIP_<ENV>_HOTEL_ID |
Sent as x-hotelid. OHIP requires it (or x-hubid) on every call, even chain-level ones. |
| Chain code | OHIP_<ENV>_CHAIN_CODE |
Needed for streaming. |
| Hotel time zone | OHIP_<ENV>_TIMEZONE |
Optional IANA id, default UTC. |
| Streaming URL | OHIP_<ENV>_WEBSOCKET_URL |
Optional. Default wss://<gateway host>/subscriptions. |
<ENV> is an upper-case environment name you choose. The rest of this README uses SANDBOX, which .env.example already contains.
You can keep several sets side by side (OHIP_SANDBOX_*, OHIP_UAT_*, …) and pick one with --env.
For streaming you also need to subscribe the application to business events in the portal: open the application, then the environment, then Business Events, and select the events you want (at least reservation and profile events). Without that subscription the stream connects but delivers nothing.
Step 1: verify the credentials
Section titled “Step 1: verify the credentials”set -a; source .env; set +adotnet run --project src/StayFn.Cli -- ohip ping --env sandbox --hotel "$OHIP_SANDBOX_HOTEL_ID"This fetches a token and makes one read-only hotel call through the full StayFn pipeline. It prints token ok and the call result, and
never prints secrets. A non-zero exit code means the token or the call failed. The error names the OHIP error code where one exists.
Step 2: seed a tenant that uses these credentials
Section titled “Step 2: seed a tenant that uses these credentials”dotnet run --project src/StayFn.Cli -- dev seed --env sandbox --tenant "Sandbox (dev)"This creates or updates one tenant, one environment and one default hotel. The environment stores references such as
env:OHIP_SANDBOX_APP_KEY, never the values, so the host must see the same variables at run time. stayfn dev runs this seeding
automatically.
Step 3: call a real OHIP function
Section titled “Step 3: call a real OHIP function”With the host running (for example stayfn dev) and the dev tenant enabled:
curl -s -X POST http://localhost:5080/api/functions/rsv/get-reservation/invoke \ -H 'Content-Type: application/json' \ -H "X-Hotel-Id: $OHIP_SANDBOX_HOTEL_ID" \ -d '{"confirmationNumber":"<a confirmation number in that hotel>"}'The response carries X-Invocation-Id. Open that invocation in the dashboard to see each OHIP call: operation, status, duration, retries
and rate-limit waits. An unknown confirmation number returns 404 with an RFC 9457 problem body.
The reference functions you can call right away:
| Function | What it does |
|---|---|
custom/ping-echo |
echoes its input, no OHIP call (smoke test) |
rsv/get-reservation |
fetches a reservation by confirmation number (searchHotelReservations) |
rsv/nightly-arrivals-export |
scheduled export of a hotel’s arrivals |
rsv/sync-reservation-to-crm-stub |
target of the reservation.changed handler |
rsv/count-arrivals |
counts a date’s arrivals, optionally for one room type (plain signature, {"date":"2026-10-01","roomType":"KING"}) |
rsv/arrivals-summary |
OHIP’s reported arrival total for a date (flat ctx.Ohip.Api.Rsv.SearchHotelReservationsAsync) next to the count of rsv/count-arrivals (typed client); Derived idempotency, so the same date replays |
crm/search-profiles |
profile search (searchProfiles) |
crm/upsert-profile |
creates or updates a guest profile (writes to OPERA) |
crm/audit-profile-change |
target of the profile.changed handler |
Handlers: reservation.changed → rsv/sync-reservation-to-crm-stub, profile.changed → crm/audit-profile-change.
How StayFn talks to OHIP
Section titled “How StayFn talks to OHIP”- Tokens.
POST {gateway}/oauth/v1/tokenswith Basic auth andx-app-key. It uses the password grant, or client credentials withscopeand theenterpriseIdheader. Tokens are cached per environment and refreshed at the earlier of 80 % of their lifetime andexp − 2 min. Sandbox tokens live 8 hours. - Headers. Every call carries
Authorization: Bearer …,x-app-key,x-hotelid(the request hotel, or the tenant’s default hotel for chain-level calls) andx-request-id(the StayFn correlation id). - Rate limits. OHIP documents about 50 requests/s per gateway, shared by all consumers, and throttles by delaying responses rather
than returning 429. StayFn’s governor defaults to a token bucket of 40 with 40/s refill per app key. Each function declares its
expected number of calls with
[OhipBudget(n)](default 10), and the budget is reserved before it runs. Tune it withSTAYFN__Ohip__RateLimit__*. - Errors. OHIP error bodies (
type,title,detail,o:errorCode) becomeOhipException. 408, 429, 5xx and network errors are transient and retried with backoff. Other 4xx errors are permanent. An empty204on a fetch, or a search with no match, becomesResourceNotFoundException(404). - Asynchronous operations (the
*asyncmodules) use POST →202+Location→ HEAD polling withretry-after→ GET once. - One method per operation.
ctx.Ohip.Api.<Module>.<Operation>Async(for examplectx.Ohip.Api.Crm.GetProfileAsync(profileId)) is generated from the pinned specs bytools/kiota/operations-facade: 2,843 of the 2,845 operations. It uses the same module clients as the Kiota chains, so it sends the same requests and leaves the sameinvocation_ohip_callsrows. ItshotelIddefaults to the hotel that feedsx-hotelid. The rules and the two skipped operations are intools/kiota/README.md. - Specs. The generated clients and the flat layer come from the specs pinned in
tools/ohip-specs/26.3.0.0(Oracle’shospitality-api-docs, Swagger 2.0).tools/kiota/generate.shregenerates both deterministically, and CI fails if the generated code drifts.
Everything above, with the evidence for each item, is in docs/adr/0002-ohip-facts.md.
Sandbox etiquette
Section titled “Sandbox etiquette”Oracle’s partner sandbox is shared with other partners. Keep writes to a minimum, never load-test it, and never commit data read from it.
The opt-in live tests in tests/StayFn.SandboxTests run only when the OHIP_SANDBOX_* variables are present.