Prebook
Send the guests' details and re-validate the offer with the supplier. Prebook returns the combinationKey for Book, so compare its offer with Confirm before you book.
Compare with Confirm before Book
Prebook re-validates the offer with the supplier. Whether that is a live or a cached rate depends on the supplier. Before you call Book, compare the Prebook offer with what the guest accepted at Confirm, and stop if anything changed.
function assertSameOffer(confirm: any, prebook: any) {
const a = confirm.accommodation.combination;
const b = prebook.accommodation.combination;
const changed =
(prebook.warnings?.length ?? 0) > 0 ||
a.price.amount !== b.price.amount ||
a.price.currency !== b.price.currency ||
a.mealPlan.id !== b.mealPlan.id ||
JSON.stringify(a.cancellationPolicies) !== JSON.stringify(b.cancellationPolicies);
if (changed) throw new OfferChanged(a, b, prebook.warnings); // show it, ask the guest again
}Same rooms, same order
distributionsmust repeat the Quote's rooms in the same order, with the same number of people in each. Reordering rooms after the Quote, for example in your UI, triggers "order changed" errors.- The first person of the first room is the contact person. The first person of each other room is its room holder. Put the adult with an email address and phone number first. See Confirm.
- In the example,
roomHolders: ["FIRST_NAME", "EMAIL"]applies to Marta. Everyone else followsotherPersons(FIRST_NAME,LAST_NAME,TITLE), which is why the child Leo also carries acourtesyTitle.
Ages at the end of the stay
requestedAgeis the age at the end of the stay. A child who turns 6 during the stay is 6.- It must equal the
ageyou sent in the Quote. That is why the Quote should also carry each guest's age at checkout. requestedAgeis always mandatory, even whenrequiredFielddoesn't list it.- If you send
birthDate, it must be consistent withrequestedAge.
Latin names
name and lastName must match ^[\p{IsLatin} .'-]+$: Latin letters, spaces, full stops, apostrophes and hyphens only. Arabic, Cyrillic, Greek and CJK names fail. Transliterate them before Prebook, and keep the original script in your own records.
Pre-validate every guest with the spec's patterns so the guest can fix their input before you call Prebook:
const LATIN_NAME = /^[\p{Script=Latin} .'-]+$/u; // spec: ^[\p{IsLatin} .'-]+$
const PHONE = /^[0-9]{1,15}$/; // digits only, max 15
const PHONE_CC = /^\+[0-9]{1,3}$/; // "+34", never "0034"
const DOCUMENT = /^[A-Za-z0-9]+(?:-[A-Za-z0-9]+){0,2}$/; // max 20 characters
export function guestErrors(p: Person): string[] {
const e: string[] = [];
if (p.name && !LATIN_NAME.test(p.name)) e.push('name: Latin letters only, transliterate first');
if (p.lastName && !LATIN_NAME.test(p.lastName)) e.push('lastName: Latin letters only');
if (p.phone && !PHONE.test(p.phone)) e.push('phone: digits only, max 15');
if (p.phoneCountryCode && !PHONE_CC.test(p.phoneCountryCode)) e.push('phoneCountryCode: + and 1 to 3 digits');
if (p.documentNumber && (p.documentNumber.length > 20 || !DOCUMENT.test(p.documentNumber)))
e.push('documentNumber: letters and digits, at most two hyphens, max 20');
if (p.documentType && !p.documentNumber) e.push('documentType sent without documentNumber');
return e;
}Phone numbers are validated with Google's libphonenumber (com.google.i18n.phonenumbers). Use the same library on your side.
person or persons
The docs and Postman send distributions[].persons , while the original OpenAPI definition names the array person. Prebook accepts both.
- Send
persons, the recommended name. - Responses use
person. When parsing, accept bothpersonandpersons, and bothbirthDateandbirthdate, because some documentation samples usepersonsandbirthdate.
const guests = (d: any) => d.person ?? d.persons ?? [];
const birthDate = (p: any) => p.birthDate ?? p.birthdate;Common validation errors
Errors the API returns on guest data:
| Error | Usual cause | Fix |
|---|---|---|
| Missing the following required field of the contact person: Birthdate | A field from Confirm's requiredField was not sent. One message per person and field. | Build the form from requiredField. |
| Phone incorrect | Not a valid number for phoneCountryCode | Validate with libphonenumber. |
| Invalid email format | Malformed email | Validate before Prebook. |
| Duplicate passenger names | Two guests with the same first name and surname | Add a suffix such as JR. |
| Birthdate does not match requested age | birthDate and requestedAge disagree | Compute the age at the end of the stay. |
| Child age different from availability / order changed | requestedAge differs from the quoted age, or rooms were reordered | Same ages, same room order as the Quote. |
| Duplicate document | The same documentNumber twice | One document per guest. |
| Passport expires before end of trip | passportExpirationDate before checkout | Ask for a valid passport. |
| Document type informed without document number | documentType sent alone | Send both or neither. |
| Different number of persons in distribution / order changed | Person counts differ from the Quote | Same rooms, same counts. |
More in the FAQ.
Gotchas
- Special requests are free text only, in
commentToAccommodation. Bedding types cannot be sent. - Guest names per room are received, but only mandatory data is forwarded to suppliers.
- The booked service later calls the comment
commentsToAccommodation(plural). Map both when you read bookings. - We recommend treating Prebook as non-idempotent and never retrying it automatically.
- Payloads carry names, emails, phone numbers and documents. Store only what you need, and redact personal data before logs and analytics.
Related
- Book, the next step
- Confirm
- Rooms and guests
- FAQ
- The booking flow
Confirm
Lock the offer the guest picked. Confirm returns the first authoritative price and cancellation policy, the guest fields Prebook needs, and a new combinationKey.
Book
Close the booking with the supplier, using the key from Prebook. The status in the response tells you whether the room is confirmed. A 200 response on its own does not.