Direct Contracts
Tenants frequently negotiate direct contracts with hotel chains for guaranteed rates and inventory during disruption events. Nexa surfaces these contracts as a first-class inventory source alongside Amadeus and Hotelbeds, with the same scatter-gather search semantics and the same booking pipeline.
Why direct contracts matter
Two main reasons:
- Cost. Negotiated rates are typically 20–40% below GDS rack rates. The savings on a single Tier-1 hub closure can pay for the platform.
- Guaranteed availability. A direct contract reserves a configured room block during the contract period — rooms that the hotel cannot resell to walk-in guests when a disruption hits and the operator needs them.
When the platform's allocation scorer picks between options, the cost penalty for direct contracts is structurally low and the consolidation bonus (multiple PNRs into the same hotel) is amplified — direct-contract hotels naturally come out near the top of the ranking.
How it works
Direct contracts live in the tenant's operational database under the contracts collection. The DirectContractAdapter reads from the collection at search time and returns matching results in the canonical inventory format — indistinguishable from a GDS result except for the URN's vendor:direct tag.
Creating a contract
Contracts are created by an OPS_SUPERVISOR+ role in the operations console.
Manually
POST /v1/contracts with the negotiated terms:
{
"hotelName": "Holiday Inn SCL Airport",
"address": "Av. Diego Aracena 555, Pudahuel",
"coordinates": { "lat": -33.3962, "lng": -70.7847 },
"stars": 4,
"amenities": ["FREE_BREAKFAST", "WHEELCHAIR_ACCESSIBLE", "AIRPORT_SHUTTLE"],
"rateUSD": 89.00,
"roomCap": 50,
"validFrom": "2026-01-01",
"validUntil": "2026-12-31",
"scope": {
"airports": ["SCL"],
"tiers": ["ECONOMY", "PREMIUM"]
},
"contactPhone": "+56 9 5555 5555",
"supportEmail": "ops@holidayinnscl.example",
"notes": "Free shuttle every 30 min until 23:00. After-hours arrivals must call +56 9 ..."
}
Required fields: hotelName, address, coordinates, rateUSD, roomCap, validFrom, validUntil. Everything else is optional.
Via the AI wizard (PDF / image)
Tenants typically receive contracts as PDFs or scans. The platform offers an AI-driven extractor:
POST /v1/contracts/analyze-pdf with the PDF or image. A Nexa-trained extractor parses the document, identifies the negotiated terms, and returns a draft contract with confidence scores per field. The operator reviews, edits, and saves. This converts a 30-minute manual data-entry task into 2-minute review-and-confirm.
The extractor is not a black box — every extracted field is highlighted on the rendered PDF in the UI so the operator can verify against the source.
Booking against a direct contract
Booking is handled by the DirectContractBookingAdapter. There is no external API call — the adapter:
- Acquires a per-attempt soft-hold against the contract offer (same model as GDS bookings).
- Validates available capacity from the live set of holds plus confirmed reservations.
- Writes a confirmed
Reservationdocument withvendor: direct. - Generates a voucher with the contract's contact phone and notes embedded.
There is no third-party billing for direct-contract bookings. The tenant's own contract terms govern payment to the hotel; Nexa records the booking and the price for finance/accounting purposes but doesn't initiate payment.
Cancellation
DirectContractBookingAdapter.cancel deletes the reservation and emits the canonical BookingCancelled event. Direct contracts typically have lenient cancellation terms (since the tenant negotiated them); the platform records the cancellation but does not enforce a deadline by default.
If a specific contract has a cancellation cutoff (e.g., 4 hours before check-in), the contract record carries cancellationDeadlineHours: 4 and the saga rollback path enforces it the same way it does for Hotelbeds.
Inventory tracking
Direct-contract inventory is computed, not maintained as a counter. Same model as the GDS soft-hold:
- Room cap on the contract (
roomCap: 50). - Live hold keys + confirmed reservations consumed from the cap.
- Available capacity =
roomCap - active_holds - confirmed_active_reservations.
The cap renews per the contract's calendar (typically resetting nightly for "rooms per night" contracts; per-event for "rooms per disruption" contracts). The renewal runs as a scheduled platform job per tenant.
Operator-managed contract lifecycle
| Action | Role | Effect |
|---|---|---|
| Create | OPS_SUPERVISOR+ | New contract; immediately searchable. |
| Update | OPS_SUPERVISOR+ | New version of the contract; old version retained for historical reservations. |
| Pause | OPS_SUPERVISOR+ | Removed from search; existing reservations honored. |
| Archive | ADMIN | Retained for audit; no new reservations possible. |
| Delete | n/a — never deleted | Contracts are append-only. Archive is the lifecycle terminus. |
Every change is recorded in the audit log with actor, before/after snapshot, and correlation URN.
Reporting
The platform's "savings" report (L2 in the use-cases catalog) compares each direct-contract booking against the GDS market rate at the time of booking, summing the savings. Finance/audit roles see a per-airport, per-period rollup. This is one of the strongest ROI signals tenants get out of the platform.
Cross-reference
- Operator API → contracts — endpoints for contract CRUD.
- Allocation engine — how direct contracts are scored against GDS results.