This document specifies an HTTP API for placing bookings to participate in physical activities, either by attending events or through the use of leisure or sports facilities.
Contributions to this document are not only welcomed but are actively solicited and should be made via [GitHub Issues](https://github.com/openactive/open-booking-api/issues) and pull requests. The source code is available on [GitHub](https://github.com/openactive/open-booking-api).
This document represents an early Editors Draft of the planned API design. It is likely to change between now and its final version. These early drafts are intended to help developers provide that feedback by developing proof-of-concept implementations. We encourage developers to explore this API and contribute to the development of the specification.
# Introduction The document is an output of the [OpenActive Community Group](https://www.w3.org/community/openactive/). As part of the [OpenActive](https://openactive.io) initiative, the community group is developing standards that will promote the publication and use of open [opportunity data](https://openactive.io/modelling-opportunity-data/#categories-of-physical-activity-data) in helping people to become more physically active. The Community Group have already published specifications that define standard data models ([[!Modelling-Opportunity-Data]]) and support the publication and harvesting of data in standard formats ([[RPDE]]). This existing work helps increase the discovery of opportunities for people to be active. This specification builds on that work by defining an HTTP API that can be implemented by booking systems that are publishing opportunity data. By allowing third-party applications to place bookings in their systems, it becomes possible for more people to participate in events or make use of leisure and sporting facilities. The specification defines the flow of HTTP requests and responses between the booking system (server) and the third-party application (client). This includes definitions of the overall application flow, detailed request and response formats and the potential error conditions that applications may encounter. ## Scope and requirements The current scope of the specification should conform to a set of [use cases and requirements](https://docs.google.com/document/d/1fsCr071V12i_2g0DK818OAGlEAJFLWwkBp9bpgx4fo8/edit?usp=sharing) that have previously been identified and discussed with the OpenActive community. The focus of the initial versions of this specification will be on the simplest use cases that will support making bookings on behalf of users. This core functionality includes: * __Pricing and availability__ – checking the price and current availabilty of an event or for the use of a facility * __Leasing__ – temporarily reserving spaces for a user to attend an event or make use of a facility, whilst placing a booking * __Booking__ – placing and viewing bookings, and where necessary, confirming receipt of payment from users A number of additional requirements that relate to the booking of events and facilities are currently out of scope: * creating and managing accounts for users * more complex pricing options, e.g. membership based pricing * handling bookings for multiple events ("shopping carts") or for groups of users * waiting lists for events It is possible that future versions of this specification may include support for these or other features. Revisions will be driven by the needs of the community. ### Functionality that is out of scope By design this specification will not define some types of functionality. These have been declared as permanently out of scope because they are either adequately covered by existing specifications, or will be covered by future work of the OpenActive Community Group. The functionality that is currrently defined as out of scope includes: * __API authentication and security__ – while the specification recommends some best practices relating to authentication and security it does not mandate a specific means of securing an API. Implementers are free to choose the system that offers the best security for their platform and users * __Payment processing__ – the design assumes that all payment handling will be carried out by the third-party application, in a separate payment flow. Booking systems and application developers are able to use the payment and reconciliation mechanisms that provide the best options for their customers * __API business models__ – the design is agnostic to any business models that might govern the use of the API, e.g. revenue-sharing or transaction processing fees * __Discovery__ – finding, filtering and searching for opportunity data. This specification assumes that the client and server applications have already shared data about the relevant opportunities. Additional functionality in this area will be addressed by future specifications from the Community Group ## Audience The document is primarily intended for the following audiences: * Software developers who want to implement the API, e.g. as part of a booking system * Software developers building client libraries or applications that will use the API The following sections introduce terminology, concepts and requirements that underpin the design of the API. This content might also be useful for a wider audience, e.g. business analysts or product managers looking for a high-level overview of the API functionality.
This specification makes use of the compact IRI Syntax; please refer to the [Compact IRIs](https://www.w3.org/TR/json-ld/#compact-iris) from [[JSON-LD]].
# Typographical Conventions The following typographic conventions are used in this specification:
markup
Markup (elements, attributes, properties), machine processable values (string, characters, media types), property name, or a file name is in a monospace font.
definition
A definition of a term, to be used elsewhere in this or other specifications, is in bold and italics.
hyperlink
A hyperlink is underlined and in blue.
[[reference]]
A document reference (normative or informative) is enclosed in square brackets and links to the references section.
Notes are in light green boxes with a green left border and with a "Note" header in green. Notes are normative or informative depending on the whether they are in a normative or informative section, respectively.
Examples are in light khaki boxes, with khaki left border, and with a
numbered "Example" header in khaki. Examples are always informative.
The content of the example is in monospace font and may be syntax colored.
# Key Concepts
booking system
The platform or service that provides a server-side implementation of this API.
broker
The organisation or developer providing an application that allows customers to make bookings. Those applications will be clients of the API defined in this specification
confirmation
The process of contacting a customer following the placement of a successful order, e.g. to provide tickets, attendance details, etc. This specification does not define this process
customer
The user who places a booking using an application provided by a broker.
event
An Event is an opportunity to take part in a physical activity. Events take place at a specific location and at an agreed date and time.
facility
A sporting facility, e.g. a squash court, table tennis table or football pitch.
facilityuse
A FacilityUse is an generic opportunity to utilise the facilities of a seller in order to take part in a physical activity. FacilityUses are not scheduled, and their slots are an equivalent for an Event in being an opportunity which can be booked.
lease
A temporary reservation of a space at an event or for the use of a facility. Leases provide a limited time window during which a customer can place a booking and still be guaranteed a place at an event (or use of the facility).
offer
An Offer describes the terms under which a customer may participate in an event or use a facility.
opportunity
An opportunity is a generic term to describe an opportunity to be active. Currently an opportunity is either an Event (for scheduled events, such as a yoga class) or a Slot for a FacilityUse (for non-scheduled use of the facilities of a seller).
order
Describes the in-process or completed booking. An order may be made up of several order items. An order results in a customer having a confirmed reservation.
payment
The process of taking funds from a customer in order to place an order. Payment is managed between the customer and the broker.
reconciliation
The process by which brokers and booking systems confirm that payment(s) have been taken on behalf of customers, to ensure that money is appropriately exchanged between the brokers, booking systems and sellers. This specification does not define this process.
refund
The process of returning funds to a customer, e.g. if an order is cancelled. This specification does not define this process.
reservation
A reservation for a customer to attend an event or for the use of a facility. A reservation is created for every item in an order.
seller
The organisation providing access to events or use of facilities via a booking system. E.g. a leisure provider running yoga classes.
slot
A Slot is an opportunity to use a facility at a specific time and for a specified duration. A Slot is therefore a specific type of Event, although it has a more constrained set of properties.
# Booking Flow ## High-level summary of the API This API defines a means for a **booking system** to expose an HTTP API that may be used by a **broker** to place bookings for **opportunities** on behalf of a **customer**. By exposing this API the booking system can support a **seller** in reaching a wider audience. A **broker** provides **customers** with an application that helps them to discover **opportunities** to be physically active. The customer may then choose to make a booking to attend an **event** or to make use of a **facility**. The **broker** will check the availability of the **opportunity** in order to check whether the **customer** can still attend. The **broker** will also present the **customer** with information about any costs associated with taking up the **opportunity**. This involves identifying the applicable **offers** and presenting them to the user. The customer should be made aware of any terms and conditions and any restrictions relating to age, gender or ability, as part of the process of proceeding to being in a position to make a booking. If there is still availability and the **customer** is happy to accept an **offer**, then the **customer** can proceed to make a booking. The broker creates an **order** on behalf of the user. Creating the **order** will also create a temporary **lease** that will reserve a space for the **customer** to take up the **opportunity** while the rest of the workflow completes. If the **offers** for an event or use of a facility indicate that the user must pay, then the broker will take the customer through a separate **payment** workflow. This workflow is not defined by this specification. Once **payment** has been completed, the **broker** will update the **booking platform** to indicate that **payment** has been taken. The **booking** process is then completed and the **order** is maked as completed. The completion of the **order** will result in the **lease** being removed and a permanent **reservation** being created on behalf of the **customer**. The **customer** may receive **confirmation** from either the **broker** or **booking system** (or both) that provides details about how and when they can attend the **event** or use the booked **facility**. Separately the **broker** and **booking system** will go through a **reconciliation** process to transfer funds or otherwise report on successful transactions. A **customer** might also cancel an **order** and will then receive a **refund**. The reconciliation processes is not defined by this specification. The details will depend on the business model and agreements in place between the **broker** and **booking system**. The cancellation workflow is defined in this specification in terms of the steps that the **broker** must undertake to provide the **booking system** with notification of the cancellation. ## URLs used in the description of the API The following sections contain URIs which are for guidance, and are used as examples only. For example, /events/452 is used to represent the canonical reference to the web representation of an Event with an id of 452. The URI could alternatively be /sessions/452 if session is the term used to describe the specific type of Event by a particular seller or booking system or by /slots/452 if dealing with a **slot** for use of a **facility**. All URIs should however be long lived identifiers and canonical and should not be recycled. Adherence to the workflows within this specification is required to develop a compliant **booking system**/**broker** implementation of this API and associated APIs such as RDPE in the Open Active ecosystem. ## Workflow diagram
Flow diagram
Simple illustration of booking workflow
## Sequence diagram
Sequence diagram
Sequence diagram showing API interactions
## Step-by-step process description The 1.0, and higher, versions of the Booking API supports single items per **order** and single **payments** per **order**. Future versions of the specification may support multiple items and multipart **payments** which are out of scope for the 1.0 release. ### Complete transaction 1. The **broker** requests details on a specific **opportunity** from the **booking system** (/events/452 in example) in order to discover available **offers**. These offers are subsequently presented to the **customer**. 2. The **customer** chooses an bookable **offer** (see definition of a bookable event). 3. If the **offer** for the **opportunity** is bookable, the broker makes a POST request to the **orders** endpoint (/orders in the examples) containing a Person object describing the **customer**, and information about the **offer** and **opportunity** selected. At a minimum, this Person object should contain the `email` address for the **customer**, and if possible the `givenName` and `familyName`. The **booking system** creates a timed **lease** on the space in the **opportunity** which is being booked. At this point the **offer** is deemed to be accepted and an **order** is created. The **booking system** sends a 201 status code, with a representation of the **order** in the body of the response, and with a Location header set containing the URI for the web representation of the created **Order** object. 4. If there is a price for the **offer**, the **broker** takes payment from the **customer** via a third party payment provider. 5. The **broker** notifies the **seller** of the payment by making a PATCH request to the specific order endpoint (/orders/123 in the example) of the **booking system**. This should include details of the `paymentMethod` and the `paymentMethodId` (commonly the last four digits of the card which can be used by the **seller** to identify the **customer**). The **broker** should store any details about the transaction which are needed for processing refunds or in the case of chargebacks. This is out of scope for the Booking API spec and is the responsibility of the **broker**. 6. The **booking system** notifies the **broker** of a successful order completion by returning a response with a 200 status code and an object with an appropriate value for the `confirmationCode` field. The **broker** is the entity initiating **payment** and as the merchant in the transaction is responsible for storing records of the **payment** transaction such as authorization codes. This is an implementation detail and is thus, out of scope for the Booking API and specification. ### Cancelling a Lease If the **customer** no longer wants to proceed with a particular **offer**, the lease for the space in the **opportunity** and the **order** may be cancelled by issuing a DELETE request to the specific order endpoint (/orders/123 in the example) of the **booking system**. The **booking system** SHOULD release the space in the **event** and delete the underlying resource. Any subsequent requests to this resource should result in a 404 response. ### Handling lease expiry If the lease has expired during the **payment** process, the **broker** SHOULD check whether the **opportunity** is still bookable and if the selected **offer** is still available. If they are, the **broker** should create a new **order** using the workflow described above. The **booking system** should respond to requests for **orders** with an expired lease with a 410 status code with an error specifying the `errorType` as https://openactive.io/errors/anonymous_lease_expired until the underlying **order** resource is removed. ## Order cancellation and refunds Since the **broker** is the party which initiates the third-party **payment** and is the entity which notifies the **booking system** of **payment** and creates the **order** on behalf of the **customer**, the **broker** is also the entity which handles refunds and cancellation. It should be noted that not all opportunities/offers are cancellable by the customer. There are two distinct types of cancellation: - **customer** originated cancellation - **booking system** originated cancellation These two different types of cancellation have very different workflows which are outlined below. ### Customer originated cancellation The workflow to deal with the cancellation and refund of an order by the **customer** is described below. For clarity, it is assumed that the order being cancelled may be cancelled (see section around Cancellable offers/opportunities). 1. The **broker** intiates a refund for the **customer** using the mechanisms provided by the third-party payment provider. 2. The **broker** notifies the **booking system** of the cancellation by making a PATCH request to the specific order endpoint (/orders/123 in the example) of the **booking system**. The PATCH request body should contain an updated order item in the `orderedItem` property, with the `orderItemStatus` set to https://schema.org/OrderCancelled to indicate that the ordered item in the **order** is no longer required. 3. The **booking system** responds to the **broker** with a representation of the **order** with the `orderStatus` property set to https://schema.org/OrderCancelled with a status of 200. The **order** is now deemed to be cancelled. ### Booking system originated cancellation The workflow to deal with the cancellation and refund of an order by the **booking system** is described below. Only the **booking system** can cancel **orders** relating to **opportunities** and **offers** which are not otherwise cancellable. This is to be used in situations where the underlying **opportunity**/**offer** needs to be cancelled, thus necessitating the cancellation of a **customer's** order. The mechanism for the **booking system** to notify the **broker** that an **order** is cancelled is through a webhook, where the **booking system** POSTs data relating to the orders which are cancelled to the **broker**. The **booking system** MUST only POST **orders** to a **broker** which have been initiated by that **broker**. No **customer** details should be transmitted in the POST to the webhook. The process of registering webhooks by the **broker** with the **booking system** is out of scope for this specification. One possible addition in a later version of the Booking API specification is the extension of this mechanism to cover opportunities that are rescheduled. 1. The POST sent by the **booking system** to the webhook receiver of the **broker** should contain an array of cancelled **orders**. 2. The **broker** parses the array of cancelled orders and matches them to existing **orders** from **customers**. 3. The **broker** refunds the customer and alerts them to the cancellation. It is good practice for the **booking system** to also notify the **customer** of the cancellation, where they can potentially provide more information about the rescheduling of the event. 4. The **broker** then cancels the order as described in step 2 of the previous session by making a PATCH request to the CancelAction. ### Definition of a 'cancellable' oppportunity/offer In order to provide an excellent user experience for the **customer**, the **booking system** MUST provide information about its cancellation policy in its Terms and Conditions . To avoid disappointment by the **customer**, **booking systems** MUST also make it clear prior to entering the booking workflow, whether specific opportunities and offers are cancellable, and if required, what the cancellation window is. The **broker** must always display information relating to whether the **opportunity**/**offer** is cancellable and what the cancellation window is, before the user has created an order. For an opportunity/offer to be cancellable it MUST: - contain an `isCancellable` boolean property set to 'true' If the opportunity or offer contains a `cancellationValidUntil` datetime property, it MUST NOT have already been exceeded. Different offers can potentially have different cancellation windows, or alternatively some offers may be cancellable, yet other offers on the same opportunity may not be cancellable. The data has been modelled to allow flexibility in business models for the **booking system** and the **seller**. Cancellable opportunities/offers will result in **orders** which contain a `CancelAction` in their `potentialAction` property.
# Common Requirements This section defines some common functionality that applies to the design of the whole API. ## Media types Custom media types are used in this API to allow clients to choose the format of the data they receive. **Booking platforms** MAY choose to support additional media types but MUST support the media type(s) defined by this specification. Media types will conform to the following pattern: ``` application/vnd.openactive[.version]+json ``` The current media type is: ``` application/vnd.openactive.1.0+json ``` **Brokers** specifying a media type without a version, such as: ``` application/vnd.openactive+json ``` will recieve data in in the most current format which is 1.0. If you are building an application as a **broker** and care about the stability of the response, or have a client application which is tied to a specific version you should always try to use a media type containing the version number. **Booking systems** are not obliged to support previous versions of the specification, and so may not support a specific version number. We anticipate that when the Booking API reaches the stability of a 1.0 version that this advice may change. ## Response formats Requests and response documents exchanged via this API will all be valid [[!JSON-LD]] documents. The documents MUST include a [[!JSON-LD]] `context` that refers to the [[!OpenActive-Vocabulary]] as follows:
{
  "@context": "https://openactive.io/ns/oa.jsonld"
}
Unless otherwise specified the request and response documents MUST conform to the [[!Modelling-Opportunity-Data]] data model. ## URL discovery The Booking API has been defined around the concept of URL discovery. This allows **booking systems** to create resource URLs which match their own terms for naming specific objects such as **events** and **orders**. The URL for a specific action, such as for example providing **payment** details, may be discovered by parsing the actions defined in the `potentialAction` property of an object and constructing the appropriate URL from the `urlTemplate` property of the `schema:EntryPoint` object contained within the `target` property of the `potentialAction` required. An example is below:
"potentialAction": [
        {
          "type": "PayAction",
          "name": "Payment",
          "target": {
            "type": "EntryPoint",
            "urlTemplate": "https://example.com/orders",
            "encodingType": "application/vnd.openactive.v1.0+json",
            "httpMethod": "PATCH"
          }
        }
      ]
Since discovery of URLs is an implicit part of the specification, **brokers** SHOULD avoid hard-coding URLs for specific actions into their applications and client libraries. **Brokers** SHOULD use most applicable URL discovered at the point in the workflow they are currently in - e.g. not use a cached URL that has been previously successfully used. They should also not attempt to create URLs for a specific action based on patterns within previously used URLs. It is the responsibility of **booking platforms** to advertise the URL for further activity in the booking workflow at the point of need. For example they MUST advertise the URL for **order** creation when a **broker** requests a specific **opportunity** resource and SHOULD advertise it in their RDPE feeds since both of these points within the booking workflow are places where a \ **customer** may wish to check availability. ## Error handling Error responses MUST be served using an appropriate HTTP 4XX status code or HTTP 5XX status code. All error responses from this API MUST be returned in a JSON-LD format using a content type of `application/vnd.openactive.v1.0+json` and include at least one Error object. The error handling has in the Booking API has been modelled closely on [[!RFC7807]].
{
  "@context": "https://openactive.io/ns/oa.jsonld",
  "type": "Error",
  "errorType": "https://openactive.io/errors/incomplete_customer_details",
  "title": "Incomplete customer details",
  "details": "No customer details supplied",
  "status": 400,
  "instance": "/orders",
  "method": "POST"
}
* `errorType` – a URI reference [[!RFC3986]] that identifies the problem type. This specification encourages that, when dereferenced, it provide human-readable documentation for the problem type. When this member is not present, its value is assumed to be "about:blank". * `title` – a short, human-readable summary of the problem type. It SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization. * `description` – a slightly longer, human-readable summary of the problem type. It largely SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization or to provide specific information about why the error occurred in that particular case. * `status` – the HTTP status code ([[!RFC7231]], Section 6) generated by the origin server for this occurrence of the problem. * `instance` – a URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. * `requestId` – used by technical support for diagnostics purposes. ### Order creation : Incomplete customer details Use cases: - if the `email` address of the customer is not supplied within a schema:Person object. - if the `customer` property supplied is not a valid schema:Person object. errorType URI: - https://openactive.io/errors/incomplete_customer_details Status code: 400 ### Order creation : Incomplete broker details Use cases: - if there is insuffficient detail in the schema:Organisation object describing the **broker** - if the `broker` property supplied is not a valid schema:Organisation object. errorType URI: - https://openactive.io/errors/incomplete_broker_details Status code: 400 ### Order creation : Incomplete offer details Use cases: - if there is no `acceptedOffer` property on the schema:OrderItem - if the `acceptedOffer` is not a URL which corresponds to an **offer** on the **booking system** errorType URI: - https://openactive.io/errors/incomplete_offer_details Status code: 400 ### Order creation : Incomplete opportunity details Use cases: - if there is no `orderedItem` property on the schema:OrderItem - if the `orderedItem` is not a URL which corresponds to an **opportunity** on the **booking system** errorType URIs: - https://openactive.io/errors/incomplete_event_details - https://openactive.io/errors/incomplete_facilityuse_details - https://openactive.io/errors/incomplete_slot_details - https://openactive.io/errors/incomplete_opportunity_details Status code: 400 ### Order creation : Unavailable offer Use cases: - if the **offer** contained in the `acceptedOffer` property is not bookable errorType URI: - https://openactive.io/errors/unavailable_offer Status code: 400 ### Order creation : Unavailable opportunity Use cases: - if the **opportunity** contained in the `orderedItem` property is not bookable errorType URIs: - https://openactive.io/errors/unavailable_event - https://openactive.io/errors/unavailable_facilityuse - https://openactive.io/errors/unavailable_slot - https://openactive.io/errors/unavailable_opportunity Status code: 400 ### Order creation : Opportunity full Use cases: - if there are no available spaces for the **opportunity** contained in the `orderedItem` property errorType URIs: - https://openactive.io/errors/event_is_full - https://openactive.io/errors/slot_is_full - https://openactive.io/errors/opportunity_is_full Status code: 400 ### Order completion : Incomplete payment details Use cases: - if the `paymentMethod` or `paymentMethodId` properties are missing or incorrect errorType URI: - https://openactive.io/errors/incomplete_payment_details Status code: 400 ### Order completion : Anonymous lease expired Use cases: - if **broker** did not supply **payment** details between the creation of the **order** and the expiration of the lease. errorType URI: - https://openactive.io/errors/anonymous_lease_expired Status code: 410 ### API authentication : Unauthenticated Use cases: - if **broker** did not supply any form of authentication errorType URI: - https://openactive.io/errors/unauthenticated Status code: 403 ### API authentication : No API token provided Use cases: - if **broker** did not supply an API key errorType URI: - https://openactive.io/errors/no_api_token_provided Status code: 403 ### API authentication : Invalid API token provided Use cases: - if **broker** supplied an invalid API key, either malformed or expired errorType URI: - https://openactive.io/errors/invalid_api_token_provided Status code: 401 ### API authentication : Invalid authorization details provided Use cases: - if **broker** supplied an invalid set of authorization details, either malformed or expired errorType URI: - https://openactive.io/errors/invalid_authorization_details Status code: 401 ### Technical errors : Temporarily unable to create order Use cases: - if **booking platform** is unable for technical reasons to create an **order** where the data provided to it is sufficient to allow it to do so errorType URI: - https://openactive.io/errors/temporarily_unable_to_create_order Status code: 500 ### Technical errors : Temporarily unable to create order Use cases: - if **booking platform** is unable for technical reasons to update an **order** where the data provided to it is sufficient to allow it to do so errorType URI: - https://openactive.io/errors/temporarily_unable_to_update_order Status code: 500 ### Technical errors : Temporarily unable to delete order Use cases: - if **booking platform** is unable for technical reasons to delete an **order** where the data provided to it is sufficient to allow it to do so errorType URI: - https://openactive.io/errors/temporarily_unable_to_delete_order Status code: 500 ### Generic errors : Unknown or incorrect offer Use cases: - where a **booking platform** has no **offer** matching the one requested errorType URI: - https://openactive.io/errors/unknown_or_incorrect_offer Status code: 404 ### Generic errors : Unknown or incorrect opportunity Use cases: - where a **booking platform** has no **opportunity** matching the one requested errorType URI: - https://openactive.io/errors/unknown_or_incorrect_opportunity - https://openactive.io/errors/unknown_or_incorrect_event - https://openactive.io/errors/unknown_or_incorrect_facilityuse - https://openactive.io/errors/unknown_or_incorrect_slot Status code: 404 ### Generic errors : Unknown or incorrect order Use cases: - where a **booking platform** has no **order** matching the one requested errorType URI: - https://openactive.io/errors/unknown_or_incorrect_order Status code: 404 ### Generic errors : Unknown or incorrect endpoint Use cases: - where a **booking platform** has no endpoint matching the one requested errorType URI: - https://openactive.io/errors/unknown_or_incorrect_endpoint Status code: 404 ### Generic errors : Not found Use cases: - where a **booking platform** does not have the generic resource specified errorType URI: - https://openactive.io/errors/not_found Status code: 404 ### Generic errors : Method not allowed Use cases: - where a **booking platform** does not a speficic HTTP method for the endpoint requested with that specific HTTP verb errorType URI: - https://openactive.io/errors/method_not_allowed Status code: 405 ### Generic errors : Gone Use cases: - where an **order** has been cancelled and the underlying resources are marked for deletion errorType URI: - https://openactive.io/errors/gone Status code: 410 ## Security All HTTP requests and responses SHOULD be secured using SSL. ## Authentication ### API level authentication All of the API transactions described in this document SHOULD require authentication. This standard does not mandate a particular authentication method, but recommendation is that implementers SHOULD consider using [[OAuth2]] as it is well-defined, widely supported and can be used in a variety of different application flows (e.g. via a Javascript web application or between servers). Other server to server authentication mechanisms such as basic authentication, API key or bearer tokern based mechanisms are also appropriate. One principle relating to authentication and the Booking API is that **orders** created by a specific **broker** SHOULD NOT be able to be changed by any other **broker**. It is the responsibility of the **booking system** to ensure that subsequent requests to an **order** should only be initiated by the **broker** which created the **order**. ### Customer level authentication Where necessary, OpenID Connect ([[OpenIdConnect]]) provides a standard way of verifying the identity of customer. It is a thin layer that sits atop OAuth2. One principle relating to authentication and the Booking API is that **orders** created by a specific **customer** SHOULD NOT be able to be changed by any other **customer**. It is the responsibility of the **booking system** to ensure that subsequent requests to an **order** should only be initiated by the **customer** which created the **order**. ## Delivery of Terms and Conditions and Privacy Policy The **broker** should make the **customer** aware of and assent to any specific Terms and Conditions and Privacy Policy for using their service at the point of a **customer** signing up to the service and/or providing their contact details. Since, during **booking**, the details of the **customer** are provided to the **booking system** and the **seller**, the **broker** should inform the **customer** of this any implications with resepct to GDPR and any other data protection legislation. Specific Terms and Conditions/Privacy Policy for the **booking service**/**seller** and/or the **opportynity**/**offer** being taken MUST be made available using the `privacyPolicy` and `termsAndConditions` properties described in the example at the most appropriate level. If the properties are present at the **opportunity** level, they apply to all included **opportunities** and **offers** unless the properties are present at a lower level.
  "privacyPolicy": {
		"type": "Terms",
		"url": "https://example.com/privacy-policy",
		"name": "Privacy Policy"
	},
	"termsAndConditions": {
		"type": "Terms",
		"url": "https://example.com/terms-and-conditions",
		"name": "Terms and Conditions"
	}

The URLs in the example are only illustrative.
# Operations ## Checking availability and offers This specification assumes that a *broker* has already obtained opportunity data from a *booking system* that includes descriptions of **events**, **facilities**, **slots** etc. For example by harvesting data via an [[RPDE]] feed. The availability of an **event**, or `subEvent`, or **facility** (either as a `FacilityUse` or `Slot`) may change at any time. The **offers** available to a customer might also change over time and the data should be refreshed so that the most up-to-date representation of the live data is presented to the **purchaser** before booking. A **booking system** MUST allow a **broker** to check the current state of **events** and/or **facilities** depending on what decide they expose from their platform. ### Definition of a 'bookable' Event An **event** is deemed to be 'bookable' if: - The **event** has not already occurred, i.e. the `startDate` (when expressed as a timedatestamp) and the `endDate` of the **event** has not been exceeded - The `eventStatus` of the **event** does not indicate that it has been cancelled or postponed - The `remainingAttendeeCapacity` of the **event** is greater than the number of places required - The `potentialAction` of the **event** contains one or more appropriate ReserveAction objects - There are one or more valid bookable **offers** for the **event** - The temporary lease on the space in the **event** (defined in `orderLeaseDuration`) has not expired before the **payment** is taken by the **broker** and the **seller** is notified by the **broker** that payment has taken place. The **customer** may be ineligible to attend a 'bookable' **event**, but may book a space for someone else (e.g. a child) if: - The gender of the **customer** taking up the opportunity is incompatible with the `genderRestriction` of the **event** but the attendee is compatible - The age of the **customer** taking up the opportunity is incompatible with the `ageRange` of the **event** but the attendee is compatible The **event** may also be deemed to be bookable if the **event** contains a deep link to a URL where the user can book the **event** independently, however this is out of scope for this API The above set of restrictions are at the level of the **event** and the data contained within the content of the API returns. On a **booking system** level, an **event** is 'bookable', mediated by a specific **broker**, if that **broker** has machine readable permission to access the relevant instance of the Booking API provided by the **booking** system via an API key or similar token. For the purposes of this specification, the **event** is only bookable if the data describing the the **event** is published via a **booking system** which is compliant with and supports the Open Active Booking API. ### Definition of a 'bookable' slot/facility use A slot is essentially an event, and many of the above conditions apply, however some of the properties for a slot are different and as a consequence there are different criteria for it being bookable. A **slot** is deemed to be 'bookable' if: - The **slot** has not already occurred, i.e. the `startDate` (when expressed as a timedatestamp) and `endDate` of the **slot** has not been exceeded - The `remainingUses` of the **slot** is greater than or equal to the number of slots required. For example if an independently organised badminton league requires four courts, the number of `remainingUses` should be 4 or higher. - There are one or more valid **offers** for the slot - The `potentialAction` of the **slot** contains one or more appropriate ReserveAction objects There may be more restrictions placed by a **seller** on the booking of a **slot** for use of a **facility**. For example, it may be that one of the party must be over a certain age as a supervising adult if some of the party are minors. It is the responsibility of the **booking platform** to make the terms and conditions of **booking** available to the **broker** and it is the responsibility of the **broker** to ensure that the **customer** is aware of these conditions before entering the **order** workflow. ### Checking availability and offers for an event or facility use slot To check the availability of an opportunity a **broker** will issue a GET request to the URI specified in the `id` of the opportunity, having discovered it from the RDPE feed. It is the responsibility of **brokers** to ensure that spaces within the opportunity are still available before proceeding with booking.
GET /events/452 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:50:35 GMT
Accept: application/vnd.openactive.v1.0+json
        
HTTP/1.1 200 OK
Date: Mon, 8 Oct 2018 20:50:36 GMT
Content-Type: application/vnd.openactive.v1.0+json
Content-Length: ...


{
	"@context": "https://openactive.io/ns/oa.jsonld",
	"type": "Event",
	"identifier": 452,
	"id": "https://example.com/events/452",
	"name": "Speedball",
	"offers": [{
		"type": "Offer",
		"validThrough": "2018-10-29T11:00:00Z",
		"validFrom": "2018-10-01T11:00:00Z",
		"description": "Winger space for Speedball.",
		"id": "https://example.com/offers/1234",
		"name": "Speedball winger position",
		"price": 10.00,
		"priceCurrency": "GBP",
		"isCancellable": true,
		"cancellationValidUntil": "2018-10-28T11:00:00Z"
	}],
	"eventStatus": "https://schema.org/EventScheduled",
	"maximumAttendeeCapacity": 30,
	"remainingAttendeeCapacity": 20,
	"startDate": "2018-10-30T11:00:00Z",
	"endDate": "2018-10-30T12:00:00Z",
	"duration": "PT1H",
	"location": {},
	"potentialAction": [{
		"type": "ReserveAction",
		"name": "Book",
		"target": {
			"type": "EntryPoint",
			"urlTemplate": "https://example.com/orders",
			"encodingType": "application/vnd.openactive.v1.0+json",
			"httpMethod": "POST"
		}
	}],
	"privacyPolicy": {
		"type": "Terms",
		"url": "https://example.com/privacy-policy",
		"name": "Privacy Policy"
	},
	"termsAndConditions": {
		"type": "Terms",
		"url": "https://example.com/terms-and-conditions",
		"name": "Terms and Conditions"
	}
}

The **booking system** MUST ensure that the data presented to the **broker** in the `remainingAttendeeCapacity` property of the **event** is up to date and includes a combination of places already booked and places currently reserved by leases from in-progress **orders**.
GET /slots/324 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:51:35 GMT
Accept: application/vnd.openactive.v1.0+json
HTTP/1.1 200 OK
Date: Mon, 8 Oct 2018 20:51:36 GMT
Content-Type: application/vnd.openactive.v1.0+json
Content-Length: ...


{
	"@context": "https://openactive.io/ns/oa.jsonld",
	"type": "Slot",
	"id": "http://www.example.org/slots/324",
	"identifier": 324,
	"facilityUse": "http://www.example.org/facility-use/1",
	"startDate": "2018-10-01T11:00:00Z",
	"duration": "PT30M",
	"remainingUses": 3,
	"maximumUses": 4,
	"offers": [{
		"type": "Offer",
		"name": "30 minute hire",
		"price": 15.00,
		"priceCurrency": "GBP"
	}],
  "potentialAction": [{
    "type": "ReserveAction",
    "name": "Book",
    "target": {
      "type": "EntryPoint",
      "urlTemplate": "https://example.com/orders",
      "encodingType": "application/vnd.openactive.v1.0+json",
      "httpMethod": "POST"
    }
  }]
}

The **booking system** MUST ensure that the data presented to the **broker** in the `remainingUses` property of the **slot** is up to date and includes a combination of places already booked and places currently reserved by leases from in-progress **orders**.
GET /facility-use/1 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:51:35 GMT
Accept: application/vnd.openactive.v1.0+json
HTTP/1.1 200 OK
Date: Mon, 8 Oct 2018 20:51:36 GMT
Content-Type: application/vnd.openactive.v1.0+json
Content-Length: ...

{
	"@context": "https://www.openactive.io/ns/oa.jsonld",
	"type": "FacilityUse",
	"url": "http://www.example.org/facility-use/1",
	"name": "Example Leisure Centre Table Tennis",
	"description": "Table tennis tables are available to hire for thirty minute slots",
	"activity": "Table Tennis",
	"location": {
		"type": "Place",
		"name": "Example Leisure Centre",
		"address": {
			"type": "PostalAddress",
			"streetAddress": "1 High Street",
			"addressLocality": "Bristol",
			"postalCode": "BS1 4SD"
		}
	},
	"offers": [{
		"type": "Offer",
		"name": "30 minute hire",
		"price": 10.00,
		"priceCurrency": "GBP"
	}],
  "potentialAction": [{
    "type": "ReserveAction",
    "name": "Book",
    "target": {
      "type": "EntryPoint",
      "urlTemplate": "https://example.com/orders",
      "encodingType": "application/vnd.openactive.v1.0+json",
      "httpMethod": "POST"
    }
  }],
	"event": [{
			"type": "Slot",
			"id": "http://www.example.org/slots/123",
			"facilityUse": "http://www.example.org/facility-use/1",
			"startDate": "2018-03-01T10:00:00Z",
			"duration": "PT30M",
			"remainingUses": 0,
			"maximumUses": 4
		},
		{
			"type": "Slot",
			"id": "http://www.example.org/slots/234",
			"facilityUse": "http://www.example.org/facility-use/1",
			"startDate": "2018-03-01T11:00:00Z",
			"duration": "PT30M",
			"remainingUses": 3,
			"maximumUses": 4,
			"offers": [{
				"name": "30 minute hire",
				"price": 15.00,
				"priceCurrency": "GBP"
			}]
		}
	]
}

### Assembling a set of offers and presenting them to a customer The **broker** MUST assemble a set of all applicable **offers** for an **opportunity** from the information supplied by the **booking system**. An applicable **offer** is an **offer** which is available for an **opportunity** which is bookable and for which the **customer** is eligible. #### Collating offers for an event In the case of the **event** having no `subEvent` the set of all applicable **offers** is simply the array of **offers** contained within the `offers` property. In the case of an **event** containing sub-events within the `subEvent` property the set of all applicable offers should be compiled from the **offers** from the sub-event selected by the customer, augmented with any additional different offers contained in the parent super-event. If the sub-event selected by the **customer** has no **offers** the set of all applicable offers will be compiled from the **offers** contained withinin the super-event. The **broker** should then filter this set to provide a set of current **offers** which apply to the **customer**. This specification provides no guidance as to how the filtering should be performed. The **broker** SHOULD however show all applicable **offers** to a customer, including information on pricing restrictions, unless the **customer** has only requested a subset in some form of preferences (e.g. only show adult classes and offers). The **customer** selects one of these **offers** and when all pre-conditions have been met, the **broker** enters the **order** creation workflow on behalf of the customer. #### Collating offers for the use of a facility The process for building a set of all applicable offers is similar for **facilities** with the **offers** which are part of a specific **slot** having primacy over the more generic **offers** contained within a`FacilityUse`. The **booking system** MUST ensure that the data presented to the **broker** in the `remainingUses` property in the **slot** is up to date and includes a combination of places already booked and places currently reserved by leases from in-progress **orders**. The **booking system** MUST also ensure that the `hoursAvailable` property on the `FacilityUse` is kept updated. ### Obtaining a lease on a space within an opportunity **Orders** SHOULD contain a `orderLeaseDuration` property. This property MUST contain an ISO 8601 formatted duration for the lease. The lease duration may also be advertised somewhere else to make it clear in advance, e.g. in docs/setup config, or as part of a `potentialAction` property. (Note: In previous iterations of the Booking API specification, the lease was a property of an event. This has now been deprecated.) The `orderLeaseDuration` property notifies the **broker** about the length of time for which a space will be reserved by the **booking system** whilst payment for the **event** is taken by the third-party payment system. The default suggested lease duration is 15 minutes, but it is up to each **booking plaftorm** to define and advertise their own lease duration. This lease is NOT designed as a pause for a shopping cart system (in addition shopping cart functionality is out of scope for v1.0 of this specification). The **broker** MUST ensure that it has obtained all of the relevant details about a **customer** before getting to the point where it attempts to place an **order**. The lease is not designed as a pause in the process whilst a **customer** is registered by a **broker**. The client may then present the updated availability and suitable **offers** to the customer. ## Creating and updating orders **Booking systems** MUST expose an **orders** endpoint, e.g. `/orders` to allow **brokers** to initiate the **booking** workflow. **Orders** initiated by a specific **broker** SHOULD only be able to be updated by the same **broker**. For the purpose of creating and updating an **order** the only reliable mechanism for ensuring that the **broker** is the same across requests is via authentication/identification by API keys or Authoriztion headers. The **broker** property within the **order** is for information only. It is the responsibility of the **booking system** to ensure that only the **broker** which created the **order** can update, complete and cancel an order. ### Discovering the order endpoint **Booking systems** MUST expose an orders endpoint, e.g. /orders to allow **brokers** to initiate and enter the **booking** workflow. To initiate the **booking workflow**, the **booking system** must include a `potentialAction` field within each **bookable** **opportunity**. The `potentialAction` field MUST contain one or more objects of type ReserveAction. These ReserveAction objects must contain a `target` field containing an EntryPoint object. The EntryPoint object MUST state the `url` of the orders endpoint, and SHOULD contain the `encodingType` and the `httpMethod` which must be POST as a new **order** object will be created on successful invocation. If the **offer** relates to a **slot** within a `FacilityUse`, the closest `potentialAction` property matching the type `ReserveAction` should be used. An example is below:
"potentialAction": [
  {
    "type": "ReserveAction",
    "name": "Book",
    "target": {
      "type": "EntryPoint",
      "urlTemplate": "https://example.com/orders",
      "encodingType": "application/vnd.openactive.v1.0+json",
      "httpMethod": "POST"
    }
  }
]

### Pre-conditions for creating an order Before creating an **order** the **broker** MUST: - have obtained **customer** information - have determined that the **opportunity** is bookable (see sections on the definition of bookable for events and slots/facility uses) - have identified the selected **offer** - have prompted **customer** that they will enter a **payment** flow (no pre-emptive leasing) - have indicates that the **customer's** personal information is being submitted to third-party (the **booking system** and the **seller**) and made the customer aware of and assent to any legitimate privacy policy and terms and conditions - have already obtained permission to use API Before creating an **order** the **broker** SHOULD: - have registered the **customer** (or have the **customer's** details to hand) - have identified the **customer's** preferred payment options ### Creating a new order (`/orders`) A POST request to the `/orders` endpoint of the **booking system** will result in server creating an new **Order**. A time limited lease will be created for spaces in the **event** referenced in the **Order**. The `broker` property in the request body is to help the **booking platform** to clarify who is making request and to help them in situations such as queries to allow reconciliation of payments. It is not designed as any part of an authentication which should be performed outside of the JSON body of the request. No authentication tokens should be present within the `broker` property of the submitted JSON. The `broker` property should be a well formed schema:Organisation object. In the situation where a middleware to be used to handle access control for various **brokers**, independently of API key provisioning at the **booking system** level (which in some systems is a lengthly and time-consuming process), the `broker` property should contain the details of the end-user **broker** rather than the middleware. To acheive compliance with Apple Pay, Google Pay and Google Reserve, the customer's telephone number, an optional field, should be supplied.
POST /orders HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.v1.0+json

{
	"@context": "https://openactive.io/ns/oa.jsonld",
	"type": "Order",
	"broker": {
		"type": "Organization",
		"name": "MyFitnessApp",
		"url": "https://myfitnessapp.example.com"
	},
	"customer": {
		"type": "Person",
		"email": "[email protected]",
		"telephone": "020 811 8055",
		"givenName": "Geoff",
		"familyName": "Capes"
	},
	"acceptedOffer": [{
		"type": "Offer",
		"validThrough": "2018-10-29T11:00:00Z",
		"validFrom": "2018-10-01T11:00:00Z",
		"description": "Winger space for Speedball.",
		"id": "https://example.com/offers/1234",
		"itemOffered": {
			"type": "Event",
			"id": "https://example.com/events/123"
		},
		"name": "Speedball winger position",
		"price": 10.00,
		"priceCurrency": "GBP",
		"isCancellable": true,
		"cancellationValidUntil": "2018-10-28T11:00:00Z"
	}],
	"orderedItem": [{
		"orderQuantity": "1",
		"orderedItem": {
			"type": "Event",
			"id": "https://example.com/events/123"
		}
	}]
}

If successful the server will response with the location of a newly created `Order` resource:
HTTP/1.1 201 Created
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.v1.0+json
Location: /orders/890


{
	"@context": "https://openactive.io/ns/oa.jsonld",
	"type": "Order",
	"id": "https://example.com/orders/890",
	"acceptedOffer": [{
		"type": "Offer",
		"validThrough": "2018-10-29T11:00:00Z",
		"validFrom": "2018-10-01T11:00:00Z",
		"description": "Winger space for Speedball.",
		"id": "https://example.com/offers/1234",
		"itemOffered": {
			"type": "Event",
			"id": "https://example.com/events/123"
		},
		"name": "Speedball winger position",
		"price": 10.00,
		"priceCurrency": "GBP",
		"isCancellable": true,
		"cancellationValidUntil": "2018-10-28T11:00:00Z"
	}],
	"broker": {
		"type": "Organization",
		"name": "MyFitnessApp",
		"url": "https://myfitnessapp.example.com"
	},
	"customer": {
		"type": "Person",
		"email": "[email protected]",
		"telephone": "020 811 8055",
		"givenName": "Geoff",
		"familyName": "Capes"
	},
	"orderDate": "2018-10-08T20:52:36Z",
	"orderedItem": [{
		"type": "OrderItem",
		"orderQuantity": 1,
		"orderItemStatus": "https://schema.org/OrderPaymentDue",
		"orderedItem": {
			"type": "Event",
			"id": "https://example.com/events/123"
		}
	}],
	"orderLeaseDuration": "PT15M",
	"orderStatus": "https://schema.org/OrderPaymentDue",
	"partOfInvoice": {
		"type": "Invoice",
		"paymentStatus": "PaymentDue",
		"totalPaymentDue": {
			"type": "MonetaryAmount",
			"value": 10.00,
			"currency": "GBP"
		}
	},
	"paymentDueDate": "2018-10-08T21:07:36Z",
	"potentialAction": [{
		"type": "PayAction",
		"name": "Pay",
		"target": {
			"type": "EntryPoint",
			"urlTemplate": "https://example.com/orders/{order_id}",
			"encodingType": "application/vnd.openactive.v1.0+json",
			"httpMethod": "PATCH"
		}
	}]
}

### Viewing an order (`/orders/{orderId}`) To get the latest state of an order, perform a GET request on its URI.
GET /orders/890 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:53:35 GMT
Accept: application/vnd.openactive.v1.0+json
    
HTTP/1.1 200 Created
Date: Mon, 8 Oct 2018 20:53:36 GMT
Content-Type: application/vnd.openactive.v1.0+json
Content-Length: ...

{
	"@context": "https://openactive.io/ns/oa.jsonld",
	"type": "Order",
	"id": "https://example.com/orders/890",
	"acceptedOffer": [{
		"type": "Offer",
		"validThrough": "2018-10-29T11:00:00Z",
		"validFrom": "2018-10-01T11:00:00Z",
		"description": "Winger space for Speedball.",
		"id": "https://example.com/offers/1234",
		"itemOffered": {
			"type": "Event",
			"id": "https://example.com/events/123"
		},
		"name": "Speedball winger position",
		"price": 10.00,
		"priceCurrency": "GBP",
		"isCancellable": true,
		"cancellationValidUntil": "2018-10-28T11:00:00Z"
	}],
	"broker": {
		"type": "Organization",
		"name": "MyFitnessApp",
		"url": "https://myfitnessapp.example.com"
	},
	"customer": {
		"type": "Person",
		"email": "[email protected]",
		"telephone": "020 811 8055",
		"givenName": "Geoff",
		"familyName": "Capes"
	},
	"orderDate": "2018-10-08T20:52:36Z",
	"orderedItem": [{
		"type": "OrderItem",
		"orderQuantity": 1,
		"orderItemStatus": "https://schema.org/OrderPaymentDue",
		"orderedItem": {
			"type": "Event",
			"id": "https://example.com/events/123"
		}
	}],
	"orderLeaseDuration": "PT15M",
	"orderStatus": "https://schema.org/OrderPaymentDue",
	"partOfInvoice": {
		"type": "Invoice",
		"paymentStatus": "PaymentDue",
		"totalPaymentDue": {
			"type": "MonetaryAmount",
			"value": 10.00,
			"currency": "GBP"
		}
	},
	"paymentDueDate": "2018-10-08T21:07:36Z",
	"potentialAction": [{
			"type": "PayAction",
			"name": "Pay",
			"target": {
				"type": "EntryPoint",
				"urlTemplate": "https://example.com/orders/{order_id}",
				"encodingType": "application/vnd.openactive.v1.0+json",
				"httpMethod": "PATCH"
			}
		}
	]
}

Note: The `paymentDueDate` is set by the date and time of the **order** and the `orderLeaseDuration`. ### Deleting an order (`/orders/{orderId}`) A `DELETE` request to an **Order** URI will delete the order. Deletion may only be used in the specific case for cancelling an **order** and the associated lease on a space in an **event** if the **customer** no longer wishes to take up the opportunity. A `DELETE` request MUST NOT be used to cancel an existing completed **order** as part of a refund/cancellation workflow. An appropriate approach for that is defined elsewhere. ### Completing an order (`/orders/{orderId}`) An **order** is completed by the **broker** submitting notification that the **customer** has made a payment via a third-party payment gateway. An **order** is not considered complete until payment has occurred. **Brokers** MUST update the **booking system** to indicate that **payment** has been taken. Once the **booking system** is content with the payments, it then sets the `orderStatus` property of the `Order` to https://schema.org/OrderDelivered and returns a representation of the completed **order** with a 200 status code.
PATCH /orders/123 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:56:35 GMT
Accept: application/vnd.openactive.v1.0+json

{
	"type": "Order",
	"payments": [{
		"type": "Payment",
		"paymentMethod": "https://purl.org/goodrelations/v1#PaymentMethodCreditCard",
		"paymentMethodId": "3204",
		"totalPaidToProvider": {
			"type": "MonetaryAmount",
			"value": 10.00,
			"currency": "GBP"
		}
	}]
}

HTTP/1.1 200 OK
Date: Mon, 8 Oct 2018 20:56:36 GMT
Content-Type: application/vnd.openactive.v1.0+json
Content-Length: ...


{
	"@context": "https://openactive.io/ns/oa.jsonld",
	"type": "Order",
	"id": "https://example.com/orders/890",
	"acceptedOffer": [{
		"type": "Offer",
		"validThrough": "2018-10-29T11:00:00Z",
		"validFrom": "2018-10-01T11:00:00Z",
		"description": "Winger space for Speedball.",
		"id": "https://example.com/offers/1234",
		"itemOffered": {
			"type": "Event",
			"id": "https://example.com/events/123"
		},
		"name": "Speedball winger position",
		"price": 10.00,
		"priceCurrency": "GBP",
		"isCancellable": true,
		"cancellationValidUntil": "2018-10-28T11:00:00Z"
	}],
	"broker": {
		"type": "Organization",
		"name": "MyFitnessApp",
		"url": "https://myfitnessapp.example.com"
	},
	"customer": {
		"type": "Person",
		"email": "[email protected]",
		"telephone": "020 811 8055",
		"givenName": "Geoff",
		"familyName": "Capes"
	},
	"orderDate": "2018-10-08T20:52:36Z",
	"orderedItem": [{
		"type": "OrderItem",
		"orderQuantity": 1,
		"orderItemStatus": "https://schema.org/OrderDelivered",
		"orderedItem": {
			"type": "Event",
			"id": "https://example.com/events/123"
		}
	}],
	"orderStatus": "https://schema.org/OrderDelivered",
	"partOfInvoice": {
		"type": "Invoice",
		"paymentStatus": "PaymentComplete",
		"totalPaymentDue": {
			"type": "MonetaryAmount",
			"value": 10.00,
			"currency": "GBP"
		}
	},
	"payments": [{
		"type": "Payment",
		"paymentMethod": "https://purl.org/goodrelations/v1#PaymentMethodCreditCard",
		"paymentMethodId": "3204",
		"totalPaidToProvider": {
			"type": "MonetaryAmount",
			"value": 10.00,
			"currency": "GBP"
		},
		"paymentDate": "2018-10-08T20:56:36Z"
	}],
	"potentialAction": [{
		"type": "CancelAction",
		"name": "Cancel",
		"target": {
			"type": "EntryPoint",
			"urlTemplate": "https://example.com/orders/{order_id}",
			"encodingType": "application/vnd.openactive.v1.0+json",
			"httpMethod": "PATCH"
		}
	}]
}

Note: If the record had been retrieved after the expiry of the cancellation window, the `CancelAction` would be omitted. ## Ordering a place at a free event/ordering a free slot Free **opportunities** MUST follow the same workflow as paid **opportunities**. A free **event** or **slot** is technically a free **offer** on a space for an opportunity. There MUST be at least one offer with a price property of `0.00`. For free Offers publishers MAY leave out the priceCurrency. If a priceCurrency is not available then brokers SHOULD display a suitable default currency when displaying a zero price. The **order** workflow MUST be followed as described for **offers** with a payment. The PATCH to the URL defined in `PayAction` should follow the example shown below. **Orders** for a free place at an **opportunity** are the only current scenario in the Booking API specification where a `Payment` object has `paymentMethod` and `paymentMethodId` as optional properties.
PATCH /orders/123 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:51:35 GMT
Accept: application/vnd.openactive.v1.0+json

{
  "type": "Order",
  "payments": [{
    "type": "Payment",
    "totalPaidToProvider": {
      "type": "MonetaryAmount",
      "value": 0.00,
      "currency": "GBP"
    }
  }]
}

## Leases and the orderLeaseDuration property If the lease expires during **payment**, the **broker** MAY delete the **order** (if the **booking system** has not already done so, returning a response with a `410` status code). The **broker** should then create a new **order** for the **customer** in order to generate a new lease. Before creating a new order, the **broker** MUST check the **opportunity** and **offers** to ensure that a space still exists within the **opportunity** and that the **offer** selected by the **customer** is still valid. If no space exists or the **offer** is now invalid, the **customer** should be refunded if payment has been taken. ## The state transitions during the order process 1. An **order** object is created by a **broker** invoking the orders endpoint with a POST request containing details of the **broker** (represented as a schema:Organisation), **customer** (represented as a schema:Person) and details of the selected **event** and **offer** as components of a schema:OrderItem object. The **booking system** returns a representation of the **order** object to the **broker** in a response with a 201 status and the URL for the object in the `Location` header. The `orderStatus` of the **order** at this point in the flow should be https://schema.org/OrderPaymentDue 2. Concurrently the **booking system** also creates a lease for a space in the **event** for the **customer** for the duration specified in the `orderLeaseDuration` property of the **event**. 3. The **broker** directs the customer into the flow of the third-party **payment** provider. 4. If the **customer** successfully completes the payment within the duration of the lease, the **broker** sends notification of the payment with a PATCH request to the specific order end point (/orders/123 in the examples) with details of the **payment** in the `paymentMethod` and `paymentMethodId` properties. At this point the **order** is deemed to be completed. The `orderStatus` property should be currently set to `https://schema.org/OrderDelivered` and the `confirmationNumber` property set to an appropriate unique value by the **booking system**. At this point in the flow, the **order** is deemed to be complete. If the lease expires before the payment is made, the **order** should either be deleted by the **broker** or be deleted by the **booking system** at some point after expiry. The `orderStatus` property should be set to https://schema.org/OrderCancelled to indicate that the **order** cannot proceed to completion. ## Actions which occur after order completion: confirmations and refunds Both the **broker** and **booking system** may need to take further actions after completing an order. This section describes some potential interactions. ### Supplying a confirmation to the customer The **broker** SHOULD provide the **customer** with confirmation of a successful payment having been made through the third-party system and that the **order** has been completed successfully. The **broker** will have access to a completed **order** which will contain a `confirmationCode` property. The **booking system** may also provide the **customer** with a confirmation that the space at the **opportunity** has been booked on their behalf. ### Refunding a customer and cancelling an order Refunds and cancellations are a responsibility of the **broker** who is the party in the system in a position to process a refund for the **customer** with the third-party payments system. The **broker** should initiate a refund with the third-party payment provider. The refund process should be considered to be asynchronous for the purposes of this specification. After initiating a refund, the **broker** should cancel the **order** by submitting a PATCH request to the URL defined in the `CancelAction` prescribed in the `potentialAction` property of the **order** response. The process of cancellation is documented in Section 5.6 ## Customers ### Creation of customers on the booking system In this current iteration of the Booking API specification, the programmatic creation of **customers** on the **booking system** by the **broker** is considered to be out of scope. ### Viewing a customer's previously placed orders A server MUST provide endpoints to allow **brokers** to retrieve information about previously placed **orders**. In future versions of the specification, endpoints may be provided which permit a **broker** to see all **orders** created by a **customer**. In this current implementation, if the **broker** wishes to present a view of all **orders** for a **customer**, it should keep an internal record. ### Deletion of personal data of the customer if the order is not completed If an **order** is not completed, the **booking system** SHOULD delete all personal information of the **customer** provided to it as part of the **order** creation process.
# Future versions of this API Some of the patterns within version 1.0, for example the PATCHing of an order resource to provide a payment and using PATCH to change the status of an `orderItem` to cancel an order provide the groundwork for known requirements such as multiple order items (shopping carts), cancellation of individual order items and also multipart payments. Both `orderItems` and `payments` are modelled as collections to support extension of the version 1.0 specification towards multipart orders and payments without breaking changes in the Order model for subsequent versions. The new models for Payment and for Error enhance the Open Active model specification to make it more transactional when used as part of the Booking API. The currently known requirements not satisfied in version 1.0 are: - multiple part orders - supporting multiple payments - customer creation - managing of reservations - support for member lookups We have not yet decided what the priorities are, or whether all of these requirements are in scope for version 1.0 and would encourage people to shape future iterations of the specification by becoming involved in the communities associated with Open Active and the open specification. In a future version, it is imagined that an API method describing the service, and including such things as the links to Privacy Policy, Terms and Conditions and the target for a ReserveAction could be used to reduce data duplication. At the moment there is no provision for a **customer** to cancel an **order** after the cancellation window, yet they may wish to do this out of politeness, even though no refund is possible, with the benefit that their place may be taken by another participant.
# New Open Active models defined in the 1.0 Booking API specification These models are not yet reflected in the current version (2.0) of the Open Active Modelling Specification. ## Error
Property Status Notes
errorType REQUIRED A URI providing an enumeration value for the type of error.
title REQUIRED A human readable reason for the error.
detail RECOMMENDED A human readable description of the error.
instance REQUIRED The requested URL.
method RECOMMENDED The method of the request (e.g. GET).
status OPTIONAL An integer representing the HTTP status code.
invalid-params OPTIONAL An array of invalid parameters, if appropriate.
requestId OPTIONAL Used by technical support for diagnostics purposes.
## Payment
Property Status Notes
paymentMethod REQUIRED A URI providing an enumeration value for the type of payment method used.
paymentMethodId REQUIRED A string identifier for the method of payment used (e.g. the last 4 digits of the credit card).
totalPaidToProvider REQUIRED A schema:MonetaryAmount object deining the total amount paid.
confirmationNumber OPTIONAL A string identifier to confirm that a payment has been recorded by the booking system.
paymentDate OPTIONAL The date and time that payment has been recorded by the booking system, expressed as an ISO 8601 DateTime.
# Acknowledgements The editors thank [all members](https://www.w3.org/community/openactive/participants) of the OpenActive Community Group for their contributions.