API de Revenia

La API REST de Revenia permite que otros sistemas lean y creen reservas, consulten disponibilidad y precios, carguen consumos al folio y reciban avisos cuando algo cambia. Está incluida en los planes Pro y AI Premium.

openapi.json · Contrato OpenAPI 3.1 (para importar en Postman, Insomnia o generar un cliente)

https://revenia.ai/api/v1

Primeros pasos

  1. En Revenia: Configuración → API → Nueva llave. Elige los permisos que necesita tu sistema y copia la llave (se muestra una sola vez).
  2. Envía la llave en cada llamada: Authorization: Bearer rvk_…
  3. Cada llave pertenece a una propiedad y solo ve esa propiedad.
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"}'

Permisos

Cada llave tiene los permisos que el hotel le da. Un permiso :write incluye el :read del mismo recurso.

  • property:read
  • availability:read
  • rates:write
  • reservations:read
  • reservations:write
  • guests:read
  • folios:read
  • folios:write
  • webhooks:manage

Límites

120 llamadas por minuto por llave (cabeceras x-ratelimit-limit y x-ratelimit-remaining; 429 rate_limited con retry-after). Cada plan incluye una cuota de llamadas por mes y propiedad (x-quota-limit, x-quota-remaining; 429 quota_exceeded hasta el día 1 del mes siguiente).

Montos

Enteros en unidades menores (centavos) de currency. Los totales de cotizaciones y folios incluyen impuestos.

Errores

Formato { "error": { "code", "message" } }. Las reglas del hotel (sin disponibilidad, estadía mínima, fechas pasadas…) responden 422 business_rule con reason. Envía Accept-Language: es para mensajes en español.

HTTPCódigoSignificado
400invalid_requestParámetros o cuerpo inválidos
401unauthorizedFalta la llave o no es válida
403insufficient_scopeLa llave no tiene el permiso (requiredScope)
403plan_requiredEl plan del hotel no incluye la API
404not_foundNo existe en esta propiedad
409duplicate_external_refYa hay una reserva con esa externalRef
409new_chargesCheck-out: entraron consumos nuevos; revisa el saldo y reintenta
409request_in_progressLa misma Idempotency-Key sigue en curso
409request_outcome_unknownUna llamada anterior con esa llave no terminó: revisa si se hizo antes de reintentar con otra llave
422business_ruleUna regla del hotel lo impide (reason)
422idempotency_mismatchIdempotency-Key repetida con otro cuerpo
429rate_limitedDemasiadas llamadas por minuto
429quota_exceededSe agotó la cuota del mes
503charges_sync_failedCheck-out: un sistema de consumos no respondió
503unavailableNo disponible por un momento; reintenta

Reservas y cargos

Las reservas y cargos siguen las mismas reglas que recepción (disponibilidad, restricciones, política de cancelación, saldo en cero para el check-out) y el huésped recibe los mismos correos. Cada cambio aparece en el historial de la reserva con el nombre de la llave. Los pagos y reembolsos se registran en recepción.

Reintentos seguros (Idempotency-Key)

Envía Idempotency-Key (p. ej. un UUID) en cada POST. Si repites la misma llamada con la misma llave en 24 h, recibes la primera respuesta (cabecera idempotent-replayed: true) en vez de crear otra reserva o cobrar dos veces. Los errores no se guardan: puedes reintentar con la misma llave después de corregir la causa.

Sincronizar

GET /reservations?updatedSince= devuelve las reservas que cambiaron después de ese instante (cualquier cambio del folio cuenta). Guarda el updatedAt de la última que procesaste y vuelve a preguntar con unos minutos de traslape; sigue nextCursor hasta que sea null. Con webhooks te enteras al momento; la consulta periódica es el respaldo.

Webhooks

Registra una URL https con POST /webhooks y los eventos que quieres; llegan cambios de cualquier origen (recepción, motor de reservas, canales, la API). Revenia envía un POST con el evento en segundos, firmado en la cabecera Revenia-Signature. Responde 2xx en menos de 10 s; si no, se reintenta durante unos 2 días (1 min, 5 min, 30 min, 2 h, 6 h, 12 h, 24 h). Una URL que falla 3 días se apaga; se reactiva con PATCH active: true. Un evento puede llegar más de una vez o en otro orden: descarta repetidos por id y compara el updatedAt de la reserva.

Eventos

  • 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)

Verificar la firma (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);
}

Agentes de IA: buscar, comparar y reservar

Cualquier asistente de IA (ChatGPT, Claude, Perplexity o el agente de una agencia de viajes) puede buscar y comparar los hoteles de Revenia, ver su ficha completa, encontrar las mejores fechas, cotizar y reservar para su usuario, sin credencial y con las mismas reglas, precios y depósito que el motor de reservas del hotel. Para cambiar o cancelar, el huésped confirma con un código que le llega por correo. Solo aparecen los hoteles que activaron «Visible para agentes de IA» en Configuración → Motor de reservas.

Conéctalo como servidor MCP remoto, sin autenticación:

  • ChatGPT: Configuración → Conectores → crear un conector personalizado con la URL https://revenia.ai/mcp y sin autenticación.
  • Claude (claude.ai o la app): Configuración → Conectores → Agregar conector personalizado con la URL https://revenia.ai/mcp.
  • Claude Code, Cursor y otros clientes MCP: un servidor HTTP con la URL https://revenia.ai/mcp (ejemplo abajo).
# 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" } } }

Herramientas

  • 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: tu asistente de IA conectado al hotel

Con el servidor MCP de Revenia, tu asistente de IA (Claude, Cursor y otros compatibles) consulta y opera el hotel en lenguaje natural: «¿quién llega mañana?», «¿cuánto debe la 101?», «mueve la reserva de Ana al viernes». Usa una llave de Configuración → API: las herramientas disponibles dependen de sus permisos, siguen las mismas reglas que recepción, cada cambio queda en el historial con el nombre de la llave y cada mensaje cuenta para los límites de la API. El asistente debe confirmar contigo antes de cambiar algo.

En claude.ai o ChatGPT: agrega un conector personalizado con la URL https://revenia.ai/api/mcp y entra con tu cuenta de Revenia; eliges la propiedad y los permisos (solo los que tu rol tiene) y la conexión aparece en Configuración → API, donde la puedes revocar. En Claude Code, Claude Desktop, Cursor o n8n también puedes usar una llave:

# 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_…" }
    }
  }
}

Herramientas

  • 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

Referencia

Descripciones en inglés, del contrato OpenAPI.

  • GET/propertyPermiso: property:read

    The property of the API key

  • GET/room-typesPermiso: property:read

    Room types

  • GET/roomsPermiso: property:read

    Rooms

  • GET/availabilityPermiso: 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/quotesPermiso: 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/reservationsPermiso: reservations:read

    List reservations (sync)

    Ordered by updatedAt. Archived (test) reservations only with status=ARCHIVED.

  • POST/reservationsPermiso: 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}Permiso: reservations:read

    One reservation

  • PATCH/reservations/{ref}Permiso: 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}/cancelPermiso: 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-inPermiso: 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-outPermiso: 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/chargesPermiso: 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}/folioPermiso: folios:read

    Folio of a reservation

    Charges, payments and refunds, voided ones included (voided: true), with totals.

  • GET/webhooksPermiso: webhooks:manage

    Webhooks of this API key

  • POST/webhooksPermiso: 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}Permiso: webhooks:manage

    One webhook

  • PATCH/webhooks/{id}Permiso: 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}Permiso: webhooks:manage

    Delete a webhook

  • POST/webhooks/{id}/testPermiso: webhooks:manage

    Send a test event

    Sends a signed `ping` event now (no retries) and returns the result.

  • POST/webhooks/{id}/rotate-secretPermiso: webhooks:manage

    Rotate the signing secret

    The previous secret stops working immediately.

  • GET/webhooks/{id}/deliveriesPermiso: webhooks:manage

    Recent deliveries

    Newest first; kept 30 days.

  • POST/webhooks/{id}/deliveries/{deliveryId}/retryPermiso: 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/guestsPermiso: guests:read

    Guests with reservations at this property

  • GET/guests/{id}Permiso: guests:read

    One guest and their reservations at this property