OnSeasonChannel API

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

ParameterTypeDescription
displayCurrencystringISO 4217 currency for the amounts in the response.

Request body

FieldRequiredTypeDescription
quoteTokenYesstringquoteToken from the latest quote, unchanged.
listingIDYesUUIDMust match the quote.
checkInDateYesdateMust match the quote.
checkOutDateYesdateMust match the quote.
guestsYesnumberTotal guests. Must match the quote.
guestYesobjectLead guest: firstName, lastName, email, and phone.
adultsYesnumberAdults. Minimum 1.
childrenNonumberChildren. Minimum 0.
infantsNonumberInfants. Minimum 0.
petsNonumberPets. Minimum 0.
notesYesstringNotes for the host. Send "" when there are none.
sourceYesstringYour channel name, as agreed with OnSeason.
totalBasePriceYesnumbercomponents.basePrice.amount from the quote.
totalFeePriceYesnumbercomponents.fees.amount from the quote, or 0.
totalTaxPriceYesnumbercomponents.taxes.amount from the quote, or 0.
totalPriceYesnumbertotalPrice from the quote.
currencyNostringcurrency from the quote.
unitIDNoUUIDThe unit you quoted. Must match the quote when sent.
roomTypeNostringThe room type you quoted, when you used one.
commissionNoobjectYour commission: amount (required, at least 0) and description (optional).
reservationTypeNoenumINSTANT_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

StatusMeaning
201Reservation created. Check that status is CONFIRMED.
400The request is invalid, the token is missing, expired, or doesn't match the request, or the dates are no longer available.
401The API key is missing or invalid.
403The listing is not enabled for your channel, or the API key is not allowed to create reservations.
404The listing or unit was not found.
422The 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.

On this page