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
- En Revenia: Configuración → API → Nueva llave. Elige los permisos que necesita tu sistema y copia la llave (se muestra una sola vez).
- Envía la llave en cada llamada: Authorization: Bearer rvk_…
- 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.
| HTTP | Código | Significado |
|---|---|---|
| 400 | invalid_request | Parámetros o cuerpo inválidos |
| 401 | unauthorized | Falta la llave o no es válida |
| 403 | insufficient_scope | La llave no tiene el permiso (requiredScope) |
| 403 | plan_required | El plan del hotel no incluye la API |
| 404 | not_found | No existe en esta propiedad |
| 409 | duplicate_external_ref | Ya hay una reserva con esa externalRef |
| 409 | new_charges | Check-out: entraron consumos nuevos; revisa el saldo y reintenta |
| 409 | request_in_progress | La misma Idempotency-Key sigue en curso |
| 409 | request_outcome_unknown | Una llamada anterior con esa llave no terminó: revisa si se hizo antes de reintentar con otra llave |
| 422 | business_rule | Una regla del hotel lo impide (reason) |
| 422 | idempotency_mismatch | Idempotency-Key repetida con otro cuerpo |
| 429 | rate_limited | Demasiadas llamadas por minuto |
| 429 | quota_exceeded | Se agotó la cuota del mes |
| 503 | charges_sync_failed | Check-out: un sistema de consumos no respondió |
| 503 | unavailable | No 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