NAV
Shell

Introduction

Welcome to the Arise Network documentation! We will introduce our network architecture, give examples of how to use our GraphQL interface, and provide step-by-step instructions to make your first transaction on our test network.

About Arise

Arise is a distributed network for hotels and the ecosystem of products and services around them. Our technology replaces the traditional API and cache based infrastructure in travel distribution with a model that is more similar to a distributed database that is shared by multiple organizations.

To accomplish this, we use a distributed ledger as our underlying data distribution model.

Our Network

Architecture

Our network is composed of connected nodes all running the same software. It has no central servers or single points of failure.

Network

We use distributed ledger technology to build a shared, transparent database that multiple parties can read from and write to at the same time.

Our network is private and permissioned, meaning every connected node is linked to a known identity in the real world. New nodes need to be granted permissions by the network for read and write access.

We’ve built a distributed application on top of this shared database, that can use the stored ARI, Guest and Booking information to perform complex queries or to enforce detailed booking rules.

Our network supports 500+ TPS (writes) and does not suffer the same scaling issues as a centralized service would under heavy query load. The network is currently validating and confirming bookings transactions within 3 seconds of their submission.

Sensitive data is automatically stored in full compliance with GDPR and all traffic sent between nodes is encrypted.

GraphQL

Each node provides a local cache of all hotel data and business logic that can perform complex, open-ended queries. Connectivity to other hotel applications on the network is built into the protocol.

Identity and Access

Access on the network is tied to the identity used to sign the transaction sent from each node. Each identity has a corresponding public / private key-pair that is signed by a Certificate Authority on the network. These identities are linked to specific Property or Partner records at the time of creation.

Any outside request for data or any transaction proposal without a valid signature will be rejected by the network.

Developer Experience

In order to make development on our network as simple as possible, we’ve abstracted away the underlying ledger and smart contracts behind a strongly typed GraphQL API.

The majority of 3rd party application integrations will only interact with our GraphQL API.

GraphQL

This GraphQL API makes it easy to explore the capabilities of the network while allowing developers to read and write information in a way that makes the most sense for their use case.

Our API

Why GraphQL

We’ve chosen to abstract away the network’s smart contracts behind an extensive GraphQL schema. GraphQL offers developers a way to interact with the network without needing to understand it’s underlying architecture or individual ‘smart contracts’ on the network. We also believe the rich set of developer tools in the GraphQL ecosystem will be a welcome change when compared to some of the tooling available for older API formats.

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools. - graphql.org

Benefits

No API Versioning

In GraphQL the client specifies which fields they would like in their response. GraphQL makes it easy to extend parts of the graph with new functionality or data in a way that won’t impact existing client integrations. Backwards compatibility is essential in this industry where hundreds of API integration can be built for a single client. The overhead of maintaining integrations can be significant and our goal is to reduce this load on development teams.

Strongly Typed Schema

Being strongly typed makes GraphQL queries less error prone and easier to validate before the client sends a request. A strongly typed data schema makes it possible to have rich integrations into IDEs/editors. Introspection queries make it possible to retrieve detailed information about allowed field values which is great for dynamically generating select elements in UI components.

Optimal Interactions with Smart Contracts

GraphQL allows us to optimize queries that otherwise would require multiple RESTful API calls and multiple interactions with smart contracts. Interacting with smart contracts (especially write transactions) can be complex. We’re often able to make specific optimizations around common data fetching patterns that allow us to combine multiple transactions on the network into a single transaction.

Excellent Tooling

The GraphQL ecosystem is growing. There are not only integrations for the strongly typed nature of GraphQL for editors and IDEs, but also standalone applications for GraphQL itself.

Using GraphQL

At first GraphQL can appear intimidating. Working with data as a graph can be quite a bit different from the common XML and RESTful APIs in the travel industry. We believe the advantages of having a self-documenting, strongly typed API with a thriving ecosystem of developer tools outweigh the initial learning curve.

We will provide examples throughout this documentation that any developer can run against a GraphQL endpoint on our test network to get started.

GraphQL Resources

GraphQL.org

Accessing Our API

Every node on our network runs it’s own standalone GraphQL interface and exposes an endpoint for queries and mutations at /graphql.

GraphQL queries are sent over https with the POST method.

Authentication

Example Authorization Header:

Authorization: Bearer <YOUR_GENERATED_JWT>

By default our network GraphQL interfaces are configured to check for a JWT Authorization header. This header must be included with each request made to the node’s /graphql endpoint. This includes GraphQL introspection queries that retrieve the available schema.

Our Schema

Schema Conventions

Unique IDs

There are certain types on our network that are assigned globally unique IDs. This ID format follows UUID standards with an additional prefix.

Each unique ID on our network is composed of four uppercase letters that correspond to a specific network data-type followed by a standard UUID. This code can be used to fetch individual parts of the graph through the node interface without the need to fetch information through a root node. For example, if a developer wants to fetch a single Traveler from a Reservation, they could use the Traveler’s ID in the format TRAV-XXXXXXXX-XXXX-MXXX-NXXX-XXXXXXXXXXXX and retrieve that information directly without re-fetching the parent reservation.

Type NameCodeSummary
PartnerPARTA partner on the network. An identity linked to an OTA, RMS, PMS .etc
PropertyPROPA property on the network. Maps to one physical address.
Space TypeSPTPA space type at a property. Equivalent to a room type
Space Type RateSPRTA rate for a specific space type
AvailabilityAVALA unit of availability in a space type
Rate PlanRATEA rate plan
Rate OverrideRAORA single day modification to a rate
ReservationRSRVA reservation
TravelerTRAVA Traveler. A Guest etc.
ProductPRODA Product available for booking.
InternalINTRAn internal ID used for special cases.

Field Capitalization

Any field that has a capital letter as its first character like Traveler or SpaceType has a set of subfields. Any field that begins with a lowercase letter like name or value is a Scalar type (e.g. String, Integer, Enum )

Getting Started

All clients that interact with our network must be tied to a known identity. This includes clients interacting with our test network.

Request Access

Please reach out to us at contact@arise.travel and our team will follow up with you for the next steps. You’ll be asked to provide some of your business details and will be given test credentials afterwards.

We will also provide you with the steps to gain access to a node on our test network.

Test Network Limitations

The test network is shared between multiple organizations. Any data sent to the testnet should be considered public. Please do not submit transactions that contain sensitive PII or real booking details.

Testnet Rate Limits:

Using GraphQL

GraphQL Overview

query findAllProperties {
    PropertiesConnection {
        edges {
            node {
                id
                name
                websiteURL
            }
        }
    }
}

The above query returns a JSON object structured like this:

{
  "data": {
    "PropertiesConnection": {
      "edges": [
        {
          "node": {
            "id": "PROP-fd17a3d9-9ebf-5338-a048-970fb301bd7c",
            "name": "Steiningå",
            "websiteURL": "http://www.steininga.com/"
          }
        }
      ]
    }
  }
}

In a nutshell, GraphQL lets you describe the shape of the data you want to fetch. Any API implementing the GraphQL spec defines a strongly typed schema that you can write queries and mutations against.

For example, the attached findAllPropeties query will fetch all the properties in the network.

Note that query includes three fields that will be returned in the response:

Interacting with the API

The Altair IDE

To help you quickly interact with the network and explore our schema, we’ve provided a built-in GraphQL IDE called Altair. It allows you to write GraphQL queries with auto-completion and provides in-line documentation of the API alongside your GraphQL documents. It’s the fastest way to prototype queries and mutations as you’re developing your application.

You can access the Altair IDE through the /altair endpoint in your browser. Documentation is available in under Docs button on the right side of the IDE, or with Ctrl+click on any field in your current GraphQL document.

GraphQL Operations

The GraphQL spec defines three types of operations:

A query will not have an impact on the network as a whole. It will only read data from a single node on the network and requires no additional confirmation steps before returning results. Query capacity scales as the network grows.

A mutation can (but not necessarily) mutate the state of the data on the network. These transactions will be validated by multiple nodes on the network and must be added to the ledger before they will return a result. For this reason it can take up to three seconds to receive a mutation confirmation. Multiple mutations can be run in parallel.

The subscription type reacts to events on the network, but doesn’t modify any data by itself.

Making a GraphQL Request

When you’re ready to integrate our API into your project, you will need to POST your GraphQL requests to the /graphql endpoint. Any http client library can do this, but we recommend using a GraphQL client library in your language of choice.

There are a variety of tools that can make your day-to-day operations more efficient with GraphQL. You can either use simple GraphQL clients that will just bind the correct headers and parameters to the HTTP endpoint, or use more advanced libraries that generate code based on your GraphQL requests and retain type safety.

Connection Queries

GraphQL is all about exposing a graph of entities and their relationships. The Relay spec defines a set of rules to make it easier to work with one-to-many relationships, or any large collection of entities.


query propertiesForward {
  PropertiesConnection(first: 10, after: "g2wAAAACaAJkA...") {}
    edges {
      cursor
      node {
        id
        name
      }
    }
    pageInfo {
      endCursor
      hasNextPage
    }
  }
}

Both forward and backward pagination are supported by the Relay spec.

query propertiesBackwards {
  PropertiesConnection(last: 10, before: "g2wAAAACaAJkA...") {}
    edges {
      cursor
      node {
        id
        name
      }
    }
    pageInfo {
      startCursor
      hasPreviousPage
    }
  }
}

Relay enables cursor based pagination. It is different than traditional offset based pagination that uses limit and offset parameters to generate the view of a page. Instead, cursors are assigned to every returned entity and are used to set the starting point of the page we want to fetch. In other words, instead of providing the absolute position of the view in the dataset, we use a relative positioning method for this view. This strategy enables faster data retrieval for large datasets.

Because of this relative positioning, the dataset can be navigated in two different ways:

Working With Mutations

For resources updates, we provide update and patch operations. They all have very similar type signatures, but they behave in different ways:

Setting Up Test Data

Now that we’ve covered the basics of GraphQL, let’s add some data in the test network. You’ll only need access to the Altair IDE and GraphQL endpoint provided when you receive your testnet credentials.

This guide will help you get familiar with the Arise data schema and how every entity in the network is represented. This should also give you some rough idea on how partners will interact with each other within the network.

For the sake of this example, we’ll be interacting with the network as a Property Management System.

Create a Partner

mutation createPartner {
  createPartner(payload: {
    name: "My Cloud PMS"
    CategoryTags: PMS
    # ...
  }) {
    id
    name
  }
}

Example output of the above query:

{
  "data": {
    "createPartner": {
      "id": "PART-1049c31f-b235-58e8-868b-4c5d8b9dba05",
      "name": "My Cloud PMS"
    }
  }
}

In order for your service to be visible to the network, you’ll need to be register as a Partner. This will represent your company and will generate a unique ID.

Create a Property

mutation {
  createProperty(payload: {
    name: "Deluxe Resort & Spa"
    PartnerExtRefs: {
      partnerId: "PART-1049c31f-b235-58e8-868b-4c5d8b9dba05",
      externalId: "255143"
    }
    # ...
  }) {
    id
    PartnerExtRefs {
      Partner {
        id
      }
      externalId
    }
  }
}

Example output of the above query:

{
  "data": {
    "createProperty": {
      "id": "PROP-fd17a3d9-9ebf-5538-a048-970fb301bd7c",
      "PartnerExtRefs": {
        "Partner" : {
          "id": "PART-1049c31f-b235-58e8-868b-4c5d8b9dba05"
        },
        "externalId": "255143"
      }
    }
  }
}

Now you need to register a hotel that your PMS manages. This is done by calling the createProperty mutation. You probably already have a unique identifier for the Property in your system and the schema allows you to store those mappings through the PartnerExtRefs field.

Query Your Property

# This query uses GraphQL variables to pass dynamic values into a static query

query getPropertyByExternalId($externalId: String!, $partnerId: partnerId!) {
  PropertiesConnection(query: {
    PartnerExtRefsList: {
      partnerId: $partnerId
      externalIds: [$externalId]
    }
  }) {
    edges {
      node {
        id
        name
        # ...
      }
    }
  }
}

When you want to fetch your property’s details on the network, you don’t have to manage a mapping of the network’s identifiers to yours as the PartnerExtRefs fields already contains them. You can then query the property directly using the query argument of the PropertiesConnection query field.

Create Your SpaceType and RatePlan

Next, you’ll need to populate your hotel’s inventory on the network. This is done by creating the SpaceType and RatePlan entities for each of your Property instances.

Create a SpaceType

mutation createSpaceType ($propertyId: propertyId!) {
  createSpaceType(
    payload: {
      propertyId: $propertyId
      name: "Double Room"
      isActive: true
      Occupancy: { maxChildren: 1, minOccupants: 1, maxOccupants: 3 }
      # ...
    }
  ) {
    id
    name
  }
}

First, create a SpaceType. THis represents a room type, with all of the details about the room: name, description, photos, occupancy and amenities. Check the GraphQL docs for additional fields you can include in a SpaceType.

Create a RatePlan

mutation ($propertyId: propertyId!){
  createRatePlan(payload: {
    propertyId: $propertyId,
    name: "-10% on taxes",
    isActive: true,
    Taxes: [{
      name: "Test Percentage Tax",
      percentage: 10,
      sequenceNumber: 1
    }]
  }){
    id
    name
  }
}

Next, you’ll need to create a RatePlan. This represents the type of rates you’re going to distribute on the network.

RatePlan controls the high level details of a particular rate, like Taxes and policies. It does not include the actual prices.

Create a SpaceTypeRate

mutation createSpaceTypeRate (
  $propertyId: propertyId!
  $spaceTypeId: spaceTypeId
  $ratePlanId: ratePlanId
) {
  createSpaceTypeRate(
    payload: {
      propertyId: $propertyId
      ratePlanId: $ratePlanId
      spaceTypeId: $spaceTypeId
      isActive: true
      isAvailableToAllPartners: true
      RatePeriods: [
        {
          startDate: "2022-01-01"
          endDate: "2022-12-31"
          timeUnit: DAILY
          OpenToArrivalDOW: {
            sunday: true
            monday: true
            tuesday: true
            wednesday: true
            thursday: true
            friday: true
            saturday: true
          }
          OpenForSaleDOW: {
            sunday: true
            monday: true
            tuesday: true
            wednesday: true
            thursday: true
            friday: true
            saturday: true
          }
          OpenToDepartureDOW: {
            sunday: true
            monday: true
            tuesday: true
            wednesday: true
            thursday: true
            friday: true
            saturday: true
          }
          RateAmounts: {
            BaseGuestAmounts: [
              {
                minimumAdultTravelers: 1
                AmountAfterTaxesAndFees: {
                  value: 11700 # = $117.00
                  currencyCode: USD
                  decimalPlaces: 2
                }
              }
            ]
          }
        }
      ]
    }
  ) {
    id
    Property {
      id
    }
    SpaceType {
      id
    }
    RatePlan {
      id
    }
  }
}

Now that you have created your SpaceType and RatePlan, you need to create a link between them using a SpaceTypeRate. This is where the actual pricing information is stored.

Set SpaceType Availability

mutation setAvailability($propertyId: propertyId!, $spaceTypeId: spaceTypeId!) {
  setAvailability(
    query: { propertyId: $propertyId }
    payload: {
      spaceTypeId: $spaceTypeId
      startDate: "2022-01-01"
      endDate: "2022-12-31"
      count: 15
    }
  ) {
    date
    count
  }
}

Now set the availability of your SpaceType with the setAvailability mutation. This example sets the same availability count for that entire time range, but you can also pass an array in the payload to set different availability for different days in a single call.

Search For Products

query getProducts($propertyId: propertyId!) {
  PropertiesConnection(query: { PropertyIdList: $propertyId }) {
    edges {
      node {
        ProductsConnection(
          query: {
            startDate: "2022-01-16"
            endDate: "2022-01-18"
            TravelerCount: { ageGroup: ADULT, count: 2 }
            TravelerAges: [3]
          }
        ) {
          edges {
            node {
              id
              startDate
              endDate
              SpaceType {
                id
                name
                # ...
              }
              RatePlan {
                id
                name
                # ...
              }
              TotalPrice {
                AmountAfterTaxesAndFees {
                  value
                  currencyCode
                  decimalPlaces
                }
              }
            }
          }
        }
      }
    }
  }
}

At this point, you’ve completed your first configuration as a PMS. Now other partners can search for bookable Products at the property you’ve created.

Let’s put ourselves in the shoes of an Online Travel Agency. This agency can explore available Products by searching for them under the Property.ProductsConnection field and return there results back to their users.

Create a Reservation

mutation ($propertyId: propertyId!, $partnerId: partnerId!) {
  createReservation(
    payload: {
      propertyId: $propertyId
      # Partner ID of the OTA creating this reservation
      # Note : on the main network, this is determined
      # through the network identity
      partnerId: $partnerId
      startDate: "2022-01-16"
      endDate: "2022-01-18"
      SpaceStays: [
        {
          # Get this productId from a ProductConnection Query
          productId: "PROD-79e237ad-5401-5404-819e-e39985aa5396"
          startDate: "2022-01-16"
          endDate: "2022-01-18"
          StayRate: {
            TotalPrice: {
              AmountAfterTaxesAndFees: {
                value: 23400 # = $234.00
                currencyCode: USD
                decimalPlaces: 2
              }
            }
          }
          TravelerCount: { ageGroup: ADULT, count: 2 }
          TravelerAges: [3]
          Traveler: {
            Email: { address: "john@doe.com" }
            PersonName: { Surname: "John", Given: "Doe" }
          }
        }
      ]
    }
  ) {
    id
    startDate
    endDate
  }
}

Now that a user has selected a Product to book, You can use the createReservation mutation to create a Reservation, applying the terms defined in your RatePlan, SpaceType and SpaceTypeRate items.

Notice that you need to provide the StayRate with the reservation. This ensures that there is no disparity between the displayed and the actual price in the event the underlying price has changed during the booking process.

Traveler details are not stored immutably on the network, but instead are stored outside of the ledger in a separate collection that is GDPR compliant.

Retrieve Your Reservation

subscription watchReservations {
  watchReservations(
    propertyId: "PROP-fd17a3d9-9ebf-5538-a048-970fb301bd7c"
    partnerId: "PART-1049c31f-b235-58e8-868b-4c5d8b9dba05"
  ) {
    id
    TravelerSummary {
      PersonName {
        Prefix
        Given
        Middle
        Surname
      }
      # ...
    }
    SpaceStays {
      startDate
      endDate
      # ...
    }
  }
}

Back on the PMS side, you might want to be notified when a Reservation is made on the properties you manage. This can be done through the watchReservations GraphQL subscription. Subscribing to this GraphQL subscription will open a WebSocket connection and the Arise Node will push messages to the listening client whenever a new Reservation is made for that specific property.

Primary Types

Our network schema relies on a set of primary types that join together to build a complete set of information required to manage, display and book accommodation inventory.

This section will explain each type, its purpose and how it relates to the other primary types in our schema.

Partner

A Partner represents a non-accommodation entity that can access network data. This could be an organization, business or service. Partners can be enrolled as Members at specific Property entities on the network, granting them permissions to view and / or manipulate Property data.

An example would be an RMS (Revenue Management System) that has been given permission by a hotel to dynamically adjust its rates on the network as market conditions change. The RMS company can create a Partner record on the network and hotels can grant that Partner record access to their Property. The RMS will then be able to make adjustments to Property rates directly.

Property

A Property represents an accommodation has a set of bookable units available for booking. Hotels are the most common example, but a Property could represent less traditional lodging units like a house being listed as vacation rental.

A Property can grant access to Partner entities on the network.

SpaceType

A SpaceType is defines a group of similar bookable units at a Property. An example would be a group of “King Rooms” at a Hotel. Availability of a SpaceType can be set on a day-by-day basis, and RatePlans can be linked with SpaceTypes using a SpaceTypeRates entity.

SpaceType entities contain text and MultimediaContent to display to potential Travelers during the booking process.

RatePlan

A RatePlan is assigned to a Property and contains information about its BookingPolicies (Cancellation, No-Show etc.) and BookingRules (DOW rate availability, and arrival offsets). RatePlan entities hold information about Tax and Fee structures, but do not directly contain pricing information for SpaceTypes. See SpaceTypeRates for pricing information.

SpaceTypeRates

SpaceTypeRate entities set the price for SpaceType units. They assign a price with conditions to a combination of RatePlan and SpaceType entities.

See RatePeriods for more information on the conditions and restrictions available.

Availability

Availability entities contain the number of available bookable units for a SpaceType for a single date.

Product

Product entities are generated when the network is queried for available accommodation and pricing information. Products are generated from a set of conditions based on the query being run along with SpaceType, Availability, RatePlan and SpaceTypeRates configurations. Products are ultimately the entities being sold to Travelers to create Reservations.

Traveler

A Traveler entity represents a person in the real world. Travelers are equivalent to Guests in other systems. They contain information that can link a record on the network to a real-world individual. Traveler information is sensitive and is only stored by reference on our network in order to comply with privacy and data regulations like GDPR. This is the only record type that can be completely deleted from the network.

Reservation

A Reservation entity holds information about a group of SpaceStays at a Property. It contains information related to the BookingRestrictions, BookingPolicies and Commission agreed upon when the Reservation was created.

SpaceStay

A SpaceStay entity links a purchased Product to a set of Traveler records. SpaceStays contain information about the stay dates, SpaceType and RatePlan booked, along with Traveler requests pertaining to a particular stay at a Property.

Common Types

These types are reused throughout the schema to hold information related to different entities in a common format.

MultimediaCollection

MultimediaCollection entities contain information about images or video content that can be displayed to the end user.

They contain titles, descriptions and tags related to the content and also a set of MultimediaVersion entities that contain external URLs pointing to the linked content in different sizes / variations.

All contents is stored by reference outside of the network.

Financial Types

CurrencyAmount

{
	"value": 12900,
	"currencyCode": "USD",
	"decimalPlaces": 2
}

This is equivalent to a currency amount of 129.00 USD

{
	"value": 2977152,
	"currencyCode": "VND",
	"decimalPlaces": 0
}

This is equivalent to a currency amount of 2977152 VND

Field NameTypeSummary
valueIntegerThe original currency amount multiplied by 10^decimal
currencyCodeStringISO 4217 Currency Code
decimalIntegerISO 4217 Currency decimal places

CurrencyAmount entities are reused throughout the schema to represent a monetary amount.

RatePeriod

RatePeriod entities contain low level detail about rates and the set of conditions that restrict those rates for booking as a Product.

RatePeriods are found in the SpaceTypeRate entity and are configured as set of conditions and RateAmounts specified for a date range.

Available conditions are:

Condition NameTypeSummary
OpenToArrivalDOWDOW toggleDetermines if RatePeriod is available based on startDate of a Reservation
OpenForSaleDOWDOW toggleDetermines if RatePeriod is available based on through range of a Reservation
OpenToDepartureDOWDOW toggleDetermines if RatePeriod is available based on endDate of a Reservation
minLOSIntegerThe minimum number of nights in a Reservation
maxLOSIntegerThe maximum number of nights in a Reservation

RateAmount

RateAmount entities contain information about pricing restrictions based on number and age of Travelers.

There are two sub-entities BaseGuestAmounts and AdditionalGuestAmounts.

BaseGuestAmounts specifies pricing information for a specific number of Travelers based on their defined ageGroup.

AdditionalGuestAmounts specifies pricing information for a each additional Traveler beyond what is defined in BaseGuestAmounts based on that Travelers defined ageGroup.

RateOverride

A RateOverride is an entity used to adjust pricing specified for a single date. It can be used to change the rate available to a specific Partner or to open or close a rate for a single date.

Taxes and Fees

Tax and Fee entities contain information about tax and fee structures used in RatePlan, Product and Reservation entities.

Tax and Fee entities can be related to each other based on the sequenceNumber and AdditionallyAppliedTo fields. These fields are used to define “tax-on-tax” scenarios, where the amount of one calculated tax is based on the outcome of another tax total.

Review the GraphQL schema field descriptions for a complete set of options for Tax and Fee entities.

We will be adding examples in the near future to expand on the configuration needed for common tax structures.

Property Examples

Complete detail of our entire schema and each individual field along with its type can be found by exploring the Playground IDE included with each node.

Find Property

A simple example of querying for data about a specific Property with a known ID.

query {
  PropertiesConnection( query: { 
    PropertyIdList: ["PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14"] 
  }){
    edges {
      node {
        id
        name
        Location {
          Address {
            AddressLine
            street
            city
            StateProv {
              name
              value
            }
            country
          }
          Position {
            latitude
            longitude
          }
        }
      }
    }
  }
}

The expected response:

{
  "data": {
    "PropertiesConnection": {
      "edges": [
        {
          "node": {
            "id": "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14",
            "name": "Hotel Arise",
            "Location": {
                "Address": {
                  "AddressLine": [
                    "Hotel Arise",
                    "660 York Street",
                    "San Francisco, CA 94110",
                    "United States"
                  ],
                  "street": "660 York Street",
                  "city": "San Francisco",
                  "StateProv": {
                    "name": "California",
                    "value": "US-CA"
                  },
                  "country": "US"
                },
                "Position": {
                  "latitude": -33.9314534,
                  "longitude": 151.1904941
                }
              }
            }
          }
        }
      ]
    }
  }
}

In this example we will fetch detailed information about:

Property Content

Fetching additional information about a Property's Amenities and available Multimedia Content.

query {
  PropertiesConnection( query: { 
    PropertyIdList: ["PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14"] 
  }){
    edges {
      node {
        id
        name
        Amenities
        MultimediaCollection {
          type
          Versions {
            url
          }
          title
          Description {
            value
            language
          }
        }
      }
    }
  }
}

The expected response:

{
  "data": {
    "PropertiesConnection": {
      "edges": [
        {
          "node": {
            "id": "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14",
            "name": "Hotel Arise",
            "Amenities": [
                "AIR_CONDITIONING",
                "POOL",
                "DISCO",
                "EXERCISE_GYM",
                "COFFEE_SHOP"
              ],
              "MultimediaCollection": {
                {
                  "type": "IMAGE",
                  "Versions": [
                    {
                      "url": "https://arise.travel/assets/hero.svg"
                    }
                  ],
                  "title": "Our Beautiful Hotel",
                  "Description": [
                    {
                      "value": "Our historic hotel is in a perfect location for everything you need.",
                      "language": "eng"
                    }
                  ]
                }
              ]
            }
          }
        }
      ]
    }
  }
}

In this example we will fetch detailed information about:

Filter Properties

query {
  PropertiesConnection( query:{
    Location:{
      llatitude: -33.9314500,
      longitude: 151.1904900,
      distance: 1,
      distanceUnits: MILES
    }
  }){
    edges {
      node {
        id
        name
        Location {
          Position {
            latitude
            longitude
          }
        }
      }
    }
  }
}

The expected response:

{
  "data": {
    "PropertiesConnection": {
      "edges": [
        {
          "node": {
            "id": "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14",
            "name": "Hotel Arise",
            "Location": {
              "Position": {
                "latitude": -33.9314534,
                "longitude": 151.1904941
              }
            }
          }
        },
        {
          "node": {
            "id": "PROP-dc7fdc57-7c0d-5e8c-b472-2245529768f8",
            "name": "York Street Motel",
            "Location": {
              "Position": {
                "latitude": -33.9314539,
                "longitude": 151.1904934
              }
            }
          }
        }
      ]
    }
  }
}

An example of querying for a list of Properties within a geographic area.

This query will return multiple Property objects returned as a node inside an edge wrapper. This configuration follows the Relay Specification for GraphQL pagination.

Modify a Property


mutation {
  updateProperty( query: {
    propertyId: "PROP-14b57dad-5ac2-5534-b822-387cc58a89ec"
  }, 
  payload: {
    ExternalIds: [{
      partnerId: "PART-76c9acb4-e4a1-52db-ae52-242fdb1bce66",
      externalId: "BDC-555-555"
    }]
  }){
    id
    name
    ExternalIds{
      Partner {
        name
      }
      externalId
    }
  }
}

The expected response:

{
  "data": {
    "PropertiesConnection": {
      "edges": [
        {
          "node": {
            "id": "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14",
            "name": "Hotel Arise",
            "Location": {
              "Position": {
                "latitude": -33.9314534,
                "longitude": 151.1904941
              }
            }
          }
        },
        {
          "node": {
            "id": "PROP-dc7fdc57-7c0d-5e8c-b472-2245529768f8",
            "name": "York Street Motel",
            "Location": {
              "Position": {
                "latitude": -33.9314539,
                "longitude": 151.1904934
              }
            }
          }
        }
      ]
    }
  }
}

Create a SpaceType


mutation {
  createSpaceType(payload: {
    propertyId: "PROP-14b57dad-5ac2-5534-b822-387cc58a89ec",
    name: "Test Space Type",
    ExternalIds: [{
        partnerId: "PART-76c9acb4-e4a1-52db-ae52-242fdb1bce66",
        externalId: "BDC-444-444"
    }]
  }) {
    id
    name
    PartnerExtRefs{
      Partner {
        name
      }
      externalId
    }
  }
}

The expected response:

{
  "data": {
    "PropertiesConnection": {
      "edges": [
        {
          "node": {
            "id": "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14",
            "name": "Hotel Arise",
            "Location": {
              "Position": {
                "latitude": -33.9314534,
                "longitude": 151.1904941
              }
            }
          }
        },
        {
          "node": {
            "id": "PROP-dc7fdc57-7c0d-5e8c-b472-2245529768f8",
            "name": "York Street Motel",
            "Location": {
              "Position": {
                "latitude": -33.9314539,
                "longitude": 151.1904934
              }
            }
          }
        }
      ]
    }
  }
}

In this example we will add a new SpaceType to an existing Property that has been created on the network. A SpaceType is equivalent to a Room Type in some other systems. It contains information about a certain type of space available for booking at a Property. SpaceTypes have a certain number of bookable units available for each date. The number of units available for booking can be set using Availability.

ARI Examples

Availability, rates and / or inventory (ARI) determines the Products available for booking by a Traveler at a Property.

Availability sets the number of bookable units available for a specific SpaceType at a Property. RatePlans and RatePeriods are used to define the amount a Traveler must pay to create a Reservation.

Complete detail of our entire schema and each individual field along with its type can be found by exploring the Playground IDE included with each node.

Update Availability


mutation {
  updateAvailability(query: { 
    propertyId: "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14"
  },
  payload: {
    spaceTypeId: "SPTP-47185db3-63cc-5d91-9cee-eecf04b7e7e5",
    startDate: "2019-10-01",
    endDate: "2019-10-10",
    count: 10
  })
  {
    date
    count
  }
}

The expected response:

{
  "data": {
    "updateAvailability": [
      {
        "date": "2019-10-01",
        "count": 10
      },
      {
        "date": "2019-10-02",
        "count": 10
      },
      {
        "date": "2019-10-03",
        "count": 10
      },
      {
        "date": "2019-10-04",
        "count": 10
      },
      {
        "date": "2019-10-05",
        "count": 10
      },
      {
        "date": "2019-10-06",
        "count": 10
      },
      {
        "date": "2019-10-07",
        "count": 10
      },
      {
        "date": "2019-10-08",
        "count": 10
      },
      {
        "date": "2019-10-09",
        "count": 10
      },
      {
        "date": "2019-10-10",
        "count": 10
      }
    ]
  }
}

This example sets the Availability count for a specific SpaceType at a Property for a given date range.

The response expands that range into day-by-day breakdown of the resulting Availability

Create a RatePlan

mutation {
  createRatePlan(payload: {
    propertyId: "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14",
    name: "Best Available Rate",
    isActive: true,
    MealsIncluded: {
      breakfastIncluded: true
    },
    BookingRules: {
      ArrivalOffset: {
        minimumDaysInAdvance: 30
      }
    },
    BookingPolicies: {
      CancellationPolicy: {
        Deadline: {
          timePeriodStart: 7,
          timePeriodUnits: DAYS,
          deadlineType: BEFORE_ARRIVAL
        },
        numberOfNights: 1,
        basisType: NIGHTS
      }
    }
  }) 
  {
    id
    name
    isActive
    MealsIncluded {
      breakfastIncluded
    }
    BookingRules {
      ArrivalOffset {
        minimumDaysInAdvance
      }
    }
    BookingPolicies {
      CancellationPolicy {
        Deadline {
          timePeriodStart
          deadlineType
        }
        numberOfNights
        basisType
      }
    }
  }
}

The expected response:

{
  "data": {
    "createRatePlan": {
      "id": "RATE-8f453169-b3e2-5a27-b392-146e6909b756",
      "name": "Best Available Rate",
      "isActive": true,
      "MealsIncluded": {
        "breakfastIncluded": true
      },
      "BookingRules": {
        "ArrivalOffset": {
          "minimumDaysInAdvance": 30
        }
      },
      "BookingPolicies": {
        "CancellationPolicy": {
          "Deadline": {
            "timePeriodStart": 7,
            "deadlineType": "BEFORE_ARRIVAL"
          },
          "numberOfNights": 1,
          "basisType": "NIGHTS"
        }
      }
    }
  }
}

This example creates a new RatePlan that includes breakfast, must be booked 30 days in advance and has a CancellationPolicy stating that any Reservation made with this RatePlan will be charged a penalty of one nights rent if canceled within 7 days of arrival.

Add a RatePlan to a SpaceType

mutation {
  createSpaceTypeRate( payload: {
    propertyId: "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14",
    ratePlanId: "RATE-8f453169-b3e2-5a27-b392-146e6909b756",
    spaceTypeId: "SPTP-47185db3-63cc-5d91-9cee-eecf04b7e7e5"
    isActive: true,
    RatePeriods: [{
      startDate: "2019-10-01",
      endDate: "2019-10-10",
      timeUnit: DAILY,
      minLOS: 1,
      RateAmounts: {
        BaseGuestAmounts: [{
          numberOfTravelers: 2,
          ageGroup: ADULT,
          AmountAfterTax: {
            value: 12900,
            currencyCode: "USD",
            decimalPlaces: 2
          }
        }],
        AdditionalGuestAmounts: [
          {
            ageGroup: ADULT,
            AmountAfterTax: {
              value: 2000,
              currencyCode: "USD",
              decimalPlaces: 2
            }
          },
          {
            ageGroup: CHILD,
            AmountAfterTax: {
              value: 1000,
              currencyCode: "USD",
              decimalPlaces: 2
            }
          }
        ]
      }
    }]
  } ){
    isActive
    RatePeriods {
      minLOS
      startDate
      endDate
      timeUnit
      RateAmounts {
        BaseGuestAmounts {
          numberOfTravelers
          ageGroup
          AmountAfterTax {
            value
            currencyCode
            decimalPlaces
          }
        }
        AdditionalGuestAmounts {
          ageGroup
          AmountAfterTax {
            value
            currencyCode
            decimalPlaces
          }
        }
      }
    }
  }
}

The expected response:

{
  "data": {
    "createSpaceTypeRate": {
      "isActive": true,
      "RatePeriods": [
        {
          "minLOS": 1,
          "startDate": "2019-10-01",
          "endDate": "2019-10-10",
          "timeUnit": "DAILY",
          "RateAmounts": {
            "BaseGuestAmounts": [
              {
                "numberOfTravelers": 2,
                "ageGroup": "ADULT",
                "AmountAfterTax": {
                  "value": 12900,
                  "currencyCode": "USD",
                  "decimalPlaces": 2
                }
              }
            ],
            "AdditionalGuestAmounts": [
              {
                "ageGroup": "ADULT",
                "AmountAfterTax": {
                  "value": 2000,
                  "currencyCode": "USD",
                  "decimalPlaces": 2
                }
              },
              {
                "ageGroup": "CHILD",
                "AmountAfterTax": {
                  "value": 1000,
                  "currencyCode": "USD",
                  "decimalPlaces": 2
                }
              }
            ]
          }
        }
      ]
    }
  }
}

This example creates a SpaceTypeRate with a single RatePeriod that defines a rate amount on a DAILY basis. The BaseGuestAmount value sets the price for up to two ADULT occupants. The AdditionalGuestAmount values define the incremental price for an additional ADULT or CHILD occupant.

Length-of-Stay Based Rates

mutation {
  createSpaceTypeRate( payload: {
    propertyId: "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14",
    ratePlanId: "RATE-8f453169-b3e2-5a27-b392-146e6909b756",
    spaceTypeId: "SPTP-47185db3-63cc-5d91-9cee-eecf04b7e7e5"
    isActive: true,
    RatePeriods: [
      {
        startDate: "2019-10-11",
        endDate: "2019-10-20",
        timeUnit: DAILY,
        minLOS: 1,
        RateAmounts: {
          BaseGuestAmounts: [{
            numberOfTravelers: 2,
            ageGroup: ADULT,
            AmountAfterTax: {
              value: 12900,
              currencyCode: "USD",
              decimalPlaces: 2
            }
          }]
        }
      },
      {
        startDate: "2019-10-11",
        endDate: "2019-10-20",
        timeUnit: FULL_DURATION,
        minLOS: 2,
        maxLOS: 2,
        RateAmounts: {
          BaseGuestAmounts: [{
            numberOfTravelers: 2,
            ageGroup: ADULT,
            AmountAfterTax: {
              value: 21900,
              currencyCode: "USD",
              decimalPlaces: 2
            }
          }]
        }
      },
      {
        startDate: "2019-10-11",
        endDate: "2019-10-20",
        timeUnit: FULL_DURATION,
        minLOS: 3,
        maxLOS: 3,
        RateAmounts: {
          BaseGuestAmounts: [{
            numberOfTravelers: 2,
            ageGroup: ADULT,
            AmountAfterTax: {
              value: 30900,
              currencyCode: "USD",
              decimalPlaces: 2
            }
          }
        ]
      }
    }]
  } ){
    isActive
    RatePeriods {
      minLOS
      maxLOS
      startDate
      endDate
      timeUnit
      RateAmounts {
        BaseGuestAmounts {
          numberOfTravelers
          ageGroup
          AmountAfterTax {
            value
            currencyCode
            decimalPlaces
          }
        }
      }
    }
  }
}

The expected response:

{
  "data": {
    "updateSpaceTypeRate": {
      "isActive": true,
      "RatePeriods": [
        {
          "minLOS": 1,
          "maxLOS": null,
          "startDate": "2019-10-11",
          "endDate": "2019-10-20",
          "timeUnit": "DAILY",
          "RateAmounts": {
            "BaseGuestAmounts": [
              {
                "numberOfTravelers": 2,
                "ageGroup": "ADULT",
                "AmountAfterTax": {
                  "value": 12900,
                  "currencyCode": "USD",
                  "decimalPlaces": 2
                }
              }
            ]
          }
        },
        {
          "minLOS": 2,
          "maxLOS": 2,
          "startDate": "2019-10-11",
          "endDate": "2019-10-20",
          "timeUnit": "FULL_DURATION",
          "RateAmounts": {
            "BaseGuestAmounts": [
              {
                "numberOfTravelers": 2,
                "ageGroup": "ADULT",
                "AmountAfterTax": {
                  "value": 21900,
                  "currencyCode": "USD",
                  "decimalPlaces": 2
                }
              }
            ]
          }
        },
        {
          "minLOS": 3,
          "maxLOS": 3,
          "startDate": "2019-10-11",
          "endDate": "2019-10-20",
          "timeUnit": "FULL_DURATION",
          "RateAmounts": {
            "BaseGuestAmounts": [
              {
                "numberOfTravelers": 2,
                "ageGroup": "ADULT",
                "AmountAfterTax": {
                  "value": 30900,
                  "currencyCode": "USD",
                  "decimalPlaces": 2
                }
              }
            ]
          }
        }
      ]
    }
  }
}

This example sets SpaceTypeRate with multiple RatePeriods that define a price based on a combination of DAILY and FULL_DURATION bases.

These RatePeriod combinations define progressive Length-of-Stay based pricing for this RatePlan and SpaceType combination.

The FULL_DURATION timeUnit defines that the RateAmounts specified are the price for the entire qualifying duration.

For example, if a single Traveler wanted to book a one night stay from 2019-10-12 to 2019-10-13, they would be offered a price of $129.00 in this example, because they only qualify for the first RatePeriod with a minLOS of 1.

If this Traveler then extended their stay from 2019-10-12 to 2019-10-14, they would be offered a price of $219.00 because they qualify for the second RatePeriod defined with a minLOS of 2. Note the "timeUnit": "FULL_DURATION" field value.

Override a RatePlan

mutation {
  setSpaceTypeRatePeriodOverrides(payload: {
    RateOverrides: [
      {
        propertyId: "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14",
        ratePlanId: "RATE-8f453169-b3e2-5a27-b392-146e6909b756",
        spaceTypeId: "SPTP-47185db3-63cc-5d91-9cee-eecf04b7e7e5",
        date: "2019-10-01",
        MainOverrides: {
          openForSale: true,
          openToArrival: true,
          openToDeparture: true
        },
        ConditionalOverrides: [
          {
            minLOS: 1,
            timeUnit: DAILY,
            RateAmounts: {
              BaseGuestAmounts: [{
                numberOfTravelers: 2,
                ageGroup: ADULT,
                AmountAfterTax: {
                  value: 13900,
                  currencyCode: "USD",
                  decimalPlaces: 2
                }
              }]
            }
          }
        ]
      }
    ]
  }) 
  {
    date
    ConditionalOverrides {
      minLOS
      timeUnit
      RateAmounts {
        BaseGuestAmounts {
          numberOfTravelers
          ageGroup
          AmountAfterTax {
            value
            currencyCode
            decimalPlaces
          }
        }
      }
    }
  }
}

The expected response:

{
  "data": {
    "setSpaceTypeRatePeriodOverrides": [
      {
        "date": "2019-10-01",
        "ConditionalOverrides": [
          {
            "minLOS": 1,
            "timeUnit": "DAILY",
            "RateAmounts": {
              "BaseGuestAmounts": [
                {
                  "numberOfTravelers": 2,
                  "ageGroup": "ADULT",
                  "AmountAfterTax": {
                    "value": 13900,
                    "currencyCode": "USD",
                    "decimalPlaces": 2
                  }
                }
              ]
            }
          }
        ]
      }
    ]
  }
}

This example uses a RateOverride to make an adjustment to a single day’s price and conditions. The RateOverride entity can be used to override the RateAmounts previously set in a SpaceTypeRatePlan configuration using the ConditionalOverrides field.

The MainOverrides field can be used enable or disable availability of a RatePlan for a date based on certain conditions.

In this example, the BaseGuestAmount set for two Travelers with a minLOS value of 1 and a timeUnit of DAILY is being overridden and set to $139.00.

Apply a Stop Sell

mutation {
  setSpaceTypeRatePeriodOverrides(payload: {
    RateOverrides: [
      {
        propertyId: "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14",
        ratePlanId: "RATE-8f453169-b3e2-5a27-b392-146e6909b756",
        spaceTypeId: "SPTP-47185db3-63cc-5d91-9cee-eecf04b7e7e5",
        date: "2019-10-01",
        MainOverrides: {
          openForSale: false,
        }
      }
    ]
  }) 
  {
    Property {
      id
    }
    SpaceType {
      id
    }
    RatePlan {
      name
    }
    date
    MainOverrides {
      openForSale
    }
  }
}

The expected response:

{
  "data": {
    "setSpaceTypeRatePeriodOverrides": [
      {
        "Property": {
          "id": "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14"
        },
        "SpaceType": {
          "id": "SPTP-47185db3-63cc-5d91-9cee-eecf04b7e7e5"
        },
        "RatePlan": {
          "id": "RATE-8f453169-b3e2-5a27-b392-146e6909b756"
        },
        "date": "2019-10-01",
        "MainOverrides": {
          "openForSale": false
        }
      }
    ]
  }
}

This example uses a RateOverride to make a SpaceTypeRate unavailable for sale on a specific date. By using the MainOverrides.openForSale property and setting it to false, any query for availability at this property will no longer return the overridden SpaceTypeRate as a bookable Product.

Retrieve Rate Breakdown

{
  PropertiesConnection(query: { 
    PropertyIdList: ["PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14"]
  }) 
  {
    edges {
      node {
        RatePlansConnection(query: {
          RatePlanIdList: ["RATE-8f453169-b3e2-5a27-b392-146e6909b756"]
        }) {
          edges {
            node {
              id
              SpaceTypeRates {
                SpaceType {
                  id
                }
                DailyRates {
                  date
                  DayRateFromRatePeriods {
                    openToArrival
                    openForSale
                    openToDeparture
                    minLOS
                    maxLOS
                    timeUnit
                    RateAmounts {
                      BaseGuestAmounts {
                        numberOfTravelers
                        ageGroup
                        AmountAfterTax {
                          value
                          currencyCode
                          decimalPlaces
                        }
                      }
                      AdditionalGuestAmounts {
                        ageGroup
                        AmountAfterTax {
                          value
                          currencyCode
                          decimalPlaces
                        }
                      }
                    }
                  }
                  RateOverrides {
                    SpaceType {
                      id
                    }
                    MainOverrides {
                      openToArrival
                      openForSale
                      openToDeparture
                    }
                    ConditionalOverrides {
                      openToArrival
                    openForSale
                    openToDeparture
                    minLOS
                    maxLOS
                    timeUnit
                    RateAmounts {
                      BaseGuestAmounts {
                        numberOfTravelers
                        ageGroup
                        AmountAfterTax {
                          value
                          currencyCode
                          decimalPlaces
                        }
                      }
                      AdditionalGuestAmounts {
                        ageGroup
                        AmountAfterTax {
                          value
                          currencyCode
                          decimalPlaces
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

The expected response:

{
  "data": {
    "PropertiesConnection": {
      "edges": [
        {
          "node": {
            "RatePlansConnection": {
              "edges": [
                {
                  "node": {
                    "id": "RATE-8f453169-b3e2-5a27-b392-146e6909b756",
                    "SpaceTypeRates": [
                      {
                        "DailyRates": []
                      }
                    ]
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}

The Rate configuration offered by the network can be complex. This example shows a query that can be used to retrieve the current Rate configuration for a specific RatePlan at a Property as a day-by-day breakdown using the DailyRates field.

The response breaks out the underlying RatePeriod configuration in the DayRatesFromRatePeriods field and corresponding RateOverrides.

The RateOverride data can be overlaid over the DayRatesFromRatePeriods to get a flattened view of the current Rate configuration being offered to Travelers for booking.

This is useful when building a “Rate Grid” interface that allows users to make detailed changes to their Rate configuration for a Property on the network.

Product Examples

Complete detail of our entire schema and each individual field along with its type can be found by exploring the Playground IDE included with each node.

Product Availability

query {
  PropertiesConnection( query: { 
    PropertyIdList: ["PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14"] 
  }){
    edges {
      node {
        id
        name
        ProductConnection(query: {
          startDate: "2019-03-01",
          endDate: "2019-03-02"
        }) {
          edges {
            node {
              id
              startDate
              endDate
              RatePlan {
                id
                name
              }
              SpaceType {
                id
                name
              }
              TotalPrice {
                AmountBeforeTaxesAndFees {
                  value
                  decimalPlaces
                  currencyCode
                }
              }
            }
          }
        }
      }
    }
  }
}

The expected response:

{
  "data": {
    "PropertiesConnection": {
      "edges": [
        {
          "node": {
            "id": "PROP-20c7c5f5-8b11-56aa-aba3-9f3554c8de14",
            "name": "Hotel Arise",
            "ProductsConnection": {
              "edges": [
                {
                  "node": {
                    "startDate": "2019-03-01",
                    "endDate": "2019-03-02",
                    "RatePlan": {
                      "id": "RATE-ea95a96b-c222-454b-b303-6940a909d5d2",
                      "name": "Off-Season Rate"
                    },
                    "SpaceType": {
                      "id": "SPTP-eea2684a-f050-4def-abd1-c70e43ad1eb6",
                      "name": "Premiere Suite"
                    },
                    "TotalPrice": {
                      "AmountBeforeTaxesAndFees": {
                        "value": 12900,
                        "decimalPlaces": 2,
                        "currencyCode": "USD"
                      }
                    }
                  }
                },
                {
                  "node": {
                    "startDate": "2019-03-01",
                    "endDate": "2019-03-02",
                    "RatePlan": {
                      "id": "RATE-ea95a96b-c222-454b-b303-6940a909d5d2",
                      "name": "Off-Season Rate"
                    },
                    "SpaceType": {
                      "id": "SPTP-atr2684a-f878-5def-abd2-w41e09fg11b6",
                      "name": "King Room"
                    },
                    "TotalPrice": {
                      "AmountBeforeTaxesAndFees": {
                        "value": 12900,
                        "decimalPlaces": 2,
                        "currencyCode": "USD"
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}

A Product is a combination of a Rate and Availability that a Traveler can book to create a Reservation.

To search for Products at a Property we can extend the Find Property query to include ProductsConnection.

A query for Products requires two parameters:

ParameterDescription
startDateThe start date of the product (equivalent to the check-in date)
endDateThe end date of the product (equivalent to the check-out date)

Reservation Examples

Complete detail of our entire schema and each individual field along with its type can be found by exploring the Playground IDE included with each node.

Create Reservation

mutation {
  createReservation( payload: {
    propertyId: "PROP-c55f8f8a-9939-46a0-b20a-700338088ab4",
    startDate: "2019-03-01",
    endDate: "2019-03-02",
    ContactPerson: {
      Person: {
        PersonName: {
          Surname: "Lamb",
          Given: "Alex"
        },
        Email: {
          address: "alex@arise.travel"
        }
      }
    }
    SpaceStays: [
      {
        spaceTypeId: "SPTP-41547dc3-fac1-45d9-b89c-b635160a3970",
        ratePlanId: "RATE-ea95a96b-c222-454b-b303-6940a909d5d2"
        startDate: "2019-03-01",
        endDate: "2019-03-02",
        TravelerCount: [{
          count: 2,
          ageGroup: ADULT
        }],
        Traveler: {
          PersonName: {
            Surname: "Lamb",
            Given: "Alex"
          }
        }
      }
    ]
  }){
    id
    propertyId
    startDate
    endDate
  }
}

The expected response:

{
  "data": {
    "createReservation": {
      "id": "RSRV-5ddb91b0-ee9d-5488-a218-4f2deaa64ede",
      "propertyId": "PROP-c55f8f8a-9939-46a0-b20a-700338088ab4",
      "startDate": "2019-03-01",
      "endDate": "2019-03-02"
    }
  }
}

A single Reservation can contain multiple SpaceStays. Each SpaceStay can have different Travelers, start / end dates and different RatePlan and SpaceType assignments.

In order to reserve a specific Product, a propertyId must be provided, along with a ratePlanId and spaceTypeId for each SpaceType in the payload object.

The response to this mutation can contain the id of this new Reservation beginning with RSRV.

Modify Reservation

mutation {
  modifyReservation( query: {
    reservationId: "RSRV-5ddb91b0-ee9d-5488-a218-4f2deaa64ede"
  },
  payload: {
    ContactPerson: {
      Person: {
        PersonName: {
          Surname: "El Manawy",
          Given: "Nadim"
        },
        Email: {
          address: "nadim@arise.travel"
        }
      }
    }
  }){
    id
    ContactPerson {
      Person {
        PersonName {
          Surname
          Given
        },
        Email {
          address
        }
      }
    }
  }
}

The expected response:

{
  "data": {
    "modifyReservation": {
      "id": "RSRV-5ddb91b0-ee9d-5488-a218-4f2deaa64ede",
      "ContactPerson": {
        "Person": {
          "PersonName": {
            "Surname": "El Manawy",
            "Given": "Nadim"
          },
          "Email": {
            "address": "nadim@arise.travel"
          }
        }
      }
    }
  }
}

A Reservation can be modified by using it’s reservationId and sending updated fields in the payload object.

The mutation in this example is updating the ContactPerson for the Reservation that was created in the previous example.