RevenueTales PMS API Clear integration guide
Start here

Move bookings from RevenueTales into your PMS

The API works like a queue. You pull bookings that need importing, import them into your PMS, then acknowledge them so they are not returned again.

What each endpoint is used for

This is the high-level view. The detailed examples are below.

EndpointWhat it doesWhen to use it
GET /reservations/pending Gets booking IDs only. Returns pending booking_id and event_id. Use when your PMS wants to first collect IDs, then fetch each reservation separately.
GET /reservations/{booking_id} Gets full details for one booking. Use after /pending when you need the reservation payload for a specific booking.
GET /reservations/pending/full Gets booking IDs and full reservation details together. Use instead of /pending + /reservations/{booking_id} when you want fewer calls.
POST /reservations/sync/bulk Acknowledges many imported bookings. Use after importing bookings successfully. This is the recommended sync endpoint.
POST /reservations/{booking_id}/sync Acknowledges one imported booking. Use for debugging, manual retries, or very small integrations.
POST /test/generate Creates test bookings and pending events. Use only for TEST hotel onboarding when you need fresh test data.

Choose your integration flow

The pull steps are interchangeable. The sync step is always required after successful PMS import.

Recommended: bulk pull flow

Use this if your PMS can process full reservation payloads in one batch.

  1. Optional test setup: POST /test/generate
  2. Pull data: GET /reservations/pending/full
  3. Import reservations into PMS
  4. Acknowledge: POST /reservations/sync/bulk
Fewer API calls Best for batch jobs

Alternative: one-by-one pull flow

Use this if your PMS wants to manage a queue of booking IDs first.

  1. Get IDs: GET /reservations/pending
  2. Fetch details: GET /reservations/{booking_id}
  3. Import reservations into PMS
  4. Acknowledge: POST /reservations/sync/bulk
More control Good for custom queues
Interchangeable endpoints: GET /reservations/pending/full can replace GET /reservations/pending plus GET /reservations/{booking_id}. After import, you still need to call a sync endpoint.

Base URL and authentication

Use this base URL and bearer token on every request.

Base URL

https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1

Required header

Authorization: Bearer <API_KEY>

API keys are scoped to one PMS and one hotel. Any hotel_id you send must match the key scope.

Get pending booking IDs

Returns a lightweight list of bookings waiting to be imported.

GET /reservations/pending
ParameterRequiredNotes
hotel_idYesMust match API key scope.
limitNoDefault 100, maximum 500.
page_tokenNoUse next_page_token from previous response.
curl -X GET "https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/pending?hotel_id=9b767697-4f60-4b78-b387-b6f69ccdb7bc&limit=100" \
  -H "Authorization: Bearer <API_KEY>"
import requests

url = "https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/pending"
headers = {"Authorization": "Bearer <API_KEY>"}
params = {"hotel_id": "9b767697-4f60-4b78-b387-b6f69ccdb7bc", "limit": 100}

response = requests.get(url, headers=headers, params=params, timeout=30)
print(response.status_code)
print(response.json())
const url = new URL("https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/pending");
url.searchParams.set("hotel_id", "9b767697-4f60-4b78-b387-b6f69ccdb7bc");
url.searchParams.set("limit", "100");

const response = await fetch(url, {
  headers: { Authorization: "Bearer <API_KEY>" }
});

console.log(response.status);
console.log(await response.json());
Sample response
{
  "api_version": "v1",
  "hotel_id": "9b767697-4f60-4b78-b387-b6f69ccdb7bc",
  "next_page_token": "eyJkb2NfaWQiOiIuLi4ifQ==",
  "reservations": [
    {
      "event_id": "e4e4b3bc-4d2a-4b41-b8b8-69f4f5f51309",
      "booking_id": "d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9",
      "sync_type": "NEW",
      "updated_at": "2026-05-17T10:30:42Z"
    }
  ]
}
Pagination: if next_page_token exists, call the same endpoint again with the same hotel_id and limit, plus page_token=eyJkb2NfaWQiOiIuLi4ifQ==. Stop when no token is returned.

Get reservation details

Returns the full reservation payload for one booking ID.

GET /reservations/{booking_id}
ParameterWhereNotes
booking_idPathBooking ID from pending response.
hotel_idQueryRequired. Must match API key scope.
curl -X GET "https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9?hotel_id=9b767697-4f60-4b78-b387-b6f69ccdb7bc" \
  -H "Authorization: Bearer <API_KEY>"
import requests

booking_id = "d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9"
url = f"https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/{booking_id}"
headers = {"Authorization": "Bearer <API_KEY>"}
params = {"hotel_id": "9b767697-4f60-4b78-b387-b6f69ccdb7bc"}

response = requests.get(url, headers=headers, params=params, timeout=30)
print(response.json())
const bookingId = "d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9";
const url = new URL(`https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/${bookingId}`);
url.searchParams.set("hotel_id", "9b767697-4f60-4b78-b387-b6f69ccdb7bc");

const response = await fetch(url, {
  headers: { Authorization: "Bearer <API_KEY>" }
});
console.log(await response.json());
Sample response
{
  "booking_id": "d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9",
  "external_booking_id": "AGENCY-9988",
  "hotel_name": "ABC",
  "hotel_id": "9b767697-4f60-4b78-b387-b6f69ccdb7bc",
  "room_name": "Double Room",
  "room_id": "DBL",
  "board_name": "Bed & Breakfast",
  "board_id": 3,
  "rate": "500",
  "currency": "EUR",
  "guest_name": "John Smith",
  "number_of_pax": 3,
  "number_of_adults": 2,
  "number_of_kids": 1,
  "number_of_infants": 0,
  "booking_date": "2026-05-16T10:00:00Z",
  "check_in_date": "2026-06-01",
  "check_out_date": "2026-06-05",
  "source": "Travel Agency ABC",
  "status": "CONFIRMED",
  "special_requests": "",
  "cancellation_reason": null,
  "updated_at": "2026-05-16T10:00:00Z"
}

Get pending bookings with full details

This is the simpler pull option. It combines pending booking IDs and reservation details in one response.

GET /reservations/pending/full
ParameterRequiredNotes
hotel_idYesMust match API key scope.
limitNoDefault 50, maximum 200.
page_tokenNoUse next_page_token from previous response.
This endpoint is interchangeable with GET /reservations/pending + GET /reservations/{booking_id}.
curl -X GET "https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/pending/full?hotel_id=9b767697-4f60-4b78-b387-b6f69ccdb7bc&limit=50" \
  -H "Authorization: Bearer <API_KEY>"
import requests

url = "https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/pending/full"
headers = {"Authorization": "Bearer <API_KEY>"}
params = {"hotel_id": "9b767697-4f60-4b78-b387-b6f69ccdb7bc", "limit": 50}

response = requests.get(url, headers=headers, params=params, timeout=30)
print(response.json())
const url = new URL("https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/pending/full");
url.searchParams.set("hotel_id", "9b767697-4f60-4b78-b387-b6f69ccdb7bc");
url.searchParams.set("limit", "50");

const response = await fetch(url, {
  headers: { Authorization: "Bearer <API_KEY>" }
});
console.log(await response.json());
Sample response
{
  "api_version": "v1",
  "hotel_id": "9b767697-4f60-4b78-b387-b6f69ccdb7bc",
  "next_page_token": "eyJkb2NfaWQiOiIuLi4ifQ==",
  "meta": {
    "missing_reservations": 0,
    "returned_count": 1
  },
  "items": [
    {
      "event_id": "e4e4b3bc-4d2a-4b41-b8b8-69f4f5f51309",
      "booking_id": "d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9",
      "sync_type": "NEW",
      "event_created_at": "2026-05-17T10:30:42Z",
      "reservation": {
        "booking_id": "d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9",
        "external_booking_id": "AGENCY-9988",
        "hotel_name": "ABC",
        "hotel_id": "9b767697-4f60-4b78-b387-b6f69ccdb7bc",
        "room_name": "Double Room",
        "board_name": "Bed & Breakfast",
        "rate": "500",
        "currency": "EUR",
        "guest_name": "John Smith",
        "check_in_date": "2026-06-01",
        "check_out_date": "2026-06-05",
        "status": "CONFIRMED"
      }
    }
  ]
}
Pagination works the same way as pending IDs. Send the same hotel_id and limit, plus page_token from the previous response.

Acknowledge imported bookings in bulk

Use this after successful PMS import. This is the recommended acknowledgement endpoint.

POST /reservations/sync/bulk
FieldRequiredNotes
hotel_idYesHotel scoped to the API key.
itemsYesArray. Maximum 100 items.
booking_idYesBooking imported into PMS.
event_idYesEvent ID from pending response.
statusYesMust be success.
pms_referenceNoYour PMS reservation reference.
curl -X POST "https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/sync/bulk" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "hotel_id": "9b767697-4f60-4b78-b387-b6f69ccdb7bc",
    "items": [
      {
        "booking_id": "d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9",
        "event_id": "e4e4b3bc-4d2a-4b41-b8b8-69f4f5f51309",
        "status": "success",
        "pms_reference": "PMS-1001"
      }
    ]
  }'
import requests

url = "https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/sync/bulk"
headers = {"Authorization": "Bearer <API_KEY>", "Content-Type": "application/json"}
payload = {
    "hotel_id": "9b767697-4f60-4b78-b387-b6f69ccdb7bc",
    "items": [
        {
            "booking_id": "d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9",
            "event_id": "e4e4b3bc-4d2a-4b41-b8b8-69f4f5f51309",
            "status": "success",
            "pms_reference": "PMS-1001",
        }
    ],
}

response = requests.post(url, headers=headers, json=payload, timeout=30)
print(response.json())
const response = await fetch(
  "https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/sync/bulk",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer <API_KEY>",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      hotel_id: "9b767697-4f60-4b78-b387-b6f69ccdb7bc",
      items: [
        {
          booking_id: "d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9",
          event_id: "e4e4b3bc-4d2a-4b41-b8b8-69f4f5f51309",
          status: "success",
          pms_reference: "PMS-1001"
        }
      ]
    })
  }
);
console.log(await response.json());
Sample response
{
  "api_version": "v1",
  "hotel_id": "9b767697-4f60-4b78-b387-b6f69ccdb7bc",
  "summary": {
    "total": 1,
    "acknowledged": 1,
    "idempotent": 0,
    "failed": 0
  },
  "results": [
    {
      "booking_id": "d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9",
      "event_id": "e4e4b3bc-4d2a-4b41-b8b8-69f4f5f51309",
      "sync_status": "ACKNOWLEDGED",
      "synced_at": "2026-05-17T10:35:11Z",
      "idempotent": false
    }
  ]
}
Bulk sync accepts up to 100 items per request. If you imported more than 100 bookings, send multiple bulk sync requests.

Acknowledge one booking

Use for debugging, manual retries, or very small integrations. Bulk sync is preferred for normal operation.

POST /reservations/{booking_id}/sync
curl -X POST "https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9/sync" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "event_id": "e4e4b3bc-4d2a-4b41-b8b8-69f4f5f51309",
    "status": "success",
    "pms_reference": "PMS-1001"
  }'
import requests

booking_id = "d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9"
url = f"https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/{booking_id}/sync"
headers = {"Authorization": "Bearer <API_KEY>", "Content-Type": "application/json"}
payload = {
    "event_id": "e4e4b3bc-4d2a-4b41-b8b8-69f4f5f51309",
    "status": "success",
    "pms_reference": "PMS-1001",
}
response = requests.post(url, headers=headers, json=payload, timeout=30)
print(response.json())
const bookingId = "d2f0ee30-f7aa-4d39-9df9-f181ec4cf5f9";
const response = await fetch(
  `https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/reservations/${bookingId}/sync`,
  {
    method: "POST",
    headers: {
      Authorization: "Bearer <API_KEY>",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      event_id: "e4e4b3bc-4d2a-4b41-b8b8-69f4f5f51309",
      status: "success",
      pms_reference: "PMS-1001"
    })
  }
);
console.log(await response.json());

Generate test data

Creates TEST hotel reservations and pending events for onboarding. Use this only with TEST hotel API keys.

POST /test/generate
FieldRequiredNotes
countNoDefault 10, maximum 30.
The endpoint only works for TEST hotel a1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1. If non-acknowledged TEST events already exist for the calling PMS, generation is skipped.
curl -X POST "https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/test/generate" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"count": 10}'
import requests

url = "https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/test/generate"
headers = {"Authorization": "Bearer <API_KEY>", "Content-Type": "application/json"}
payload = {"count": 10}
response = requests.post(url, headers=headers, json=payload, timeout=30)
print(response.json())
const response = await fetch(
  "https://travelagents-pms-integration-670805103288.europe-west1.run.app/api/v1/test/generate",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer <API_KEY>",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ count: 10 })
  }
);
console.log(await response.json());
Sample response
{
  "api_version": "v1",
  "hotel_id": "a1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1",
  "hotel_name": "TEST",
  "pms_id": "TEST_PMS",
  "generated_count": 10,
  "generated": [
    {
      "booking_id": "8f0e0d3c-8f66-4a75-95c9-6058d6948a8a",
      "event_id": "15337d3d-1042-4e2a-b7af-b3efe84af81a"
    }
  ]
}

Errors

All errors return a consistent JSON object. Keep the request_id for support.

Error shape
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Missing or invalid API key",
    "request_id": "req_..."
  }
}
401 missing/invalid key 403 unauthorized hotel 404 not found 422 validation error 500 server error