Create a reservation
Book a quoted stay at the signed price and terms.
POST/api/v1/reservation
Books the stay in a current quote. OnSeason checks the quoteToken, asks the PMS for availability again, and books at the price and under the cancellation terms that are signed into the token. A 201 with status CONFIRMED is the booking confirmation.
Query parameters
| Parameter | Type | Description |
|---|---|---|
displayCurrency | string | ISO 4217 currency for the amounts in the response. |
Request body
| Field | Required | Type | Description |
|---|---|---|---|
quoteToken | Yes | string | quoteToken from the latest quote, unchanged. |
listingID | Yes | UUID | Must match the quote. |
checkInDate | Yes | date | Must match the quote. |
checkOutDate | Yes | date | Must match the quote. |
guests | Yes | number | Total guests. Must match the quote. |
guest | Yes | object | Lead guest: firstName, lastName, email, and phone. |
adults | Yes | number | Adults. Minimum 1. |
children | No | number | Children. Minimum 0. |
infants | No | number | Infants. Minimum 0. |
pets | No | number | Pets. Minimum 0. |
notes | Yes | string | Notes for the host. Send "" when there are none. |
source | Yes | string | Your channel name, as agreed with OnSeason. |
totalBasePrice | Yes | number | components.basePrice.amount from the quote. |
totalFeePrice | Yes | number | components.fees.amount from the quote, or 0. |
totalTaxPrice | Yes | number | components.taxes.amount from the quote, or 0. |
totalPrice | Yes | number | totalPrice from the quote. |
currency | No | string | currency from the quote. |
unitID | No | UUID | The unit you quoted. Must match the quote when sent. |
roomType | No | string | The room type you quoted, when you used one. |
commission | No | object | Your commission: amount (required, at least 0) and description (optional). |
reservationType | No | enum | INSTANT_BOOKING, the default. |
OnSeason books the prices, the PMS metadata, and the cancellation terms from the token. Send the price fields from the quote anyway, because they are required.
Example
curl --request POST "https://zepl-staging.calry.app/api/v1/reservation?displayCurrency=EUR" \
--header "x-calry-api-key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
"quoteToken": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
"listingID": "80e0e6d4-48cf-4ca7-8c85-6e8d7e9d969a",
"checkInDate": "2026-12-05",
"checkOutDate": "2026-12-08",
"guest": {
"firstName": "Jane",
"lastName": "Doe",
"email": "jane.doe@example.com",
"phone": "+33123456789"
},
"guests": 4,
"adults": 3,
"children": 1,
"infants": 0,
"pets": 0,
"currency": "EUR",
"notes": "",
"source": "Acme Travel",
"totalBasePrice": 396,
"totalFeePrice": 40,
"totalTaxPrice": 50,
"totalPrice": 486,
"reservationType": "INSTANT_BOOKING"
}'{
"id": "9fa05abc-daf0-4ee4-a9e6-f959c2e8a421",
"listingID": "80e0e6d4-48cf-4ca7-8c85-6e8d7e9d969a",
"unitID": "70978a5b-c0d4-4f8c-8187-c695cac9d64e",
"integrationAccountID": "50bfb7a4-cbc4-4c33-a8c8-37ba9a75c96d",
"providerReservationID": "STAGING-BOOKING-12345",
"providerID": "staging-property-101",
"parentPropertyID": null,
"checkInDate": "2026-12-05",
"checkOutDate": "2026-12-08",
"guests": 4,
"guest": {
"id": null,
"name": "Jane Doe",
"firstName": "Jane",
"lastName": "Doe",
"email": "jane.doe@example.com",
"phone": "+33123456789"
},
"adults": 3,
"children": 1,
"infants": 0,
"pets": 0,
"status": "CONFIRMED",
"source": "Acme Travel",
"notes": "",
"originalCurrency": "EUR",
"currency": "EUR",
"totalPrice": 486,
"components": {
"basePrice": { "amount": 396, "breakdown": [] },
"fees": { "amount": 40, "breakdown": [] },
"taxes": { "amount": 50, "breakdown": [] },
"discounts": { "amount": 0, "breakdown": [] }
},
"salesChannel": "acme",
"cancellationPolicy": {
"policyID": "2f8d6b1a-9c3e-4d7f-8a1b-5e6c7d8f9a0b",
"name": "Strict",
"version": 1,
"source": "TENANT",
"salesChannel": "acme",
"timeZone": "Europe/Paris",
"fixedAt": "2026-11-10T10:00:00.000Z",
"refundable": true,
"refundableTag": "RFN",
"freeCancellationUntil": "2026-11-21T14:00:00.000Z",
"penalties": [
{ "from": "2026-11-21T14:00:00.000Z", "percentage": 50, "amount": 243, "currency": "EUR" },
{ "from": "2026-11-28T14:00:00.000Z", "percentage": 100, "amount": 486, "currency": "EUR" }
],
"description": "Full refund if cancelled before 2026-11-21 15:00 (Europe/Paris). 50% refund (243.00 EUR) if cancelled before 2026-11-28 15:00 (Europe/Paris). No refund after that."
},
"cancelledBy": null,
"cancellationCharge": null,
"createdAt": "2026-11-10T10:05:00.000Z",
"updatedAt": "2026-11-10T10:05:00.000Z",
"cancelledAt": null,
"confirmedAt": "2026-11-10T10:05:00.000Z"
}Response
A reservation object. Store id and providerReservationID.
Status codes
| Status | Meaning |
|---|---|
201 | Reservation created. Check that status is CONFIRMED. |
400 | The request is invalid, the token is missing, expired, or doesn't match the request, or the dates are no longer available. |
401 | The API key is missing or invalid. |
403 | The listing is not enabled for your channel, or the API key is not allowed to create reservations. |
404 | The listing or unit was not found. |
422 | The PMS accepted the booking but OnSeason couldn't finish recording it. The message carries the PMS reference. Don't book again. Contact support. |
A timeout doesn't mean the booking failed. Read Never retry a reservation blindly before you build retries.