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 schemas 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.

GraphQL 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 100s 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

Getting Started

Accessing GraphQL on Arise Nodes

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

Each node also 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.

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.
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 )

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.

API Examples

Example Objective

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.

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.

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)

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 and 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 requests to its local port and then the proxy 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 has a minimal impact on network latency or system load.

Installation

The plug-in binary is hosted in a private s3 bucket. We provide a shell script that downloads and installs the plug-in binary on partner infrastructure. Once installed 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

Step 1

Request credentials from alex@arise.travel for plug-in binary access.

To install the plug-in use this code:

# a cURL command executes a shell script to perform installation of the plug-in
curl -s https://s3.amazonaws.com/adapters.download/s3-download.sh | bash -s ACCESS_KEY ACCESS_SECRET

Make sure to replace ACCESS_KEY and ACCESS_SECRET with the values we provide.

Step 2

Execute cURL command to download and install binary locally.

curl -s https://s3.amazonaws.com/adapters.download/s3-download.sh | bash -s ACCESS_KEY ACCESS_SECRET

Example plug-in configuration options:

# to start the plug-in on port 8081 with logging enabled
ariseplugin start -p 8081 -l /var/log/arise.log

Step 3

Start ariseplugin program with desired configuration options (Options)

ariseplugin CONFIGIURATION_PARAMETERS

Step 4

Direct intermediary traffic to running plug-in listening port.

Step 5

Run a few test queries to confirm they are sent to our network.

Integration Complete!

We will monitor incoming data and get in touch if there are any issues.

Configuration

We offer a number of configuration options for the plug-in from the command line.

Name Parameter R/O Default Description
Service Endpoint s or service_endpoint Required service dependent Specifies the original API URI of the service provider (i.e. supplier’s API URI).
Port p or port Optional 8027 The port the plug-in will listen on for intermediary traffic.
Timeout t or timeout Optional 2000 Tcp timeout in milliseconds when connect to the service endpoint.
Log Path l or log_path Optional /var/log/arise.log Sets the file-path to log all data being sent to the network.
Daemon mode daemon Optional false Keeps proxy running, after closing the command line. If you want to run proxy in the daemon mode, the command line can be shut down, just add the --daemon parameter at the end of the command.
Monitor mode forever Optional false Proxy will fork subprocess, then monitor the child process, if the subprocess exits, it restarts the subprocess after 5 seconds. Just add the --forever parameter at the end of the command.
Production mode prod Optional false Proxy will be less verbose. Just add the --prod parameter at the end of the command.
Test mode test Optional true Proxy will run in debug mode. Just add the --test parameter at the end of the command.
Metrics prometheus Optional false Proxy will spin up a prometheus node exporter in order to gather HTTP requests metrics. Just add the --prometheus parameter at the end of the command. The node exporter will run on port 3030

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.