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.
| Endpoint | What it does | When 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.
- Optional test setup:
POST /test/generate - Pull data:
GET /reservations/pending/full - Import reservations into PMS
- Acknowledge:
POST /reservations/sync/bulk
Alternative: one-by-one pull flow
Use this if your PMS wants to manage a queue of booking IDs first.
- Get IDs:
GET /reservations/pending - Fetch details:
GET /reservations/{booking_id} - Import reservations into PMS
- Acknowledge:
POST /reservations/sync/bulk
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>
hotel_id you send must match the key scope.
Get pending booking IDs
Returns a lightweight list of bookings waiting to be imported.
| Parameter | Required | Notes |
|---|---|---|
hotel_id | Yes | Must match API key scope. |
limit | No | Default 100, maximum 500. |
page_token | No | Use 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());
{
"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"
}
]
}
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.
| Parameter | Where | Notes |
|---|---|---|
booking_id | Path | Booking ID from pending response. |
hotel_id | Query | Required. 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());
{
"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.
| Parameter | Required | Notes |
|---|---|---|
hotel_id | Yes | Must match API key scope. |
limit | No | Default 50, maximum 200. |
page_token | No | Use next_page_token from previous response. |
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());
{
"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"
}
}
]
}
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.
| Field | Required | Notes |
|---|---|---|
hotel_id | Yes | Hotel scoped to the API key. |
items | Yes | Array. Maximum 100 items. |
booking_id | Yes | Booking imported into PMS. |
event_id | Yes | Event ID from pending response. |
status | Yes | Must be success. |
pms_reference | No | Your 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());
{
"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
}
]
}
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.
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.
| Field | Required | Notes |
|---|---|---|
count | No | Default 10, maximum 30. |
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());
{
"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": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid API key",
"request_id": "req_..."
}
}