NAV
Shell

Introduction

Welcome to the Arise Network documentation! We will introduce basic network architecture, give examples of how to use the GraphQL interface that is available on each network node, and eventually provide a detailed breakdown of the full capabilities of the network.

GraphQL schema are self documenting. Field by field detail for this API can be reviewed using the provided IDE that runs on all network nodes by default.

For most GraphQL examples we have language bindings in Javascript and cURL! You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.

Our Network

Architecture

Arise is a distributed network for hotels and the ecosystem of products and services around them. The network is composed of connected nodes all running the same application and has no central servers or single points of failure.

Network

We use distributed ledger technology to build a shared, transparent database that connected parties can read from and write to.

Our network is private and permissioned, meaning every connected node is linked to a known identity in the real world. 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 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 the business logic that is needed to perform complex, open-ended queries. Connectivity to other hotel applications on the network is built into the protocol.

Developer Experience

Our goal is to make development on our network as straightforward as possible for developers. We’ve abstracted away the underlying ledger and smart contracts behind a strongly typed GraphQL schema.

GraphQL

This GraphQL schema 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.

Developers have access to an open test network that doesn’t require a verified identity to use. We also offer public test nodes with an open GraphQL interface to develop against without the need to run a node to get started.

Identity and Permissions

Permissions on the network are 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.

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. To ensure that a comparable RESTful API was simple for developers we would have to require that the developer send multiple requests and join data in their own application. GraphQL can handle those joins automatically.

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 through 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 application and exposes an endpoint for queries and mutations at /graphql.

GraphQL queries are posted 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 generated with shared secret configured in the node application when it is deployed. 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

Getting Started

Each node on our network runs a web-based version of GraphQL Playground available at /playground. GraphQL Playground is a powerful GraphQL IDE that allows developers to explore and interact directly with a GraphQL schema from their browser. It’s great for collaboration between developers and an excellent way to mock up network queries before writing a single line of code. It also has a great curl export function.

This GraphQL IDE is the best way to explore our network’s GraphQL schema. We operate a test network thats configured for public access. This network runs the most up to date of our node application and schema.

Our 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 addition.

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 Name Code Summary
Property PROP A property on the network. Maps to one physical address.
Availability AVAL A unit of availability in a space type
Rate Plan RATE A rate plan
Rate Override RAOR A single day modification to a rate
Reservation RSRV A reservation
Space Type SPTP A space type at a property. Equivalent to a room type
Space Type Rate SPRT A rate for a specific space type
Partner PART A partner on the network. An identity linked to an OTA, RMS, PMS .etc
Traveler TRAV A Traveler. A Guest etc.
Product PROD A Product available for booking.
Internal INTR An 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 )

Querying Data

Modifying Data

GraphQL uses the concept of mutations to create or modify records on the network. create mutations accept one payload parameter containing the data required for record creation.

Mutations that modify data accept an additional query parameter that specifies the ID of the record being updated.

Pagination

We follow Relay’s conventions for Pagination.

Any field name in our Schema that ends with *Connection like PropertyConnection or ProductConnection can be paginated through using this standard.

Schema Examples

In an effort to put some real-world examples of how an application might interact with our GraphQL API to complete a certain action, we’ve put together a series of queries and mutations to complete a task. We will make an effort to chain these example together into more and more complex use-cases to show the flexibility and power of this interface.

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 Name Type Summary
value Integer The original currency amount multiplied by 10^decimal
currencyCode String ISO 4217 Currency Code
decimal Integer ISO 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 Name Type Summary
OpenToArrivalDOW DOW toggle Determines if RatePeriod is available based on startDate of a Reservation
OpenForSaleDOW DOW toggle Determines if RatePeriod is available based on through range of a Reservation
OpenToDepartureDOW DOW toggle Determines if RatePeriod is available based on endDate of a Reservation
minLOS Integer The minimum number of nights in a Reservation
maxLOS Integer The 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:

Parameter Description
startDate The start date of the product (equivalent to the check-in date)
endDate The 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.

Distributor Plug-In

Introduction

We understand that most development teams in the travel industry are swamped with a list of integrations to complete and maintain. Thats why we’ve taken a different approach to help teams get up and running with Arise without the need for custom development, heavy testing or “certification” processes.

Our plug-in works with the existing message formats of the integrations to industry intermediaries that teams have already built. This plug-in pulls anonymized booking and search data out of sent and received messages and sends it to our network, creating a record of demand for hotel inventory.

Plug-In

We’ve essentially built a proxy application that runs on partner infrastructure. Our partners install the plug-in, route outgoing intermediary API requests to its local port and then the plug-in forwards on the requests to the intended intermediary.

The plug-in listens for specific request / response message pairs and concurrently parses those messages before selectively sending booking and query data to nodes we maintain for this purpose on our network.

The plug-in is designed for high throughput applications and has a minimal impact on network latency or system load.

Supported APIs

We currently support the follow intermediary API message formats

Name Message Format Config Value
Travelport Universal API travelport_universal
Expedia EPS Rapid expedia_eps

Additional API support may be available on request. Please contact support@arise.travel with your intermediary API requirements.

Quick Start Guide

For this guide, we are assuming the ariseplugin application is being installed and tested in a Linux based development environment that is running a common service manager (launch, upstart, systemd, runit).

We also provide a Docker Image and other installation options. See Installation for the complete list.

Step 1

Install the ariseplugin application using our convenience script.

curl -fsSL https://downloads.arise.travel/ariseplugin/linux/install.sh | sh

Step 2

Edit the default /etc/arise/plugin.toml file with your desired message_format and service_endpoint settings. See Configuration File for more options.

Step 3

Start the ariseplugin service

service ariseplugin start

Step 4

Direct intermediary traffic to running ariseplugin listening port.

Step 5

Run a few test queries and create a few test bookings against your selected intermediary’s sandbox or test environment.

Step 6

Login with your provided Arise Extranet credentials and review the test bookings on the network.

Step 7

If the test bookings appear then you’ve successfully connected you environment to our network!

Installation

We provide a shell script that downloads and installs the plug-in binary on partner infrastructure. After installation a few parameters need to be set to start the plug-in with the desired configuration.

Once up and running intermediary traffic can be routed through the plug-in and your integration to Arise is complete!

On average, plug-in installation and configuration take 30 minutes to complete

Linux

We provide a convenience script that attempts to check the local environment and install the plug-in using the native package manager.

To install the plug-in use this code:

# a cURL command executes a shell script to perform installation of the plug-in
curl -fsSL https://downloads.arise.travel/ariseplugin/linux/install.sh | sh

Execute cURL command to download and install plug-in binary locally.

curl -fsSL https://downloads.arise.travel/ariseplugin/linux/install.sh | sh

Service Manager Notes

# Example command to start ariseplugin if you distro uses systemd
service ariseplugin start
# This will pull configuration settings from the default plugin.toml file

When using a Service Manager to [ start | stop | restart ] ariseplugin we suggest you manage the configuration using plugin.toml. See Configuration File for more information.

We currently support the following Linux distributions:

OSX / macOS

Example homebrew command:

# add homebrew tap
brew tap arise-travel/ariseplugin
# install ariseplugin
brew install ariseplugin

We use Homebrew to handle distribution of the plug-in on OSX / macOS

Add our repository to your Homebrew installation

brew tap arise-travel/ariseplugin

Run the install command for the ariseplugin application

brew install ariseplugin

Windows

We can generate an ariseplugin build for windows upon request.

Docker

Docker command to pull and start the latest arisetravel/ariseplugin image:

# Pull Docker image
docker pull arisetravel/ariseplugin
# Start ariseplugin container with config file from host
# plugin.toml is mounted in default ariseplugin config path.
docker run -v CONFIG/PATH/plugin.toml:/etc/arise/plugin.toml arisetravel/plugin

We provide a Docker Image with the ariseplugin application already installed. This image uses the alpine:3.8 image as a base.

To pull the arisetravel/ariseplugin images

docker pull arisetravel/ariseplugin

To run the arisetravel/ariseplugin images with a mounted config file.

docker run -v CONFIG/PATH/plugin.toml:/etc/arise/plugin.toml arisetravel/plugin

For details on the options and format of plugin.toml review the Configuration File section of this document.

Command-Line Options

Our plug-in is available as an installed binary accessible with the ariseplugin command. The following is a summary of the available settings and configuration options for this application.

Command

ariseplugin

Usage

ariseplugin [<flags>] <command> [<args> ...]

Flags

Name Parameter Default Description
Help help none Prints help text.
Version version none Prints the current version of the ariseplugin application.
Log Path log_path /var/log/arise.log Sets the file-path to log all data being sent to the network.
Config Path log_path /etc/arise/plugin.toml Sets the file-path to toml config file.
Daemon Mode daemon false Runs ariseplugin in the background.
Forever Mode forever false Restart ariseplugin after 5 seconds if it exits.
Production Mode prod false ariseplugin will be less verbose.
Test mode test true All traffic will be sent to the arise test network.

Command

ariseplugin start

Starts the ariseplugin application

Usage

ariseplugin [<flags>] start [<args> ...]

Arguments

Name Parameter Default Description
Message Format message_format none Specifies the Intermediary API message format Options Required
Service Endpoint service_endpoint none Specifies the URL of the intermediary API service endpoint. Required
Port port 8027 The port ariseplugin will listen on for intermediary traffic.
Timeout timeout 2000 TCP timeout in milliseconds when connecting to the service endpoint.

Configuration File

The plug-in can process configuration files in TOML format. By default the plug-in looks for configuration files at /etc/arise/plugin.toml but a different path can be passed with the --config option from the CLI.

Format

Contents of an example plugin.toml file:

prod = false
prometheus = false
test = true
config = "/etc/arise/plugin.toml"
log_path = "/var/log/arise/arise.log"

[start]
message_format = "travelport_universal"
port = 8027
timeout = 500
service_endpoint = "americas.universal-api.pp.travelport.com"

The configuration file follows the same naming convention and hierarchy as the Command-Line Options.

Setting the log file path from the CLI would look like:

ariseplugin --log_path=/var/logs/plugin.log

The same configuration would be set in plugin.toml like:

log_path=/var/logs/plugin.log

Sub-commands

Arguments for sub-commands, like start are defined in a TOML Table

An example CLI command to start the plug-in for the Travelport Universal API:

ariseplugin start --message_format=travelport_universal

The same configuration would be set in plugin.toml like:

[start]

followed by

message_format=travelport_universal

Examples

Test plug-in on testnet with Travelport Universal API Sandbox

ariseplugin start --service_endpoint=americas.universal-api.pp.travelport.com --message_format=travelport_universal --test

Start plug-in in daemon mode with Travelport Universal API for production

ariseplugin start --service_endpoint=americas.universal-api.pp.travelport.com --message_format=travelport_universal --prod -daemon

Data Security

We don’t touch the messages you send and only extract key pieces of information to help us build a case for direct hotel connectivity. We don’t want guest or payment information and our plug-in runs on your infrastructure, making sure sensitive info never leaves your application environment.

We offer complete transparency when it comes to the data being sent to our network. All captured data is visible in the plug-in logs.

Anonymity

The confidentiality and anonymity of our distribution partners is important. We mix all anonymous data from our distribution partners together, only ever showing hotels network demand as a whole.

When our partners are ready, they are free to make themselves known as network participants and can negotiate even higher commission rates with hotels as desired.

Running a Node

To get started, partners verify their identity and link it with a public key signed by the network’s certificate authority.

The partner then starts a node on infrastructure they control and give it access to their private key to sign network transactions on their behalf.

Network nodes run inside Docker containers and expose a GraphQL interface that can be used as a back-end for new applications or as a data source for existing ones.