Bookings and Reservations

Booking Experts only supports creating reservations for a single accommodation. Support for ordering multiple accommodations is under active development. This will be accomplished by supporting Bookings. Bookings will be able to contain multiple Reservations, as illustrated in the diagram below.

The current API leverages this redesigned structure by already supporting creation of bookings with a single reservation via POST reservation. When creation of bookings with multiple reservations is added, this will become possible via the endpoint POST booking.


Sideposting

As can be seen in the diagram, a Reservation belongs to a Booking and and Booking belongs to a Customer.

As the JSON:API specification does not yet support submission of multiple resources at once, we have introduced the concept of Sideposting. Sideposting will allow you to post new related resources using the standard included array. These resources don't have an ID yet, therefore it is required to link these resources by setting /meta/temp_id. You will also need to define what the system needs to do with these resources by specifying /meta/method (= create or update) on the relationship.


Creating a reservation

For a reservation to be accepted, you need to (at least) specify the following attributes:

  • start_date
  • end_date
  • rentable_type
  • guest_group

A valid example can be seen below - Endpoint : POST reservation.

{
  "data": {
    "type": "reservation",
    "attributes": {
      "start_date": "2014-01-08",
      "end_date": "2014-01-15",
      "guest_group": {
        "seniors": 0,
        "adults": 2,
        "adolescents": 0,
        "children": 0,
        "babies": 0,
        "pets": 0
      }
    },
    "relationships": {
      "rentable_type": {
        "data": {
          "id": "123",
          "type": "rentable_type"
        }
      }
    }
  }
}
 

Creating an option

In Booking Experts, an option is a reservation that expires after a configured amount of days when it is not accepted by the customer. To create an option, you can use the same endpoint and pass the option_validity attribute, which defines the option validity in days.

📘

line 8 ⬇️

Notice the difference with the code above

//Endpoint: POST reservation
{
  "data": {
    "type": "reservation",
    "attributes": {
      "start_date": "2014-01-08",
      "end_date": "2014-01-15",
      "option_validity": 5,
      "guest_group": {
        "seniors": 0,
        "adults": 2,
        "adolescents": 0,
        "children": 0,
        "babies": 0,
        "pets": 0
      }
    },
    "relationships": {
      "rentable_type": {
        "data": {
          "id": "123",
          "type": "rentable_type"
        }
      }
    }
  }
}


Creating a reservation with customer and booking details

It's essential to add Customer details. Booking attributes are relevant as well, like redeemable codes or an after payment return url. In this case, you will need to use Sideposting to include these details when creating a new reservation.

The example below is a more complex example that shows how this can be done.
This example does not only specify booking and customer details, but also includes some ordered extras (ExtraOrderItem) and a couple of custom cancellation rules (CancellationRule). Resource linking has been realized by properly setting temp_id. As these resources are new, the _method _passed in each relationship is create.

{
  "data": {
    "type": "reservation",
    "attributes": {
      "start_date": "2014-01-08",
      "end_date": "2014-01-15",
      "late_checkout": false,
      "locale": "en",
      "note": "Please note",
      "currency": "EUR",
      "price_according_to_channel": { "currency": "EUR", "value": "85.75" },
      "guest_group": {
        "seniors": 0,
        "adults": 1,
        "adolescents": 0,
        "children": 1,
        "babies": 0,
        "pets": 0
      },
      "license_plates": ["19XNZ1"]
    },
    "relationships": {
      "rentable_type": {
        "data": {
          "id": "123",
          "type": "rentable_type"
        }
      },
      "booking": {
        "data": {
          "type": "booking",
          "meta": {
            "temp_id": "booking-id",
            "method": "create"
          }
        }
      },
      "primary_package": {
        "data": {
          "id": "1",
          "type": "primary_package"
        }
      },
      "extra_packages" : {
        "data": [{
          "id": "101",
          "type": "extra_package"
        }]
      },
      "extra_order_items": {
        "data": [{
          "type": "extra_order_item",
          "meta": {
            "temp_id": "extra-id",
            "method": "create"
          }
        }]
      },
      "cancellation_rules": {
        "data": [{
          "type": "cancellation_rule",
          "meta": {
            "temp_id": "cancellation-rule-1",
            "method": "create"
          }
        },{
          "type": "cancellation_rule",
          "meta": {
            "temp_id": "cancellation-rule-2",
            "method": "create"
          }
        }]
      }
    }
  },
  "included": [{
    "type": "booking",
    "attributes": {
      "remote_booking_nr": "12345",
      "after_payment_return_url": "https://www.example.com/callback",
      "redeemable_codes": ["DISCOUNT2021"]
    },
    "relationships": {
      "customer": {
        "data": {
          "type": "customer",
          "meta": {
            "temp_id": "customer-id",
            "method": "create"
          }
        }
      }
    },
    "meta": {
      "temp_id": "booking-id"
    }
  },{
    "type": "customer",
    "attributes": {
      "title": "family",
      "first_name": "RIV",
      "last_name": "Rowe",
      "email": "[email protected]",
      "phone": "+31612345678",
      "is_company": false,
      "date_of_birth": "1955-05-05",
      "receive_newsletter": false,
      "address": "78229 Marquerite Flat",
      "number": null,
      "postalcode": "03756",
      "city": "Amsterdam",
      "country_code": "NL",
      "has_custom_invoice_details": true,
      "custom_invoice_details": {
        "name": "Ali",
        "email": "[email protected]",
        "address": "Het Eeftink 11-12",
        "postalcode": "7541 WH",
        "city": "Enschede",
        "country_code": "NL"
      }
    },
    "meta": {
      "temp_id": "customer-id"
    }
  },{
    "type": "extra_order_item",
    "attributes": {
      "quantity": 2,
      "guest_answer": "Guest answer"
    },
    "relationships": {
      "extra": {
        "data": {
          "id": "245",
          "type": "extra"
        }
      }
    },
    "meta": {
      "temp_id": "extra-id"
    }
  },{
    "type": "cancellation_rule",
    "attributes": {
      "percentage": 33.3,
      "days_before_arrival": 28,
      "administration_costs": { "currency": "EUR", "value": "0.00" }
    },
    "meta": {
      "temp_id": "cancellation-rule-1"
    }
  },{
    "type": "cancellation_rule",
    "attributes": {
      "percentage": 100,
      "days_before_arrival": 14,
      "administration_costs": { "currency": "EUR", "value": "10.00" }
    },
    "meta": {
      "temp_id": "cancellation-rule-2"
    }
  }]
}
 

Updating a reservation

A reservation, it's booking, and customer details can be updated in the same way as creating reservations.
For existing resources you are now required to pass their actual ID in stead of their temp_id. If you like to update a related resource, you need to explicitly specify this by setting /meta/method to update on the relationship.
As an example, consider the code below : it shows an example of how to update the redeemable codes array of the booking of a reservation.

{
  "data": {
    "id": "3245",
    "type": "reservation",
    "attributes": {
      "start_date": "2014-01-08",
      "end_date": "2014-01-15",
      "currency": "EUR",
      "guest_group": {
        "seniors": 0,
        "adults": 2,
        "adolescents": 0,
        "children": 0,
        "babies": 0,
        "pets": 0
      }
    },
    "relationships": {
      "rentable_type": {
        "data": {
          "id": "123",
          "type": "rentable_type"
        }
      },
      "booking": {
        "data": {
          "id": "3245",
          "type": "booking",
          "meta": {
            "method": "update"
          }
        }
      }
    }
  },
  "included": [{
    "id": "3245",
    "type": "booking",
    "attributes": {
      "redeemable_codes": ["DISCOUNT2021", "FREEFORALL"]
    }
  }]
}
 

Cancelling a reservation

A reservation can be cancelled by sending an existing reservation resource. Optionally, you can pass the cancel_date and cancel_reason attributes to set a custom date and reason for cancelling.

{
  "data": {
    "id": "3245",
    "type": "reservation",
    "attributes": {
      "cancel_date": "2014-01-05",
      "cancel_reason": "COVID",
    }
  }
}
 

Payment of reservations

To initiate a payment of a Reservation, look at the Booking of the Reservation.

When this Booking can be paid, it will have a pay_url link to which the user should be redirected.
After payment, the user will be redirected back to the after_payment_return_url that was set when the Booking was created.
A query parameter status will be passed that can have the following values:

  • success : The amount has been paid. You can show a 'Thank you' page. The guest has received a confirmation of the Reservation via email.
  • open_but_confirmed : The amount has not been paid yet, but the Reservation has been confirmed. This can happen when a bank transfer is initiated. It should be handled as a success.
  • failure : The payment has been failed, but there is a possibility that the status may still change. In the booking process, this should be handled in the same way as failure_final.
  • failure_final : The payment has failed and this will not change anymore (end state). You can ask the user to initiate a new payment by rendering a payment link to the pay_url again.
  • cancelled: The payment has been cancelled. You can ask the user to initiate a new payment by rendering a payment link to the pay_url again.

Error codes

When mutating reservations fail, error messages usually contain a detailed error code in meta.error, which you can use to generate a custom error message.

  • allotment_full: Accommodation is already occupied or not rentable for the given period since it is needed for an allotment
  • blocked: Accommodation is blocked
  • cannot_be_in_the_past: Start date cannot be in the past
  • current_allotment_full: Accommodation is full for the current channel
  • invalid_guest_group: The specified guest group is invalid
  • maximum_stay: Reservation exceeds maximum stay
  • minimum_stay: Reservation does not meet minimum stay
  • must_contain_at_least_one_adult_or_senior: At least one senior or adult must be part of the guest group
  • no_accommodation_found_for_amenities: No accommodation found for the passed amenities
  • no_accommodation_found_for_pets: No accommodation found in which pets are allowed
  • no_arrival: The given start date is not a valid arrival date
  • no_available_accommodation_found: All accommodations are occupied in the given period
  • no_checkout: The given start date is not a departure date
  • no_prices: There are no prices in the given period for the chosen accommodation
  • occupied: The accommodation is already occupied in the given period
  • past_or_already_closed_reception: The arrival date is in the past or the reception has already been closed for today