Sign inCreate account

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

  1. In Revenia: Settings → API → New key. Pick the permissions your system needs and copy the key (it is shown only once).
  2. Send the key on every request: Authorization: Bearer rvk_…
  3. 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.

HTTPCodeMeaning
400invalid_requestInvalid parameters or body
401unauthorizedMissing or invalid key
403insufficient_scopeThe key lacks the permission (requiredScope)
403plan_requiredThe hotel's plan doesn't include the API
404not_foundNot found at this property
409duplicate_external_refA reservation with that externalRef exists
409new_chargesCheck-out: new charges arrived; review the balance and retry
409request_in_progressThe same Idempotency-Key is still running
409request_outcome_unknownAn earlier request with that key never finished: check whether it took effect before retrying with a new key
422business_ruleA hotel rule prevents it (reason)
422idempotency_mismatchIdempotency-Key reused with another body
429rate_limitedToo many requests per minute
429quota_exceededMonthly quota reached
503charges_sync_failedCheck-out: a charges source didn't answer
503unavailableTemporarily 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