Revenia API
The Revenia REST API lets other systems read and create reservations, check availability and prices, post charges to the folio and get notified when something changes. It is included in the Pro and AI Premium plans.
openapi.json · OpenAPI 3.1 contract (import it into Postman or Insomnia, or generate a client)
https://revenia.ai/api/v1
Getting started
- In Revenia: Settings → API → New key. Pick the permissions your system needs and copy the key (it is shown only once).
- Send the key on every request: Authorization: Bearer rvk_…
- Each key belongs to one property and only sees that property.
curl https://revenia.ai/api/v1/reservations?updatedSince=2026-10-01T00:00:00Z \
-H "Authorization: Bearer rvk_…"
curl -X POST https://revenia.ai/api/v1/reservations \
-H "Authorization: Bearer rvk_…" \
-H "Idempotency-Key: 6f1c2a1e-1d2b-4c3d-9e8f-0a1b2c3d4e5f" \
-H "Content-Type: application/json" \
-d '{"roomTypeId":"…","checkIn":"2026-12-20","checkOut":"2026-12-23","adults":2,
"guest":{"firstName":"Ana","lastName":"López","email":"ana@example.com"},"externalRef":"PO-1234"}'Permissions
Each key has the permissions the hotel grants. A :write permission includes the :read of the same resource.
- property:read
- availability:read
- rates:write
- reservations:read
- reservations:write
- guests:read
- folios:read
- folios:write
- webhooks:manage
Limits
120 requests per minute per key (headers x-ratelimit-limit and x-ratelimit-remaining; 429 rate_limited with retry-after). Each plan includes a monthly request quota per property (x-quota-limit, x-quota-remaining; 429 quota_exceeded until the 1st of next month).
Money
Integers in minor units (cents) of currency. Quote and folio amounts include taxes.
Errors
Format { "error": { "code", "message" } }. Hotel rules (no availability, minimum stay, past dates…) answer 422 business_rule with reason. Send Accept-Language: es for Spanish messages.
| HTTP | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Invalid parameters or body |
| 401 | unauthorized | Missing or invalid key |
| 403 | insufficient_scope | The key lacks the permission (requiredScope) |
| 403 | plan_required | The hotel's plan doesn't include the API |
| 404 | not_found | Not found at this property |
| 409 | duplicate_external_ref | A reservation with that externalRef exists |
| 409 | new_charges | Check-out: new charges arrived; review the balance and retry |
| 409 | request_in_progress | The same Idempotency-Key is still running |
| 409 | request_outcome_unknown | An earlier request with that key never finished: check whether it took effect before retrying with a new key |
| 422 | business_rule | A hotel rule prevents it (reason) |
| 422 | idempotency_mismatch | Idempotency-Key reused with another body |
| 429 | rate_limited | Too many requests per minute |
| 429 | quota_exceeded | Monthly quota reached |
| 503 | charges_sync_failed | Check-out: a charges source didn't answer |
| 503 | unavailable | Temporarily unavailable; retry |
Reservations and charges
Reservations and charges follow the same rules as the front desk (availability, restrictions, cancellation policy, zero balance to check out) and the guest gets the same emails. Every change appears in the reservation history with the key's name. Payments and refunds are recorded at the front desk.
Safe retries (Idempotency-Key)
Send Idempotency-Key (e.g. a UUID) on every POST. Repeating the same request with the same key within 24 h returns the first response (header idempotent-replayed: true) instead of booking or charging twice. Errors are not stored, so you can retry with the same key after fixing the cause.
Syncing
GET /reservations?updatedSince= returns reservations changed after that instant (any folio change counts). Store the updatedAt of the last one you processed and ask again with a few minutes of overlap; follow nextCursor until it is null. Webhooks tell you right away; polling is the fallback.
Webhooks
Register an https URL with POST /webhooks and the events you want; changes from any source arrive (front desk, booking engine, channels, the API). Revenia POSTs the event within seconds, signed in the Revenia-Signature header. Answer 2xx within 10 s; otherwise it is retried for about 2 days (1 min, 5 min, 30 min, 2 h, 6 h, 12 h, 24 h). A URL failing for 3 days is disabled; re-enable it with PATCH active: true. An event can arrive more than once or out of order: deduplicate by id and compare the reservation's updatedAt.
Events
- reservation.created · A reservation was created
- reservation.updated · Dates, room, occupancy, guest, rate, notes or guarantee changed (folio changes do not count)
- reservation.cancelled · A reservation was cancelled
- reservation.checked_in · The guest checked in
- reservation.checked_out · The guest checked out
- reservation.no_show · The reservation was marked as no-show
- payment.received · A payment was recorded on the folio
- payment.voided · A payment was voided (payment.voided: true)
- payment.refunded · A refund was recorded (payment.kind REFUND)
Verify the signature (Node.js)
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody: the request body exactly as received (text, not re-serialized JSON)
export function verifyRevenia(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=")));
const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest();
const got = Buffer.from(parts.v1 ?? "", "hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && got.length === expected.length && timingSafeEqual(got, expected);
}AI agents: search, compare and book
Any AI assistant (ChatGPT, Claude, Perplexity or a travel agency's agent) can search and compare hotels on Revenia, see their full details, find the best dates, quote and book for its user, with no credentials and with the same rules, prices and deposit as the hotel's booking engine. To change or cancel, the guest confirms with a code sent to their email. Only hotels that turned on “Visible to AI agents” in Settings → Booking engine appear.
Connect it as a remote MCP server, with no authentication:
- ChatGPT: Settings → Connectors → create a custom connector with the URL https://revenia.ai/mcp and no authentication.
- Claude (claude.ai or the app): Settings → Connectors → Add custom connector with the URL https://revenia.ai/mcp.
- Claude Code, Cursor and other MCP clients: an HTTP server with the URL https://revenia.ai/mcp (example below).
# Claude Code
claude mcp add --transport http revenia-hotels https://revenia.ai/mcp
# mcp.json (Cursor y otros)
{ "mcpServers": { "revenia-hotels": { "url": "https://revenia.ai/mcp" } } }Tools
- search_hotels · Search and compare hotels
- hotel_details · Hotel details
- price_calendar · Price calendar (flexible dates)
- quote_stay · Quote a stay
- book_stay · Book
- get_booking · Find a booking
- request_booking_change · Request a change
- request_cancellation · Request a cancellation
- confirm_booking_change · Confirm a change or cancellation
MCP: your AI assistant connected to the hotel
With the Revenia MCP server, your AI assistant (Claude, Cursor and other compatible clients) reads and runs the hotel in plain language: “who arrives tomorrow?”, “how much does room 101 owe?”, “move Ana's booking to Friday”. It uses a key from Settings → API: the available tools depend on its permissions, follow the same rules as the front desk, every change shows in the reservation history with the key's name and every message counts toward the API limits. The assistant should confirm with you before changing anything.
In claude.ai or ChatGPT: add a custom connector with the URL https://revenia.ai/api/mcp and sign in with your Revenia account; you pick the property and the permissions (only those your role has) and the connection shows in Settings → API, where you can revoke it. In Claude Code, Claude Desktop, Cursor or n8n you can also use a key:
# Claude Code
claude mcp add --transport http revenia https://revenia.ai/api/mcp \
--header "Authorization: Bearer rvk_…"
# Claude Desktop / Cursor (mcp.json)
{
"mcpServers": {
"revenia": {
"url": "https://revenia.ai/api/mcp",
"headers": { "Authorization": "Bearer rvk_…" }
}
}
}Tools
- hotel_overview · property:read
- day_summary · reservations:read
- find_reservations · reservations:read
- get_reservation · reservations:read
- check_availability · availability:read
- quote_stay · availability:read
- occupancy · reservations:read
- find_guests · guests:read
- create_reservation · reservations:write
- change_reservation · reservations:write
- cancel_reservation · reservations:write
- check_in · reservations:write
- check_out · reservations:write
- add_charge · folios:write
- get_rates · availability:read
- list_restrictions · availability:read
- change_rates · rates:write
- rate_changes · rates:write
- undo_rate_change · rates:write
- save_restriction · rates:write
- delete_restriction · rates:write
- rate_plans · rates:write
- save_rate_plan · rates:write
- adjust_reservation_price · reservations:write
Reference
From the OpenAPI contract.
- GET/propertyPermission: property:read
The property of the API key
- GET/room-typesPermission: property:read
Room types
- GET/roomsPermission: property:read
Rooms
- GET/availabilityPermission: availability:read
Sellable rooms per room type and night
Nights in [from, to), up to 90. Same inventory published to the channels (reservations, blocks, out of order, stop sell). Prices come from POST /quotes.
- POST/quotesPermission: availability:read
Final prices for a stay
Same prices as the hotel's booking engine: published rates only, taxes, meals, pets and coupon included, plus the deposit the hotel's policy asks for. rate.id null is the standard rate.
- GET/reservationsPermission: reservations:read
List reservations (sync)
Ordered by updatedAt. Archived (test) reservations only with status=ARCHIVED.
- POST/reservationsPermission: reservations:write
Create a reservation
Priced with the hotel's rates (rateId null = standard rate; get prices from POST /quotes). Restrictions apply as at the front desk. The guest is reused when id is sent, or when a guest with the same email and name exists. The guest receives the hotel's confirmation email. externalRef (your id) is unique per property: 409 duplicate_external_ref if it was already used.
- GET/reservations/{ref}Permission: reservations:read
One reservation
- PATCH/reservations/{ref}Permission: reservations:write
Change the stay
Dates, room type, room and occupancy; omitted fields keep their value (a new room type without roomId leaves the room unassigned). New nights are priced with current rates; repriceAll reprices the existing ones too. In-house guests can change the departure date and room, not the arrival.
- POST/reservations/{ref}/cancelPermission: reservations:write
Cancel a reservation
Applies the reservation's cancellation policy (penalty on what was paid; a refund due shows as a negative balance) and emails the guest.
- POST/reservations/{ref}/check-inPermission: reservations:write
Check in
From the arrival date, with a clean room assigned. Send roomId to assign it in the same call.
- POST/reservations/{ref}/check-outPermission: reservations:write
Check out
First fetches the latest restaurant and activity charges. If new charges were posted it answers 409 new_charges (review the balance and call again); if a charges source does not answer, 503 charges_sync_failed. Requires a zero balance (422 err.balanceDue otherwise): payments are recorded at the front desk.
- POST/reservations/{ref}/folio/chargesPermission: folios:write
Post a charge to the folio
Charges only (consumption, services); taxes are added as configured by the hotel. Not allowed on checked-out folios; on cancelled or no-show reservations only EXTRA. Use Idempotency-Key so a retry does not charge twice.
- GET/reservations/{ref}/folioPermission: folios:read
Folio of a reservation
Charges, payments and refunds, voided ones included (voided: true), with totals.
- GET/webhooksPermission: webhooks:manage
Webhooks of this API key
- POST/webhooksPermission: webhooks:manage
Register a webhook
https URL with a public domain name. Up to 10 per API key. reservation.* events require the key to have reservations:read and payment.* events folios:read (403 insufficient_scope otherwise). The response includes the signing `secret`, shown only here and when rotating it.
- GET/webhooks/{id}Permission: webhooks:manage
One webhook
- PATCH/webhooks/{id}Permission: webhooks:manage
Change a webhook
url, events, description or active. Setting active: true re-enables a disabled webhook and clears its failure mark.
- DELETE/webhooks/{id}Permission: webhooks:manage
Delete a webhook
- POST/webhooks/{id}/testPermission: webhooks:manage
Send a test event
Sends a signed `ping` event now (no retries) and returns the result.
- POST/webhooks/{id}/rotate-secretPermission: webhooks:manage
Rotate the signing secret
The previous secret stops working immediately.
- GET/webhooks/{id}/deliveriesPermission: webhooks:manage
Recent deliveries
Newest first; kept 30 days.
- POST/webhooks/{id}/deliveries/{deliveryId}/retryPermission: webhooks:manage
Send a delivery again
Queues a delivered or failed event again with full retries (e.g. after it ran out of retries while your endpoint was down). 409 delivery_pending if it is still queued.
- GET/guestsPermission: guests:read
Guests with reservations at this property
- GET/guests/{id}Permission: guests:read
One guest and their reservations at this property