Open Booking API 1.0 CR

Draft Community Group Report

Latest editor's draft:
https://www.openactive.io/open-booking-api/EditorsDraft/
Editors:
Nick Evans (Open Data Institute)
Leigh Dodds (Open Data Institute)
Former editor:
Chris Thorpe (Open Data Institute)
Participate:
GitHub openactive/open-booking-api
File a bug
Commit history
Pull requests
Version:
1.0 CR

Abstract

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.

Status of This Document

This specification was published by the OpenActive Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

Contributions to this document are not only welcomed but are actively solicited and should be made via GitHub Issues and pull requests. The source code is available on GitHub.

Note

This document represents a published Candidate Release, including minor non-breaking clarifications that build on the version released at https://www.openactive.io/open-booking-api. This Candidate Release is published for the purposes of creating accompanying tooling and gathering implementation experience, and will be updated with further clarifications and minor technical improvements based on implementation feedback. We encourage developers to implement this API and share their experience.

If you wish to make comments regarding this document, please send them to [email protected] (subscribe, archives).

1. Introduction

This section is non-normative.

The document is an output of the OpenActive W3C Community Group. As part of the OpenActive initiative, the community group is developing standards that will promote the publication and use of open opportunity 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 get more active.

An Opportunity (e.g. "Yoga Tuesday 14th March at 7pm") in the context of this specification is a specific instance of an Event (or subclass of Event) that a Customer can attend to participate in physical activity: a ScheduledSession within a SessionSeries, a Slot within a FacilityUse, a HeadlineEvent or a CourseInstance (or an Event within a HeadlineEvent or CourseInstance). Each Opportunity has associated Offers (e.g. "Junior (8-18) £9"), which may be free, and describe the commercial means of accessing the Opportunity.

Opportunity Data in the context of this specification is openly published data pertaining to Opportunities, accessible via [RPDE] feed(s).

This specification builds on the previous work of the Community Group by defining an HTTP API that can be implemented by Booking Systems that are publishing Opportunity Data. By allowing third-party applications ("Brokers") 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 Broker (client). This includes definitions of the overall application flow, detailed request and response formats and the potential error conditions that applications may encounter. A high-level summary of these and their relationship to each other is provided in the Booking Flow overview, below.

1.1 Scope and requirements

The focus of the initial versions of this specification will be on the simplest use cases that support guest checkout bookings via a Broker.

The core functionality includes:

Additional optional functionality includes:

1.1.1 Out of Scope in this version

A number of additional requirements that relate to the booking of events and facilities are currently out of scope:

  • Creating and managing accounts for Customers
  • More complex pricing options, e.g. membership-based pricing
  • Waiting lists for events
  • Collecting marketing preferences from the Customer
  • Mandatory business-to-business tax calculation
  • Cancellation fees
  • "Subscriptions" to allow a Customer to participate ad-hoc in a Seller's activity, with such participation recorded for later reconciliation
  • Refunds/cancellations after the Opportunity has occurred
  • Back-office systems between Broker and Booking System, such as for payment reconciliation
  • Fine-grained control of Opportunity bookability in the Booking System
  • The ability to restrict specific API functionality for a particular Broker

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, please raise feature requests via GitHub.

1.1.2 Out of Scope by design

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 W3C Community Group.

The functionality that is currently 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 coordinated by the Broker, in a separate payment flow. Brokers are able to use the payment and reconciliation mechanisms that provide the best options for their Sellers and Customers.
  • API business models – the design is agnostic to any business models that might govern the use or provision of the API, e.g. revenue-sharing or transaction processing fees.
  • Opportunity 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, e.g. via an [RPDE] feed containing [Modelling-Opportunity-Data] data.
  • API endpoint discovery – the means for a Broker to discover the API endpoints included in this specification are included in the [Dataset-API-Discovery] specification.

1.2 Specification dependencies

Version 1.0 of the Open Booking specification depends on the following:

Note that the dependency on the [Modelling-Opportunity-Data] specification is only constrained to the major version release, and that the flexibility provided by supporting minor versions (which do not include breaking changes) enables implementors to take advantage of additional features as the specification evolves.

1.3 Audience

The document is primarily intended for the following audiences:

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.

2. Conformance

As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.

The key words MAY, MUST, MUST NOT, NOT RECOMMENDED, NOT REQUIRED, OPTIONAL, RECOMMENDED, REQUIRED, SHOULD, and SHOULD NOT in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

This specification makes use of the compact IRI Syntax; please refer to the Compact IRIs section from [JSON-LD].

3. 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 underlined and in black.
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.
Note

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.

4. Key Actors

Of all the organisations and systems involved in a transaction, the API defined by this specification is scoped tightly to only specify interactions between the Broker and Booking System, with high level prompts for interactions with Customers, Sellers and Payment Providers.

Actors in scope
Figure 1 Illustration of actors and systems involved in a transaction, and the focus of this specification being between Broker and Booking System.
Customer
The user who places a booking using an application provided by a Broker. The Customer is not necessarily the attendee, e.g. a parent may book on behalf of their child.
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
Booking System
The organisation or developer providing an application that maintains bookable inventory on behalf of the Seller. The platform or service that provides a server-side implementation of this API.
Seller
The organisation providing access to events or facilities via a Booking System e.g. a leisure provider running yoga classes.
Payment Provider
The notional service providing payment processing between the Customer, Broker and Seller. Although this specification refers to the Payment Provider as a single notional service, it may in reality be composed of multiple connected services providing the same functionality to allow for the widest variety of business models.

5. Booking Flows

This specification includes two main booking flows: a Simple Booking Flow, and a Booking Flow with Approval that builds on the Simple Booking Flow. The two flows are largely identical, with the addition of a (possibly iterative) approval step in the Booking Flow with Approval.

5.1 Booking flow overview

Described in the broadest possible terms, the Open Booking API defined by this specification supplies the technical means whereby a Broker can book a Customer into an Opportunity for a physical activity, by means of an Offer provided by a Seller. Typically, although not always, a Payment will have to be made for this Order.

While this process is intuitively clear, some of its implications may be less so, and a number of common use-cases introduce a degree of additional complexity into the flow. For instance, a Customer will typically want to have an idea of how much a given Offer will cost before committing to attending an Opportunity. This means that before an Order can be created, an OrderQuote (a subclass of Order) needs to be provided. In addition, many Sellers will need to review their Orders before deciding to approve them because, for instance, they need to allocate shared resources such as caretakers to a number of Customers. This is the motivation for the Booking Flow with Approval sequence, and introduces to the flow the concept of OrderProposals - another subclass of Order, which allows (re)modification by both parties and that requires explicit consent from both Customer and Seller before a new Order is created.

The process of creating an Order online takes time, and for this reason some Sellers and Brokers may optionally offer their users a Lease - a provisional reservation of an Opportunity that prevents it from being sold while the Customer is in the process of booking it.

Typically Sellers keep track of bookings by means of an automated Booking System. This raises the problem of ensuring consistency in the state of information between the Booking System and the Broker. This difficulty is bridged by the concept of Order feeds, which publish all changes made to an Order by the Booking System after its initial successful response to the client's request to create it, and which is intended for consumption only by the Broker.

The process of creating an Order will normally involve the processing of a Payment. While Payments are normally fixed, some approaches to pricing also allow for the possibility of a DynamicPayment, whereby the price may fluctuate based on external conditions. In addition, differences in approaches to taxation - whether because of regional variation, or because of the legal and commercial status of the Broker - result in a need for some nuances in the modelling of TaxChargeSpecifications.

Additionally, transactions between Customers, Brokers, and Sellers may be subject to various legal and regulatory Terms, such as a PrivacyPolicy and TermsOfUse.

5.2 Booking flow modularity

This specification is designed to provide a minimal core Simple Booking Flow, which should be straightforward to implement.

This specification also provides optional features, which allows the minimal core implementation to be augmented depending on the Booking System's requirements:

5.3 Booking pre-conditions

In order to enter either of the booking flows described in this specification, the following pre-conditions must be met:

5.4 Simple Booking Flow

5.4.1 Customer Journey

The booking journey of a Customer is generalised into the following logical steps:

High level customer journey
Figure 2 Illustration of a typical Customer journey.
  1. Select: Customer selects at least one Opportunity (e.g. ScheduledSession or Slot) and an associated Offer ("Junior (8-18) £9")
  2. Identify: Customer submits personal details and other requested details
  3. Book and Pay: Customer submits payment details

Note that some or all steps may be skipped, or may not be presented to the Customer, e.g. if their registration or payment details are already known, or they are using a voice interface. These steps are not indicative of the user experience, and simply provide a common language around the flow.

5.4.2 High-level API flow

High level booking flow
Figure 3 Illustration of high level booking flow.
  • The Broker generates a UUID which is used to uniquely identify the Order throughout the process.

  • After "1. Select" the Broker calls the Booking System to confirm an indicative price and the availability of the selected items using an OrderQuote, and retrieve a total order price. Any details that the Customer needs to supply to complete the booking are also specified. This is checkpoint C1 on the diagram above.

  • After "2. Identify" the Broker calls the Booking System to reconfirm the price and availability of the selected items using an OrderQuote, using all personal details required, and retrieve a total order price. Explicit consent for any terms and conditions is also captured at this stage if required. This is checkpoint C2 on the diagram above.

  • After "3. Book and Pay", the Broker first pre-authorizes the payment for the total order price (if required by the Payment Provider), then confirms the booking with the Booking System using an Order (B on the diagram above), and finally captures the payment. The Broker then generates invoices, and sends the Customer notifications.

  • The Booking System adds the Order to an Orders feed specific to the Broker, which the Broker reads to update its internal state, including those updates resulting from cancellations.

5.4.3 Leasing

Customers may simultaneously attempt to book the same Opportunity (e.g. space in an event or Slot of a facility), and hence contend with one another for a limited number of Opportunities. In order to ensure that items cannot be "stolen" from a Customer's "shopping basket" by another Customer, some Booking Systems use the concept of a Lease, a temporary reservation that provides a window during which a Customer can complete their customer journey and improves the user experience overall.

The design of this API supports the use of time-bound Leases. Using Leases is RECOMMENDED, however they are not required, in order to ensure a low barrier of entry for implementors of the specification.

Individually at C1 and C2, the Booking System can opt to create (or extend) a Lease for the Broker. Whether they opt to do so may depend on the Booking System's capacity to support Leases, or on the Seller's configuration of the Booking System.

At C1 an Anonymous Lease MAY be created i.e. a Lease for a Customer that has not yet provided any personal details.

At C2 a Named Lease MAY be created i.e. a Lease for a Customer who has identified themselves via personal details and other additional details. Any existing Anonymous Lease becomes a Named Lease when the customer's personal details are provided (even if the Booking System does not store them at this stage).

If a Lease is created at C1 and/or C2 it is used during B. However, if no Lease is created, or if it had expires prior to the transaction being completed, the booking completes anyway provided sufficient inventory is available.

It is RECOMMENDED that if the Booking System supports leasing, it also guarantees the price of each leased item for the duration of the Lease, so that the Customer is guaranteed to pay the price they have seen during C2.

The specification is designed to allow Brokers to be agnostic to Leases: should a Booking System be upgraded to support these, it can do so without affecting the Broker's implementation.

By design, then, the use of Leases is largely transparent to the Broker. However, the Booking System MUST provide information on the Lease to help improve the user experience of the customer journey as follows: when a Lease is created, a Lease object MUST be returned in the OrderQuote.

Example 2: Example of lease
"lease": {
  "@type": "Lease",
  "leaseExpires": "2018-10-01T11:00:00Z"
}

The leaseExpires MAY be used to inform the Customer of the time they have remaining.

5.4.4 High-level type flow

High level type flow
Figure 4 Illustration of types used during the API flow.

The OrderQuote is an ephemeral draft of the Order while it is being constructed by the Customer.

  • C1 - OrderQuote without enough personal details or additional details to be accepted as an Order.
  • C2 - OrderQuote with complete and validated detail, enough to be created successfully as an Order.
  • B - Order exists in an instantly successful state in the Booking System, though it transitions through orderCreationStatus states in the Broker.

5.4.5 Order statuses

To simplify the implementation for the Booking System, the creation of an Order is considered atomic to the Booking System (it succeeds or fails at once in B), and by contrast is considered to consist of steps to the Broker.

5.4.5.1 Order statuses in the Booking System

In this version of the specification the Order within the Booking System MUST NOT have a status, it exists atomically in a successfully created state. Although OrderItems may be cancelled by either the Customer or the Seller, the Order itself within the Booking System is simply a container. The orderStatus property of the Order MUST NOT be used and is reserved for future evolution of the specification.

The orderCreationStatus is maintained by the Broker only during this flow, and MUST NOT be sent to the Booking System.

5.4.5.2 Order statuses in the Broker

Throughout "Book and Pay" section of this flow the Broker maintains the orderCreationStatus property in its copy of the Order, which is not sent to the Booking System. This allows the Broker to consistently rollback in the case of failure.

The orderCreationStatus statuses are defined as follows:

  1. https://openactive.io/OrderCreationPaymentAuthorized - after successful Payment Authorisation
  2. https://openactive.io/OrderCreationPaymentDue - after successful B
  3. https://openactive.io/OrderCreationPaymentCaptured - after successful Payment Capture
  4. https://openactive.io/OrderCreationComplete - after successful Invoice Generation and Customer Notification

An Order MUST NOT be communicated externally by the Broker and other operations such as cancellation MUST NOT be possible until it has reached the orderCreationStatus of https://openactive.io/OrderCreationComplete, after which the orderCreationStatus can be ignored.

5.4.6 Step-by-step process description

Sequence diagram
Figure 5 Sequence diagram showing API interactions

i) "1. Select" - Customer browses Broker site anonymously and adds something to basket

  • Broker MUST have determined at least one Open Booking bookable Opportunity each with an associated applicable Offer from the Customer's browsing, using the most up-to-date Opportunity Data.
  • Broker generates a UUID to use for the Order.
  • If any catastrophic failure occurs during the booking flow (any error with status code 500, or when the request consistently returns status code 503 after a number of retries) the UUID MUST be regenerated to allow for a clean retry of the booking. UUID MUST NOT be regenerated for expected errors (status code 4xx).

ii) C1 - Broker call with OrderQuote including UUID without a customer object to check availability and that the combination of items requested can be purchased, Booking System responds with an OrderQuote including any errors against each OrderItem.

  • Booking System creates Anonymous Lease for that UUID if it is supported, otherwise it provides a simple availability and totalPaymentDue confirmation. An OrderQuote response with HTTP Status 200 will be returned if the all requested items are available. If a Lease has been created, then the OrderQuote in the response MUST include a lease property containing a valid Lease object.
  • OrderQuote MUST include totalPaymentDue, which allows the Broker to update any UI with the total.
  • OrderQuote MUST include a list of OrderItems which contain their respective Opportunity objects, including enough detail to allow the Broker to communicate key information about the Opportunity to the Customer (rather than relying on the Broker having the most up-to-date Opportunity data from the open feeds at the point of purchase).
  • The Booking System MUST ensure that the data returned to the Broker in the remainingAttendeeCapacity and remainingUses properties are up-to-date, including places already booked and those currently reserved by any Leases from competing OrderQuotes.
  • OrderQuote MUST specify any additional details required to complete the booking for the selected OrderItems via an orderItemIntakeForm.
  • If there are any issues with the OrderItems requested in the OrderQuote, the Booking System MUST respond with a 409 Conflict response, with error details provided against each offending OrderItem.
  • The Booking System MUST reflect back all properties defined by this specification that are supplied by the Broker's request in its response, including broker and brokerRole, to acknowledge they have been recognised (even if they have not actually been stored by the Booking System). The Booking System MUST NOT reflect back extension properties outside of this specification unless it explicitly supports them.
  • Where totalPaymentDue is not 0, and the prepayment value of any Offer is https://openactive.io/Required or unspecified, the payment details supplied to C2 (e.g. accountId) MUST be supplied at C1, otherwise an IncompletePaymentDetailsError MUST be returned. This ensures that a viable payment reconciliation route is available before the Customer starts to enter their data. If the payment details supplied (e.g. accountId) are not considered valid for reconciliation by the Booking System then a InvalidPaymentDetailsError MUST be returned.
  • OrderItems have no orderItemStatus.

iii) "2. Identify" - Customer submits personal details and any other additional details requested

  • The Customer MUST have been informed that their personal information is being submitted to the Seller.
  • The Customer MUST have been made aware of any relevant termsOfService, for example by displaying a link to them prominently, before submitting their personal details.
  • If requiresExplicitConsent is true for any termsOfService then the Customer MUST only be allowed to proceed if they have explicitly acted to assent to such terms.
  • attendee and orderItemIntakeFormResponse data is be captured at this stage if specified via attendeeDetailsRequired and orderItemIntakeForm in the C1 response, respectively.
  • If prepayment of all selected Offers is https://openactive.io/Optional where they are not https://openactive.io/Unavailable then the Customer MAY be given the option to prepay or simply reserve a space without payment. Providing optionality of payment to the Customer is at the Broker's discretion.

iv) C2 - Broker call with OrderQuote including UUID with a customer object and any orderItemIntakeFormResponse required, Booking System responds with an OrderQuote including any errors against each OrderItem.

  • Booking System creates Named Lease for that UUID if it is supported, otherwise it provides a simple availability and totalPaymentDue confirmation. Works exactly as C1, except validates payment details, customer details, any attendee details, and the orderItemIntakeFormResponse.
  • Broker presents the contents of the OrderQuote to the Customer to ensure they have the most up-to-date price and availability before proceeding with the purchase.
  • If there are any issues with the OrderItems requested in the OrderQuote, the Booking System MUST respond with a 409 Conflict response, with error details provided against each offending OrderItem - including if an OrderItem is no longer available, cannot be purchased in combination with another OrderItem, or does not contain a sufficient orderItemIntakeFormResponse.
  • Any errors must be resolved by the Customer or Broker before proceeding, by amending the OrderItems included in the OrderQuote and resubmitting until an OrderQuote is returned without errors.
  • In the case that payment is selected (or if the prepayment value of any selected Offer is https://openactive.io/Required or unspecified), the payment property MUST be included in the OrderQuote request and response including all details except the identifier, to express the intent to pay. If the payment property is omitted where totalPaymentDue is not 0, and the prepayment value of any Offer is https://openactive.io/Required or unspecified, then a IncompletePaymentDetailsError MUST be returned. If the payment details supplied (e.g. accountId) are not considered valid for reconciliation by the Booking System then a InvalidPaymentDetailsError MUST be returned.
  • OrderItems have no orderItemStatus.

v) "3. Book and Pay" - Customer submits payment details and clicks Book

  • The Customer MUST only be allowed to proceed if no OrderItems in the OrderQuote contain any errors.

  • If totalPaymentDue is 0 because all OrderItems are free of charge, prepayment of all selected Offers is either https://openactive.io/Unavailable or https://openactive.io/Optional, or the Customer has chosen not to pay, payment details MUST NOT be captured.

vi) Payment Authorisation - Broker pre-authorises totalPaymentDue from Payment Provider

  • The Order id, which includes the UUID, and will be generated by the Broker to make the PUT call at B (the Order id is also the PUT endpoint URL), SHOULD be used as a reference in the Payment Provider, if possible.

  • If payment details were not captured due to the prepayment value of the selected Offers or totalPaymentDue equalling 0, pre-authorisation MUST NOT be taken.

  • The Broker MUST store the Order request with an orderCreationStatus of https://openactive.io/OrderCreationPaymentAuthorized at this point, even if pre-authorisation is not taken, in order to ensure that the UUID in the Orders feed is recognised without a race condition. Such a condition might arise in the (admittedly unlikely) event of, e.g., an Order that is created at B and then immediately cancelled by the Booking System, which could result in the Broker receiving an unrecognised Order in the Orders feed if it was retrieved before the call to B returned.

vii) B - Broker call with an Order including UUID with a customer object and any orderItemIntakeFormResponse required, and totalPaymentDue including the total amount pre-authed; the Booking System responds with an Order.

  • If payment details were not captured due to the prepayment value of the selected Offers, or if totalPaymentDue is 0 due to all OrderItems being free of charge, the payment property MUST NOT be included in the Order request or response. If the payment property is omitted where totalPaymentDue is not 0, and the prepayment value of any Offer is https://openactive.io/Required or unspecified, then an IncompletePaymentDetailsError MUST be returned. If the payment details supplied (e.g. accountId) are not considered valid for reconciliation by the Booking System then an InvalidPaymentDetailsError MUST be returned.

  • The identifier of the Payment is taken from the payment pre-authorization, which must be able to be matched against any payment reports produced by the Payment Provider for the purposes of audit and/or reconciliation.

  • If the totalPaymentDue included in the Order object is different to the current totalPaymentDue (which might have changed since C2, e.g. if the headline price is changed in the Booking System between these two steps), B returns an TotalPaymentDueMismatchError with associated status code, but the Lease is sustained so that the Customer can be prompted whether they want to continue with the new amount.

  • If the Lease has expired, or if Leases are not supported, then the Booking System attempts to make the booking anyway. If there are insufficient spaces available for OrderItems the B returns an OpportunityHasInsufficientCapacityError with associated status code.

  • If the booking succeeds only for some OrderItems, the whole transaction to create the Order within the Booking System is rolled back and B returns an OrderCreationFailedError with associated status code.

  • If there's a network failure, e.g. no response from server, then the Broker can resubmit with same UUID. As the call is idempotent, the Booking System will need to track the UUID supplied by Broker against any successfully created Orders, to ensure that it can return the same Order for subsequent requests. The Broker MUST NOT reuse UUIDs across multiple Orders.

  • If the call is successful (returns a 200 response), the booking is considered as complete and paid by the Booking System at this point.

  • The Booking System MUST reflect back all properties defined by this specification that are supplied by the Broker's request in the Order returned from this call, including broker and brokerRole, to acknowledge they have been recognised (even if they have not actually been stored by the Booking System). The Booking System MUST NOT reflect back extension properties outside of this specification unless it explicitly supports them.

  • The Order returned from this call MUST be stored by the Broker, to be subsequently updated by the Orders feed. The Orders feed contains only partial-updates of the Order stored by the Broker, and the Broker is the only actor guaranteed to maintain a complete view of the Order. The Booking System MAY store the full Order if desired.

  • It is the responsibility of the Broker to store any additional state it requires that is not included in the Order returned from this call, and RECOMMENDED that such state is stored alongside the Broker's persisted Order.

  • The Broker MUST store the Order response in with an orderCreationStatus of https://openactive.io/OrderCreationPaymentDue at this point (or https://openactive.io/OrderCreationPaymentCaptured if no payment is due).

  • This call is idempotent, and hence if the UUID used for an Order already represents a completed Order with the same set of OrderItems for the same customer as those specified, the success response is returned as normal. If any differences exist in the Orders, a UUID clash is assumed, a 500 response of with OrderAlreadyExistsError is returned by the Booking System, and a new UUID must be generated by the Broker to retry the call.

  • As this call is idempotent, it can be safely retried in the case of an temporary unexpected error (any error that returns status code 503) or rate limiting (any error that returns status code 429).

  • When the request still returns status code 503 after a number of retries the UUID MUST be regenerated to allow for a clean retry of the booking. The UUID MUST NOT be regenerated for expected errors (status code 4xx).

  • OrderItems returned by the Booking System have an orderItemStatus of https://openactive.io/OrderConfirmed.

viii) Payment Capture - Broker captures totalPaymentDue from Payment Provider

  • It is the Broker's responsibility to ensure that payment has been captured if required.

  • If payment is required, the Broker MUST update its stored Order with an orderCreationStatus of https://openactive.io/OrderCreationPaymentCaptured after payment is successfully captured.

  • If payment details were not captured due to the prepayment value of the selected Offers or totalPaymentDue equalling 0, payment MUST NOT be captured.

ix) Invoice Generation and Customer Notification - Upon successful Payment Capture (or completing B in the case of payment capture not being required), the Broker generates invoices, sends the Customer notifications, and stores the Order in a success state based on the response from B.

  • The Broker MUST update its stored Order in with an orderCreationStatus of https://openactive.io/OrderDelivered after invoices have been created and notifications successfully sent.

x) Refunds and Cancellation - The Broker subscribes to updates from the Booking System to process cancellations and refunds.

  • A secure Orders feed of Orders MUST be provided by the Booking System, with the contents of the feed specific to the authentication credentials. This allows the Broker to maintain an updated state of all their bookings across a number of Booking Systems, even when changes are made outside of the Broker. This also allows the Broker to handle refunds for cancellations, and process notifications to Customers in a consistent way.
  • The Booking System must ensure that the Orders in the Orders feed represent the current state of OrderItems within the system, for the properties included in the feed. The Broker MUST use these Orders to process refunds and send update notifications to the Customer.

5.4.7 Booking Flow Rollback

To ensure catastrophic errors are detected and resolved the Broker MUST periodically check its own store for any Orders that have not been updated in over a 2 minutes, and have an orderCreationStatus other than https://openactive.io/OrderCreationComplete.

Note that due to the ordering of the Booking Flow, no notifications will have been sent to the Customer, so no notifications regarding the rollback are required assuming it is performed promptly.

5.4.7.1 Booking halted at OrderCreationPaymentCaptured

If the orderCreationStatus is https://openactive.io/OrderCreationPaymentCaptured, Invoice Generation and Customer Notification should simply be retried, and the orderCreationStatus set to https://openactive.io/OrderCreationComplete on success with no further rollback steps taken.

5.4.7.2 Booking halted at OrderCreationPaymentDue

If an error occurs during Payment Capture, which is very unlikely but possible due to system failure, and detectable where the orderCreationStatus is https://openactive.io/OrderCreationPaymentDue, the Broker MUST either:

  • Initiate manual capture via e.g. the Payment Provider's admin console. Note the pre-auth will typically last for 7 days, so manual capture is still possible after the event.
  • Capture the payment from the Customer manually.
  • Absorb the loss and make a new payment for the same amount with the same UUID in the Payment Provider (better for UX / customer service, if manual capture not possible).
  • Acknowledge that payment cannot be successfully captured.

After payment has been successfully captured via one of the above methods, the orderCreationStatus MUST be set to https://openactive.io/OrderCreationPaymentCaptured, which will cause Invoice Generation and Customer Notification to be retried as per the previous section.

If the payment cannot be captured successfully:

  • The Broker MUST issue a Order Deletion (DELETE) request for the Order to remove it from the Booking System.
  • The orderCreationStatus MUST be set to https://openactive.io/OrderCreationPaymentAuthorized, to trigger the rollback step in the next section.
5.4.7.3 Booking halted at OrderCreationPaymentAuthorized

If the orderCreationStatus is https://openactive.io/OrderCreationPaymentAuthorized, the Broker MUST:

  • Cancel the associated Payment Authorisation from the Payment Provider.
  • Soft delete the contents of the Order from the Broker (deleting any personal data, while retaining data required for audit), retaining the UUID.

Note that the UUID is retained so that any related entries in the Orders Feed can be appropriately assigned and ignored.

5.4.8 Contracts

A summary of the key conformance criteria for the Broker and Booking System is included here.

5.4.8.1 Broker contract with Booking System
  • The Broker MUST call C1 (without Customer details)
  • The Broker MUST call C2 (with Customer details), to retrieve the total order, providing all required details and resulting in no errors before calling B.
  • The Broker MUST call B (with payment identifier if relevant), and MUST store the resulting Order.
  • If B fails, the Broker MUST cancel the payment authorisation without any consequence to the Customer or Seller.
  • The Broker MUST process all updates to the Order from the Orders feed, specifically monitoring the Orders Feed for cancellations.
  • The Broker MUST generate a UUID, and use it consistently during the booking flow.
  • The Broker MUST NOT reuse UUIDs across complete or incomplete Orders.
  • The Broker MUST make the Customer aware of any relevant termsOfService, for example by displaying a link to them prominently, before they commit to the booking.
  • If requiresExplicitConsent is true for any termsOfService then the Customer MUST only be allowed to proceed if they have explicitly acted to assent to such terms.
  • The Broker MUST handle payments and reconciliation with the Seller and the Payment Provider.
5.4.8.2 Booking System contract with the Broker
  • The Booking System MUST respond to C1 (without Customer details) with an OrderQuote that includes an indicative total order price, or a relevant error. It must also include the specification of any additional details to captured from the Customer to be provided to C2.
  • The Booking System MUST respond to C2 (with Customer details) with an OrderQuote that includes an exact total order price, or a relevant error. It must also produce a relevant error if the additional details specified are not sufficient.
  • The Booking System MUST respond to B with a completed Order, or a relevant error. The whole booking MUST succeed or fails as one.
  • Upon any cancellations or updates to the Order or any OrderItem within it, the Booking System MUST include the updated Order in the Orders feed.
  • The Booking System MUST store the UUID as part of the Order (and OrderQuote if leasing is enabled).
  • The Booking System MUST refuse attempts to create new orders with a previously used UUID.
  • The Booking System MUST check availability and confirm pricing during C1, C2, and B to ensure booking can complete.
  • The Booking System MUST provide an authenticated Orders Feed specific to the Broker. The contents of this feed will consist of all Orders updated by the Booking System.
  • The Booking System MUST provide [RPDE] open feeds of Opportunity Data, including pricing and availability.

Calls to C1, C2, and B MUST be idempotent, based on the UUID.

With the exception of leasing, C1 and C2 MUST NOT have any side effects.

5.5 Booking Flow with Approval

The Booking System MAY optionally support review and approval of bookings by the Seller. As the approval processes is initiated by the Booking System, implementation of this section by the Booking System is only required if this flow is supported. Otherwise this section can be ignored.

5.5.1 Customer Journey

The booking journey of a Customer is generalised into the following logical steps:

High level customer journey with approval
Figure 6 Illustration of a typical Customer journey, with an approval step.

The steps exactly mirror the Simple Booking Flow, with two additions in the 'Identify' step:

  1. Select: Customer selects at least one Opportunity (e.g. ScheduledSession or Slot) and an associated Offer ("Junior (8-18) £9")

  2. Identify: Customer submits personal details and other requested details

    1. Propose: Customer submits payment details, and submits a proposed booking

    2. Approve: Seller approves the booking, after a period of time

  1. Book and Pay: Customer confirms they are happy to proceed

Note that some or all steps may be skipped, or may not be presented to the Customer, e.g. if their registration or payment details are already known, or they are using a voice interface. These steps are not indicative of the user experience, and simply provide a common language around the flow.

5.5.2 High-level API flow

High level booking flow with approval
Figure 7 Illustration of high level booking flow, including approval.

The flow exactly mirrors the Simple Booking Flow, with two additions:

  • The Broker generates a UUID which is used to uniquely identify the Order throughout the process.

  • After "1. Select" the Broker calls the Booking System to confirm an indicative price and availability of the selected items using an OrderQuote, and retrieve a total order price. Any details that the Customer needs to supply to complete the booking are also specified. This is checkpoint C1 on the diagram above.

  • After "2. Identify" the Broker calls the Booking System to reconfirm the price and availability of the selected items using an OrderQuote, using all details required, and retrieve a total order price. Explicit consent for any terms and conditions is also captured at this stage if required. This is checkpoint C2 on the diagram above.

  • After "2.1. Propose", the Broker first pre-authorizes the payment for the total order price (if required by the Payment Provider), then submits the proposed booking to the Booking System using an OrderProposal (P on the diagram above), and sends the Customer a notification that the proposal is under consideration. The OrderProposal may be negotiated between the Customer and Seller via the Broker at this stage.

  • After "2.2. Approve" the Seller approves the OrderProposal and the Broker is notified of this by the Booking System (A on the diagram above). The Broker can seek Customer permission to proceed, or simply complete the booking automatically, depending on the desired user experience.

  • After "3. Book and Pay", if necessary the Broker re-authorizes the payment for the total order price (if it has changed, and if required by the Payment Provider), then confirms the booking with Booking System using an Order (B on the diagram above), and finally captures the payment. The Broker then generates invoices, and sends Customer notifications.

  • The Booking System adds the Order to an Orders feed specific to the Broker, which the Broker reads to update its internal state, including those updates resulting from cancellations.

Note that the OrderProposal at stages 2.1 and 2.2 does not constitute a booking. The whole flow MUST be completed before the Customer is considered "booked" onto an Opportunity.

5.5.3 Proposal Leasing

Leasing works as specified in the Simple Booking Flow, and if a Lease was acquired at C1 or C2 it SHOULD be carried over to the OrderProposal and the Lease extended for a duration sufficient to cover the lifetime of the OrderProposal.

A Lease is NOT REQUIRED for this flow, as in some cases Sellers may prefer to manage contention for spaces manually via the approval process instead of leasing.

If an OrderProposal is in an orderProposalStatus other than https://openactive.io/CustomerRejected or https://openactive.io/SellerRejected, regardless of whether or not the Lease has expired, it MUST be possible for the Customer to still complete the booking if they attain approval. If at any point this is not the case, it is the responsibility of the Booking System to set the orderProposalStatus to https://openactive.io/SellerRejected, or if openBookingFlowRequirement of any Offer referenced in the OrderProposal includes https://openactive.io/OpenBookingNegotiation, to update the OrderProposal to remove or replace the offending OrderItem(s) with an appropriate orderSellerNote. For example, if an Opportunity becomes full or is cancelled and no substitute Opportunities are available, then all remaining outstanding OrderProposals related to that Opportunity would need to be automatically rejected or updated.

If the Lease expires the Booking System could, for example, automatically reject the OrderProposal on behalf of the Seller, automatically renew the Lease, or allow the Seller to manually deal with the contention for spaces as part of the approval process and hence do nothing.

5.5.4 High-level type flow

High level type flow with approval
Figure 8 Illustration of types used during the API flow, with approval.

As specified in the Simple Booking Flow, with one addition:

  • P/A - OrderProposal exists in a number of states, as described in the next section, and contains enough detail to successfully create an Order.

5.5.5 OrderProposal statuses

OrderProposal states
Figure 9 State transition diagram showing OrderProposal states

OrderProposal has an orderProposalStatus which is initially set by the Booking System to https://openactive.io/AwaitingSellerConfirmation.

The Broker MUST only proceed to B when orderProposalStatus is set to https://openactive.io/SellerAccepted in the Orders feed.

An OrderProposal MUST be approved or rejected atomically; partial approval is not supported in this version of the specification (though Proposal Amendment is supported).

To reach the criteria to proceed to B, or rejection, OrderProposal MUST be transitioned through states as follows:

  • The Booking System MUST transition the OrderProposal to a new orderProposalStatus by updating the Orders feed.
  • The Broker MUST transition the OrderProposal to a new orderProposalStatus by submitting a PATCH to the OrderProposal using the OrderProposal Update endpoint.
5.5.5.1 Minimal Proposal Implementation

In the Minimal Proposal Implementation the OrderProposal can either be accepted or rejected by the Seller, or aborted by the Customer.

If an Offer requires this Minimal Proposal Implementation, the Booking System MUST include the openBookingFlowRequirement of https://openactive.io/OpenBookingApproval in the Offers within its open data. This allows Brokers that do not support approval to easily filter out such offers from their experience.

Note that when Offers that require approval are mixed with Offers that do not require approval in a single OrderQuote, the whole OrderQuote MUST always require approval. It is at the discretion of the Broker whether to issue two separate OrderQuotes (and hence complete two separate booking flows) that batch those OrderItems that require approval separately to those that do not, or whether to instead combine them together into a single OrderQuote and hence complete a single Booking Flow with Approval.

5.5.5.1.1 Proposal Approval
  • If Seller approval is given, the Booking System MUST set orderProposalStatus to https://openactive.io/SellerAccepted in the Orders feed, which will give the Broker permission to proceed to B.
  • For the Minimal Proposal Implementation, the Broker must check that the orderProposalVersion received in the Orders Feed matches the original orderProposalVersion returned at P. If this is the case the Broker may seek Customer confirmation to proceed, or may simply proceed with B automatically, depending on the desired user experience. If this is not the case, the scenario MUST be handled via the Proposal Amendment process.
5.5.5.1.2 Proposal Rejection
  • If the Seller rejects the proposal, the Booking System MUST set orderProposalStatus to https://openactive.io/SellerRejected in the Orders feed, optionally including an orderSellerNote containing any reason for rejection.
  • If the Customer aborts the proposal, the Broker MUST PATCH the OrderProposal to set the orderProposalStatus to https://openactive.io/CustomerRejected, optionally including an orderCustomerNote containing any associated reason. The Booking System will then update this in the Orders feed,

Following any type of rejection, the Broker MUST immediately withdraw any payment authorisation.

5.5.5.2 Proposal Amendment

The Booking System MAY optionally support Proposal Amendment. If this is not supported by the Booking System, the simple Approval/Rejection workflow described in the Minimal Proposal Implementation may be used without needing to consider this (as the amendments are initiated by the Booking System).

If an Offer may require Proposal Amendment, the Booking System MUST include the openBookingFlowRequirement of https://openactive.io/OpenBookingNegotiation in the Offers within its open data. This allows Brokers that do not support such negotiation to easily filter out such offers from their experience.

Proposal Amendment allows the Seller to suggest amendments to the OrderProposal (which need to be accepted by the Customer) as follows:

  • If the orderProposalVersion in the Orders feed has changed compared with the orderProposalVersion in the OrderProposal stored by the Broker, the Broker MUST notify the Customer that their booking requires confirmation before it can proceed. The Broker MUST detect and highlight any changes from their stored OrderProposal in the notification, based on the difference between the OrderItems stored by the Broker and those in the feed.
  • When the Customer confirms, the Broker MUST re-authorise the totalPaymentDue, if it has increased, with the Payment Provider, then proceed to B using the orderProposalVersion the Customer has agreed to.
  • The Customer or Seller MAY also choose to abort at any point as per Minimal Proposal Implementation.

Note that this specification does not support OrderProposal amendment by the Customer, and that Customer requests for changes to an existing proposal may be done via messages communicated via Message Exchange.

5.5.5.3 Message Exchange

The Booking System and Broker MAY support a basic level of Message Exchange while the OrderProposal is in-flight.

If an Offer requires Message Exchange, the Booking System MUST include the openBookingFlowRequirement of https://openactive.io/OpenBookingMessageExchange in the Offers within its open data. This allows Brokers that do not support such negotiation to easily filter out such offers from their experience.

Message Exchange is achieved using the two properties orderSellerNote and orderCustomerNote, and does not have any impact on the orderProposalVersion.

The Seller MAY send a message to the Customer by setting orderSellerNote in the OrderProposal, and updating the OrderProposal in the Orders feed. Any change detected in the orderSellerNote in the Orders feed MUST promptly trigger a notification from the Broker to the Customer, so that the value of orderSellerNote can be safely overwritten with another message. To ensure that messages are not lost in the RPDE feed, the Seller MAY send no more than one message every 5 minutes per OrderProposal.

The Customer MAY send a message to the Seller by the Broker making a PATCH call to the OrderProposal Update endpoint to set orderCustomerNote in the OrderProposal. Any such orderCustomerNote updates via PATCH calls MUST either trigger a notification from the Booking System to the Seller, or be stored in the Booking System for later reference by the Seller, so that the value of orderCustomerNote can be safely overwritten with another message.

Note that the orderCustomerNote property MUST NOT be available in the Orders feed, to remove unnecessary RPDE updates.

5.5.6 Step-by-step process description

Sequence diagram with approval
Figure 10 Sequence diagram showing API interactions with approval

For an Order that requires approval, the flow differs from the Simple Booking Flow, and proceeds as follows:

i-iv) The steps 1. Select; C1; 2. Identify; and C2 of this flow are exactly the same as Simple Booking Flow steps i-iv, except that OrderQuote is returned at C1 and C2 with orderRequiresApproval set to true if any OrderItems require approval. In this case, the next step after C2 MUST be to follow the Booking Flow with Approval.

  • The Booking System MUST include the openBookingFlowRequirement of https://openactive.io/OpenBookingApproval for any Offers that require this flow. This allows Brokers that do not support approval to easily filter out such offers from their experience.

  • For the avoidance of doubt, as in the Simple Booking Flow:

    • Additional data is supplied at C2 to satisfy the orderItemIntakeForm for all OrderItems in C1.

    • The totalPaymentDue from C2 is used for initial Payment Authorisation.

v) "2.1. Propose" - Customer submits payment details, and submits an initial booking in the form of an OrderQuote.

    • The Customer MUST only be allowed to proceed if no OrderItems in the OrderQuote contain any errors.

    • If totalPaymentDue is 0 because all OrderItems are free of charge, prepayment of all selected Offers is either https://openactive.io/Unavailable or https://openactive.io/Optional, or the Customer has chosen not to pay, payment details MUST NOT be captured.

vi) Payment Authorisation - Broker pre-authorises totalPaymentDue from Payment Provider.

This proceeds almost exactly as in the Simple Booking Flow, except that instead of an Order, an OrderProposal (subclassing Order) is created by the Broker.

  • As in the Simple Booking Flow, the OrderProposal id, which includes the UUID and is generated by the Broker to make the PUT call at B (the OrderProposal id is also the PUT endpoint URL), SHOULD be used as a reference in the Payment Provider, if possible.

  • As in the Simple Booking Flow, the Broker MUST store the OrderProposal at this point even if pre-authorisation is not taken, in order to prevent the possibility of a race condition. However, note that orderCreationStatus MUST NOT be set on the OrderProposal.

vii) P - The Broker makes a request containing its newly-created OrderProposal, including its UUID, a customer object, any orderItemIntakeFormResponse required, and totalPaymentDue including the total amount pre-authed. The Booking System responds in turn with an OrderProposal with orderProposalStatus set to https://openactive.io/AwaitingSellerConfirmation.

  • The Broker MUST store this returned OrderProposal.

  • The call is atomic.

  • The returned OrderProposal also contains an orderProposalVersion (constructed from the id of the OrderProposal, combined with an additional version UUID).

  • OrderItems returned have an orderItemStatus of https://openactive.io/OrderProposed.

  • If the call is successful (returns a 200 response), the booking MUST NOT be considered as complete by the Booking System at this point, and instead MUST be considered in a 'proposed' state.

  • The Broker MUST NOT store an orderCreationStatus against the OrderProposal.

  • The OrderProposal MAY include an orderCustomerNote in order to allow the Customer to ask a question about this Opportunity, as the beginning of a Message Exchange, if the openBookingFlowRequirement of any Offer referenced in the OrderProposal includes https://openactive.io/OpenBookingMessageExchange.

  • To match much of the behaviour of B in the Simple Booking Flow, the following also apply:

    • The OrderProposal from P will not be present in the Orders feed until it is updated at least once.

    • totalPaymentDue MUST be submitted to P and MUST match the value calculated by the Booking System to ensure that the totalPaymentDue has not changed.

    • If payment details were not captured due to the prepayment value of the selected Offers, or if totalPaymentDue is 0 due to all OrderItems being free of charge, the payment property MUST NOT be included in the OrderProposal request or response. If the payment property is omitted where totalPaymentDue is not 0, and the prepayment value of any Offer is https://openactive.io/Required or unspecified, then an IncompletePaymentDetailsError MUST be returned. If the payment details supplied (e.g. accountId) are not considered valid for reconciliation by the Booking System then an InvalidPaymentDetailsError MUST be returned.

    • The identifier of the Payment is taken from the payment pre-authorization, which must be able to be matched against any payment reports produced by the Payment Provider for the purposes of audit and/or reconciliation.

    • If the totalPaymentDue included in the Order object is different to the current totalPaymentDue (which might have changed since C2, e.g. if the headline price is changed in the Booking System between these two steps), P returns an TotalPaymentDueMismatchError with associated status code, but the Lease is sustained so that the Customer can be prompted whether they want to continue with the new amount.

    • If the Lease has expired, or if Leases are not supported, then the Booking System attempts to create the OrderProposal anyway. If there are insufficient spaces available for OrderItems the P returns an OpportunityHasInsufficientCapacityError with associated status code.

    • If the booking succeeds only for some OrderItems, the whole transaction to create the OrderProposal within the Booking System is rolled back and P returns an OrderCreationFailedError with associated status code.

    • If there's a network failure, e.g. no response from server, then the Broker can resubmit with same UUID. As the call is idempotent, the Booking System will need to track the UUID supplied by Broker against any successfully created OrderProposals, to ensure that it can return the same OrderProposal for subsequent requests. The Broker MUST NOT reuse UUIDs across multiple OrderProposals.

    • The Booking System MUST reflect back all properties defined by this specification that are supplied by the Broker's request in the OrderProposal returned from this call, including broker and brokerRole, to acknowledge they have been recognised (even if they have not actually been stored by the Booking System). The Booking System MUST NOT reflect back extension properties outside of this specification unless it explicitly supports them.

    • The OrderProposal returned from this call MUST be stored by the Broker, to be subsequently updated by the Orders feed. The Orders feed contains only partial-updates of the OrderProposal stored by the Broker, and the Broker is the only actor guaranteed to maintain a complete view of the OrderProposal. The Booking System MAY store the full OrderProposal if desired.

    • It is the responsibility of the Broker to store any additional state it requires that is not included in the OrderProposal returned from this call, and RECOMMENDED that such state is stored alongside the Broker's persisted Order.

    • This call is idempotent, and hence if the UUID used for an OrderProposal already represents a completed OrderProposal with the same set of OrderItems for the same customer as those specified, the success response is returned as normal. If any differences exist in the OrderProposals, a UUID clash is assumed, a 500 response of with OrderAlreadyExistsError is returned by the Booking System, and a new UUID must be generated by the Broker to retry the call.

    • As this call is idempotent, it can be safely retried in the case of an temporary unexpected error (any error that returns status code 503) or rate limiting (any error that returns status code 429).

    • When the request still returns status code 503 after a number of retries the UUID MUST be regenerated to allow for a clean retry of the booking. The UUID MUST NOT be regenerated for expected errors (status code 4xx).

viii) "2.2. Approve" - Seller approves the booking after automated or manual review.

  • The Booking System must ensure that the OrderProposals in the Orders feed represent the current state of OrderProposal within the system. The Broker MUST use these OrderProposals to process orderProposalStatus changes and send update notifications to the Customer, including any new values for orderSellerNote.

  • Once approved, the Booking System puts the updated OrderProposal onto the Orders feed (A), with either an unchanged orderProposalVersion (see Minimal Proposal Implementation) or a new orderProposalVersion (see Proposal Amendment).

  • If the orderProposalVersion in the Orders feed has changed compared with the orderProposalVersion in the OrderProposal stored by the Broker, the Broker MUST notify the Customer that their booking requires confirmation before it can proceed. The Broker MUST detect and highlight any changes from their stored OrderProposal in the notification, based on the difference between the OrderItems stored by the Broker and those in the feed.

ix) "3. Book and Pay" - Customer confirms booking

  • The Broker MUST only proceed when orderProposalStatus is set to https://openactive.io/SellerAccepted in the Orders feed, and the orderProposalVersion is set to a value the Customer has agreed to (see Proposal Amendment).

  • If totalPaymentDue is 0 because all OrderItems are free of charge, prepayment of all selected Offers is either https://openactive.io/Unavailable or https://openactive.io/Optional, or the Customer has chosen not to pay, payment details MUST NOT be captured.

x) Payment Re-authorisation - Broker re-authorises totalPaymentDue from Payment Provider as necessary

  • The Broker MUST re-authorise the totalPaymentDue, if it has increased in the new orderProposalVersion that the Customer has agreed to (see Proposal Amendment), or if the previous Payment Authorisation has expired.

  • As with the Simple Booking Flow, the Broker MUST store the Order request with an orderCreationStatus of https://openactive.io/OrderCreationPaymentAuthorized at this point, even if pre-authorisation is not taken, in order to ensure that the UUID in the Orders feed is recognised without a race condition.

xi) B - The Broker makes a minimal call with an Order consisting of only the orderProposalVersion and any additional payment data from the re-authorisation step if necessary; the Booking System responds with a full Order as per the Simple Booking Flow.

  • If the Lease has expired, or if Leases are not supported, then the Booking System attempts to make the booking anyway. If there are insufficient spaces available for OrderItems the B returns an OpportunityHasInsufficientCapacityError with associated status code and the original OrderProposal MUST be rejected by the Seller as per Proposal Leasing. Note that this is unlikely to occur as in such cases the OrderProposal SHOULD have already been rejected when it could no longer be fulfilled.

  • The Booking System MUST reflect back all properties defined by this specification that were supplied in the Broker's original OrderProposal request (with amendments if Proposal Amendment has occurred), to acknowledge they have been recognised. The Booking System MUST NOT reflect back extension properties outside of this specification unless it explicitly supports them.

  • All other behaviour is identical to the Simple Booking Flow step vii.

xii-xiv) The Payment Capture, Invoice Generation, Customer Notification, and Refunds and Cancellation steps of this flow are exactly the same as Simple Booking Flow steps viii-x.

Booking Flow Rollback also applies to this flow exactly as in the Simple Booking Flow.

5.5.7 Contract

A summary of the key conformance criteria is included here. These apply for the Booking Flow with Approval in addition to the contract specified in the Simple Booking Flow.

5.5.7.1 Broker contract with Booking System
  • The Broker MUST call P with Customer details as they would for B in the Simple Booking Flow, and MUST store the resulting OrderProposal.
  • The Broker MUST process updates to the OrderProposal from the Orders feed, including A.
  • The Broker MAY negotiate on the OrderProposal using a OrderProposal Update call.
5.5.7.2 Booking System contract with the Broker
  • The Booking System MUST respond to P with an OrderProposal, or a relevant error. The whole creation of the OrderProposal MUST succeed or fails as one.
  • Upon approval or updates to the OrderProposal or any OrderItem within it, the Booking System MUST include the updated OrderProposal in the Orders feed, including A.
  • The Booking System MAY negotiate on the OrderProposal using the Orders feed.

Calls to P MUST be idempotent, based on the UUID.

5.6 Customer Details and Additional Details capture

5.6.1 Customer Details capture

The specification includes provision to capture of the following details related to the Customer (the actor who is booking), with only the e-mail address being required.

  • E-mail address (email)
  • Forename (givenName)
  • Surname (familyName)
  • Telephone (telephone)

Note that these are not the details of attendees, and that the Customer is not guaranteed to participate in the activity. This facilitates child booking as the Customer can easily book on behalf of a child, without supplying the child's own personal details.

5.6.2 Attendee Details capture

If attendee details in scope of the Person type are captured, they MUST be captured via the attendee property of the OrderItem (and not using the orderItemIntakeForm). This allows attendee details to be stored and reused by the Broker.

Note that conformance with this specification requires that the Booking System MUST NOT require attendee data to be submitted in all cases, and that configuration of which attendee details are required (if any) MUST be available to the Seller.

If an Offer requires attendee details capture, the Booking System MUST include the openBookingFlowRequirement of https://openactive.io/OpenBookingAttendeeDetails in the Offers within its open data. This allows Brokers that do not support attendee details capture to easily filter out such offers from their experience.

Example 3: Example of attendeeDetailsRequired provided by Booking System at C1
{
  "@type": "OrderItem",
  ...
  "attendeeDetailsRequired": [
    "https://schema.org/givenName",
    "https://schema.org/familyName"
  ],
}
Example 4: Example of attendee details provided with each OrderItem at C2
{
  "@type": "OrderItem",
  ...
  "attendee": {
    "@type": "Person",
    "email": "[email protected]",
    "telephone": "020 811 8055",
    "givenName": "Geoff",
    "familyName": "Capes"
  }
}

If a required property within Person is not supplied by the Broker the Booking System MUST include an IncompleteAttendeeDetailsError against the offending OrderItem at C2 which includes an instance that references the id of the missing or invalid Property.

Example 5: Example of an error for missing attendee property data
{
  "@type": "OrderItem",
  ...
  "error": [
    {
      "@type": "IncompleteAttendeeDetailsError",
      "instance": "https://schema.org/givenName",
      "description": "Missing attendee first name"
    }
  ]
}

The Broker MAY remember the attendee data previously submitted by the Customer, if consent is provided.

5.6.3 Additional Details capture

This version of the specification does not provide comprehensive support for construction of a complex additional details capture form. However, it does facilitate the use of simple text fields, dropdowns and checkboxes to capture additional details. These can be used for both the Simple Booking Flow and the Booking Flow with Approval.

If an Offer requires additional details capture, the Booking System MUST include the openBookingFlowRequirement of https://openactive.io/OpenBookingIntakeForm in the Offers within its open data. This allows Brokers that do not support such additional details capture to easily filter out such offers from their experience.

Four types of form element are available, all of which sub-class PropertyValueSpecification:

  • ShortAnswerFormFieldSpecification
  • ParagraphFormFieldSpecification
  • DropdownFormFieldSpecification
  • BooleanFormFieldSpecification
5.6.3.1 Text fields

ShortAnswerFormFieldSpecification and ParagraphFormFieldSpecification are identical except that they represent a single-line text box and a multi-line text box, respectively.

Example 6: Example of orderItemIntakeForm with ShortAnswerFormSpecification provided by Booking System at C1
{
  "@type": "OrderItem",
  ...
  "orderItemIntakeForm": [
    {
      "@type": "ShortAnswerFormSpecification",
      "@id": "https://example.com/experience",
      "name": "Level of experience",
      "description": "Have you played before? Are you a complete beginner or seasoned pro?",
      "valueRequired": true
    }
  ]
}
Example 7: Example of orderItemIntakeFormResponse for ShortAnswerFormSpecification provided by Broker at C2
{
  "@type": "OrderItem",
  ...
  "orderItemIntakeFormResponse": [
    {
      "@type": "PropertyValue",
      "propertyID": "https://example.com/experience",
      "value": "I've played twice before, but I'm a quick learner so I hope to keep up!"
    }
  ]
}
5.6.3.3 Checkbox fields

BooleanFormFieldSpecification accepts either a true or false response, and MUST NOT be specified as valueRequired.

Example 10: Example of orderItemIntakeForm with BooleanFormFieldSpecification provided by Booking System at C1
{
  "@type": "OrderItem",
  ...
  "orderItemIntakeForm": [
    {
      "@type": "BooleanFormFieldSpecification",
      "@id": "https://example.com/photoconsent",
      "name": "Photo Consent",
      "description": "Are you happy for us to include photos of you in our marketing materials?"
    }
  ]
}

The value in the PropertyValue response MUST be either true or false.

Example 11: Example of orderItemIntakeFormResponse for BooleanFormFieldSpecification provided by Broker at C2
{
  "@type": "OrderItem",
  ...
  "orderItemIntakeFormResponse": [
    {
      "@type": "PropertyValue",
      "propertyID": "https://example.com/photoconsent",
      "value": true
    }
  ]
}
5.6.3.4 Form validation and auto-fill

If a field with valueRequired is not supplied by the Broker, the Booking System MUST include an IncompleteIntakeFormError against the offending OrderItem at C2 which includes an instance that references the id of the specific PropertyValueSpecification.

Example 12: Example of an error for missing additional details field data
{
  "@type": "OrderItem",
  ...
  "error": [
    {
      "@type": "IncompleteIntakeFormError",
      "instance": "https://example.com/experience",
      "description": "Incomplete additional details supplied"
    }
  ]
}

If the contents of a field supplied by the Broker are present but invalid, the Booking System MUST include an InvalidIntakeFormError against the offending OrderItem at C2 which includes both an instance that references the id of the specific PropertyValueSpecification, and a human-readable description of the validation constraint to allow the Customer to correct it.

In this version of the specification, any required field value validation MUST be implemented by the Booking System via InvalidIntakeFormError, for example if integer values are required.

Example 13: Example of an error for invalid additional details field data
{
  "@type": "OrderItem",
  ...
  "error": [
    {
      "@type": "InvalidIntakeFormError",
      "instance": "https://example.com/shoesize",
      "description": "The provided shoe size is not a number"
    }
  ]
}

The Broker MAY remember the values previously submitted by the Customer based on the id of the PropertyValueSpecification, if consent for such storage is provided.

It is expected that future versions of the specification will support more complex field types.

6. Broker Roles

A Broker is an actor that arranges transactions between a Seller and Customer, either directly or indirectly.

From a contractual and taxation perspective, there are two types of Broker: agents (AgentBroker), and resellers (ResellerBroker). In addition, situations in which the customer contracts directly with the seller without a third party are represented by the brokerRole value of NoBroker.

Note that this specification does not deal with contractual relationships, and simply provides a mechanism to record bookings.

6.1 ResellerBroker

6.1.1 Definition

A reseller is a company or individual that purchases goods or services with the intention of selling them rather than consuming or using them. This is usually done for profit (but could be resold at a loss).

6.1.2 Contractual Relationships

In the context of OpenActive, a ResellerBroker contracts directly with the Seller as a business-to-business relationship to purchase access to the Opportunity. It then, at a later point in time (which may only be milliseconds later), separately forms a contractual relationship with the Customer, who purchases access to the Opportunity from the ResellerBroker.

ResellerBroker relationships
Figure 11 Contractual relationships in ResellerBroker mode

6.1.3 Taxation

The ResellerBroker's purchase from the Seller is business-to-business, which is subject to the appropriate taxation based on the ResellerBroker as the Customer. The Customer's purchase from the ResellerBroker is business-to-consumer, which is subject to the appropriate taxation based on the ResellerBroker as the Seller.

Hence any tax exemption that the Customer may enjoy when purchasing directly from the Seller (e.g. in UK law, if the Seller is a VAT exempt eligible body) is not relevant here, as no direct contractual relationship is formed between Seller and Customer.

6.1.4 Scope of Specification

This specification is designed to govern the interaction between the ResellerBroker and Seller. It does not include provision for the interaction between the ResellerBroker and Customer explicitly, though it may be repurposed by the Broker to perform this function by treating the Broker as a Booking System. This specification also does not include provision for the recording of the execution of the contractual relationship with any Payment Provider (e.g. for payment processing fees). Such relationships MUST be handled separately. When the brokerRole is set to ResellerBroker, this indicates that the payee for accounting and tax purposes is the broker specified in the Order. Note that the customer may still optionally be included in the Order, for example to help front-of-house staff identify the Customer.

6.1.5 Conformance criteria

When the Broker generates the Invoice, it must be made payable to Order.broker, Order.broker MUST be provided and Order.customer is optional.

6.2 AgentBroker

6.2.1 Definition

An agent is authorized to act on behalf of another (the Seller, or "principal") to create legal relations with a third party (the Customer). Succinctly, it may be referred to as the equal relationship between a Seller and an agent whereby the principal, expressly or implicitly, authorizes the agent to work under his or her control and on his or her behalf. The agent is required to negotiate on behalf of the principal (Seller) and/or bring them and third parties (Customers) into contractual relationship.

6.2.2 Contractual Relationships

There are three separate types of contractual relationship involved: AgentBroker with Seller, known as the principal-agent relationship or "internal" relationship; AgentBroker with Customer with whom they deal on their Seller's behalf ("external relationship"); and Seller with Customer when arranged by an AgentBroker.

AgentBroker relationships
Figure 12 Contractual relationships in AgentBroker mode

6.2.3 Taxation

While facilitated by the AgentBroker, the primary purchase is made by the Customer directly from the Seller, as would be the case if the Customer was to purchase from the Seller independently. Hence any tax exemption that the Customer may enjoy when purchasing directly from the Seller (e.g. in UK law, if the Seller is an VAT exempt eligible body) is relevant here.

The Customer's relationship with AgentBroker is business-to-consumer. Hence any additional services (e.g. customer-facing booking fees) are subject to the appropriate taxation based on the AgentBroker as the seller.

The AgentBroker's relationship with the Seller is business-to-business. Hence any additional services (e.g. booking commission) are subject to the appropriate taxation.

6.2.4 Scope of Specification

This specification is designed to govern the recording of the execution of the contractual relationship between the Customer and Seller. The scope of this specification does not include the contractual relationship between the AgentBroker and the Seller (e.g. for booking commission), between the AgentBroker and the Customer (e.g. for booking fees) or with any Payment Provider (e.g. for payment processing fees). Such relationships and the reconciliation of associated invoices MUST be handled separately. When the brokerRole is set to AgentBroker, this indicates that the payee for accounting and tax purposes is the customer specified in the Order.

6.2.5 Conformance criteria

When the Broker generates the Invoice, it must be made payable to Order.customer, Order.broker MUST be provided and Order.customer MUST be provided.

6.2.6 Informed purchase

When using a brokerRole of AgentBroker, the Broker MUST make the Customer aware that they are purchasing directly from the Seller via the Broker, and not directly from the Broker.

6.3 NoBroker

6.3.1 Definition

This specification supports direct purchase by a Customer (for example in the context of the Seller's own website, or for businesses that purchase large volumes of FacilityUse slots to run leagues).

6.3.2 Contractual Relationships

The contractual relationship is a simple one between the Seller and the Customer.

NoBroker relationships
Figure 13 Contractual relationships in NoBroker mode

6.3.3 Taxation

The purchase is made by the Customer directly from the Seller, as would be the case if the Customer was to purchase from the Seller outside of this specification. Hence any tax exemption that the Customer may enjoy when purchasing directly from the Seller (e.g. in UK law, if the Seller is an VAT exempt eligible body) is relevant here.

6.3.4 Scope of Specification

This specification is designed to govern the recording of the execution of the contractual relationship between the Customer and Seller. The scope of this specification does not include the contractual relationship with any Payment Provider (e.g. for payment processing fees). Such relationships MUST be handled separately.

When the brokerRole is set to NoBroker, this indicates that the payee for accounting and tax purposes is the customer specified in the Order.

6.3.5 Conformance criteria

When an Invoice is generated, it must be made payable to Order.customer, Order.broker MUST NOT be provided and Order.customer MUST be provided.

7. Systems of Record

7.1 Roles and responsibilities overview

For the purposes of this specification, the key components of a booking are: Order, Invoice, Payment and Refund.

Note that OrderQuote is a subclass of Order used during the Order creation process, and is not explicitly persisted.

The Broker has visibility of all components, while for simplicity of implementation the Booking System only has visibility of the Order, as shown below:

Roles and responsibilities
Figure 14 Roles and responsibilities

Orders are the basis of the exchange between the Booking System and the Broker, with OrderQuotes used as part of the Order creation process. This specification describes a detailed model and API to allow Brokers to manipulate Orders, and only high level functional requirements for the expected behaviour of Invoices, Payments, and Refunds.

Component Description Owner Audit
Order A mutable object that encompasses the live state of the bookable Events within the Booking System. An Order has a orderItems, a totalPaymentDue, and all tax details, but is not itself a legal representation of a purchase from the Seller. Booking System. Mutable
Invoice An immutable object that is a legal representation of the purchase, which MUST be rendered to the Customer as a legally permissible tax receipt, and exist as a tax point. A new Invoice MUST be generated for the Order to replace the previous Invoice each occasion that an orderItem within an Order is cancelled. Broker Immutable
Payment A immutable object that represents the original payment to the Seller for the initial Order. Although modelling the Payment is outside the scope of this specification, it is recommended that one payment exists for each Order. Payment Provider Immutable
Refund An immutable object that represents each refund made against the Payment, up to the Payment's total amount Payment Provider Immutable

7.2 Invoices and Tax receipts

Due to the variety of business models in use in OpenActive, the Booking System MUST NOT send receipts directly to the Customer for AgentBroker and ResellerBroker bookings.

For each successful booking operation (where C1, C2 and B together constitute a successful booking operation), the Broker MUST generate a new Invoice in the form of a tax receipt, on the basis of the Orders feed contents. To ensure that the Customer is provided with the correct tax receipt, if totalPaymentTax is provided in an Order in the Orders feed, the Broker MUST generate a new or updated Invoice and either (a) send a tax receipt representation of the Invoice to the Customer and/or (b) offer a means to allow the Customer to easily retrieve a full tax receipt representation of the specific version of the Invoice at any future date for any previous booking made. As it is ultimately the Seller's responsibility to send tax receipts, they need to be confident that the Broker is making tax information available to its Customers in an accurate and timely manner.

The tax receipt sent by the Broker MUST comply with any legal requirements of the jurisdiction of operation.

Note that if the specification is followed correctly, for all Broker modes where a Seller may need to supply a tax receipt manually for any reason, the Seller will have sufficient details of the Customer and of the transaction to generate the most recent tax receipt.

7.3 Payments and Refunds

Due to the variety of business models available in the OpenActive ecosystem, conformance to this specification requires that the Payment Provider be distinct from the Booking System for which this API is defined. The Booking System MAY separately expose its own native payment functionality as if it were a Payment Provider, however its use MUST be OPTIONAL.

Although the details of Payments and Refunds are not in the scope of this specification, a single Payment object is included in the Order in the payment property to ensure payments are fully traceable in case of audit.

If payment details were not captured due to the prepayment value of the selected Offer, or if totalPaymentDue is 0 due to all OrderItems being free of charge, the payment property MUST NOT be included. In all other cases payment property MUST be included in the Offer at B.

7.3.1 Virtual payments

Note that this Payment is permitted to represent a virtual Payment consolidating a number of actual Payments to allow for a wide variety of payment methods. In the case where the Seller is paid monthly, the Payment may refer to a future invoice against which the Payment will eventually be paid.

7.3.2 Basic Payment

A unique reference to this Payment MUST be included in the Payment identifier of the Order sent to the Booking System. This Payment identifier MUST be uniquely resolvable for audit purposes.

The accountId of the Payment SHOULD be provided to reference the specific account within the Payment Provider that is used for reconciliation purposes.

The paymentProviderId of the Payment SHOULD be provided to reference the specific Payment Provider that is used.

The name of the Payment SHOULD be used to provide information about the source of the payment that can be stored in the Booking System, to help the Seller in discussions with the Customer (e.g. "AcmeBroker Points" or "AcmeBroker via Credit Card").

Example 14: Example of Payment
"payment": {
  "@type": "Payment",
  "identifier": "12345678ABCD",
  "name": "Big Media Services",
  "accountId": "SN1593",
  "paymentProviderId": "STRIPE"
},

7.3.3 DynamicPayment

For fully dynamic pricing, where price is not known at the point of purchase, a DynamicPayment MUST be used. Example use cases include volume-based pricing, where the price of items are calculated at the end of the month depending on the total volume booked during that period.

To use the DynamicPayment the following preconditions must be met:

  • The Broker and the Seller MUST have agreed an arrangement for dynamic pricing out-of-band, including which Opportunities and Offers it applies to.
  • Dynamic pricing cannot be mixed with standard pricing behaviour within the same Order, so all OrderItems selected MUST be applicable for dynamic pricing based on such agreements.
  • The Opportunities that are selected for booking MUST be bookable as in the standard flow.

To use DynamicPayment the booking flow MUST be completed as normal with the following additional requirements:

  • The OrderQuote, OrderProposal and Order in request, response, and in the subsequent Orders feed MUST NOT include totalPaymentDue or totalPaymentTax, and OrderItems MUST NOT include acceptedOffer or unitTaxSpecification.
  • The booking flow MUST be completed as if prepayment was set to https://openactive.io/Unavailable for the acceptedOffer for all OrderItems.
  • The OrderQuote / OrderProposal / Order submitted to C1, C2 and B (and P if using approval) MUST have a payment property that contains a DynamicPayment, which is consistent throughout the calls.
  • If the DynamicPayment details supplied (e.g. accountId) are not considered either sufficient or valid for reconciliation by the Booking System then an IncompletePaymentDetailsError or InvalidPaymentDetailsError MUST be returned by all calls, respectively.
  • The Booking System MUST NOT record any price against the Order or OrderItems for reconciliation, as such reconciliation MUST occur completely out-of-band.

The accountId of the DynamicPayment MUST be used consistently in order to allow the Seller to reconcile Opportunities booked using a particular account.

The name of the DynamicPayment SHOULD be used to provide information about the source of the payment that can be stored in the Booking System, to help the Seller in discussions with the Customer (e.g. "AcmeBroker Points" or "AcmeBroker Membership").

Note that the Booking System MAY choose to validate the DynamicPayment details, as above, though this is not a requirement - it could simply accept any values provided.

Example 15: Example of DynamicPayment
"payment": {
  "@type": "DynamicPayment",
  "identifier": "12345678ABCD",
  "name": "MoveGB Membership",
  "accountId": "MOVE"
},

7.3.4 Payment reconciliation

Although automated payment reconciliation is outside the current scope of this specification, a Booking System MAY include identifiers suitable for payment reconciliation to be handled out-of-band with each OrderItem using the additionalProperty property.

In keeping with the Schema.org definition of PropertyValue, the name and value properties MUST be provided within any given PropertyValue, and the Broker SHOULD include these in any reconciliation process with the Seller.

Example 16: Example of additionalProperty for OrderItem
"additionalProperty": [
  {
    "@type": "PropertyValue",
    "name": "SiteID",
    "value": "ABC1234"
  },
  {
    "@type": "PropertyValue",
    "name": "DeptID",
    "value": "D89"
  }
],

7.4 Tax calculation

7.4.1 Business-to-consumer tax calculation by Booking System is mandatory

For AgentBroker and NoBroker modes, where the Customer is of type Person and a tax receipt is usually generated by the Booking System, the tax calculation and associated properties this specification MUST be implemented by the Booking System (even if the resulting tax amount is zero).

7.4.2 Business-to-business tax calculation by Booking System is optional

Tax calculation at the Booking System is OPTIONAL when the brokerRole of ResellerBroker is specified, or when a Customer of type Organization is specified. If tax calculation is not supported for a ResellerBroker, unitTaxSpecification and totalPaymentTax MUST NOT be included, and the taxCalculationExcluded property MUST be returned as true in the OrderQuote and Order (i.e. explicitly not including tax).

When taxCalculationExcluded is true, all Offers MUST include net prices (i.e. without tax included), regardless of the taxMode. This allows tax to be calculated by the ResellerBroker or business Customer and reconciled outside of this specification.

Note that for business-to-business sales for a Seller with taxMode of the organizer or provider set to https://openactive.io/TaxGross, the Offers available in the Opportunity Data will still be calculated as tax-inclusive for a business-to-consumer sale, and so MUST be presented by the Broker as such, with the business-to-business tax calculation occurring during the booking flow. If the usecase of presenting the correct price up-front gains traction, additional price data must be made available as part of the openly published Opportunity Data to resolve this shortcoming.

7.5 Tax mode

The taxMode (https://openactive.io/TaxNet or https://openactive.io/TaxGross) is REQUIRED to be specified at the Seller level, and MUST be reflected within the Organization or Person that is included in the REQUIRED organizer or provider properties within Opportunity Data specified in [Modelling-Opportunity-Data]. It MUST also be consistent for all Orders originating from that Seller.

Note that taxMode only affects the interpretation of the price of the Offer. totalPaymentDue is always Gross (tax inclusive), and unitTaxSpecification and totalPaymentTax include only the taxes themselves so are unaffected.

taxMode https://openactive.io/TaxNet https://openactive.io/TaxGross
Price displayed Excludes tax Includes all applicable taxes assuming a Person as a customer
Currencies usually associated USD, CAD EUR, AUD, GBP
Example price of Opportunity before tax 10.00 10.00
Example tax rate 0.2 0.2
Price displayed to Customer during browsing (Offer) 10.00 12.00
Total paid by Customer (totalPaymentDue) 12.00 12.00

7.6 Free opportunities

Free Opportunities, defined as those with at least one applicable Offer with a price of 0, MUST follow the same workflow as paid Opportunities, except with no interaction with a Payment Provider, and no payment included in C1, C2, P or B requests.

For Offers with a price of 0 publishers MAY not include priceCurrency. If a priceCurrency is not available then Brokers SHOULD communicate "Free" or equivalent when referencing a zero price.

7.7 Offer overrides

The unit price of an OrderItem MAY be overridden to allow for business models with variable pricing (where price differs from list price).

The Broker and the Seller MUST have agreed an arrangement for offer overrides out-of-band, including which Opportunities and Offers they may be applied to.

An OfferOverride type may be supplied by the Broker to the acceptedOffer property at C1, C2 and B. When an OfferOverride is used, it MUST be used consistently between C1, C2 and B, it MUST be used by the Booking System instead of acceptedOffer to calculate the tax and totals. It MAY be used for any brokerRole.

The unitTaxSpecification is calculated by the Booking System exactly as it would be for a standard Offer. The price of the OfferOverride may be zero, and also may be higher or lower than the available Offers that exist in the Booking System.

OfferOverride MUST NOT be used when isAccessibleForFree is true, as free events cannot be overridden. Note that Booking Systems that only offer Free Opportunities MUST NOT implement OfferOverride support, and must always throw an error when it is used.

Example 17: Example of OfferOverride
"acceptedOffer": {
  "@type": "OfferOverride",
  "description": "Winger space for Speedball.",
  "name": "Speedball winger position",
  "price": 10.00,
  "priceCurrency": "GBP"
}

8. Order Operations

8.1 Definition of a 'bookable' Opportunity and Offer pair

Before entering into the Booking Flow, the Customer MUST have selected at least one bookable pair of an Opportunity ("Yoga Tuesday 14th 7pm") with an associated Offer ("Junior (8-18) £9"). These pairs populate the acceptedOffer and orderedItem properties within the OrderItems.

The pairing of an Opportunity of type Event, ScheduledSession, HeadlineEvent, Slot or CourseInstance with an Offer is deemed to be bookable via the Open Booking API if the Offer is applicable to the Opportunity following the principles of Offer inheritance contained in [Modelling-Opportunity-Data], and if both the Opportunity and Offer meet the criteria specified below.

Bookable Offers:

Bookable Opportunities:

The Broker MUST NOT attempt to book an Opportunity that is not bookable by the above criteria. The Booking System MUST include an error of value UnavailableOpportunityError against each OrderItem in the OrderQuote that is not bookable.

8.1.1 Booking restrictions

Note that although the Customer may be ineligible to attend a bookable Opportunity via an associated bookable Offer based on the genderRestriction or ageRange of either the Opportunity or Offer, this specification encourages the Broker NOT to capture additional personal data about the Customer (e.g. gender and date of birth) and NOT to enforce validation of these restrictions using such data, but instead to prominently communicate clear messaging regarding such restrictions throughout the booking process to ensure that the Customer is aware of these and able to make an informed decision.

There may be additional restrictions placed on an Opportunity by the Seller. 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 System and Seller to make such restrictions available as attendeeInstructions on the SessionSeries, FacilityUse, Event, HeadlineEvent or CourseInstance, and the Broker MUST communicate these instructions clearly during the booking process.

The Broker MAY filter the Offers it makes available to the Customer, for example based on ageRange of the Offer.

8.2 Amending the OrderQuote before B

The Broker MAY call C1 and MUST call C2 when the Customer updates their basket to add and remove items.

When repeated calls to C1 or C2 are made with the same UUID, the Booking System MUST update any associated Lease to match the latest set of OrderItems by removing those OrderItem that are not present in the request from the Lease, and adding those OrderItem that have been added since the last call to the Lease.

Leases may be cancelled by the Broker as a courtesy by submitting an OrderQuote Deletion request using the same UUID. The Broker SHOULD make a reasonable attempt to cancel all Leases when it is aware that a Customer has abandoned their journey, or if an error occurs and a new UUID is to be generated for the same customer. This increases the likelihood of an Opportunity being booked with one space remaining.

For Booking Systems that have not implemented leasing, an OrderQuote Deletion request MUST simply return a 204 success status in all cases.

8.3 Cancellation after B

Either the whole Order or individual OrderItems within the Order may be cancelled after B, however OrderItems cannot be replaced or added to an existing Order past this point within this version of the specification.

The cancellation may be requested by either the Customer or the Seller, and results in an updated Order item on the Orders feed. Once a cancellation status is placed on an OrderItem within an Order in the Orders feed it is assumed by the Booking System to have been processed, and it MUST NOT be reversed. A new Order will need to created to reinstate the booking.

8.3.1 Refund after the opportunity has occurred

Although a cancellation may be requested before the Opportunity has occurred in order to trigger a refund, cancellation and refund after an Opportunity has occurred, or when an OrderItem has orderItemStatus set to https://openactive.io/CustomerAttended, is currently outside the scope of this specification.

Full and partial refunds after an Opportunity has occurred MUST be handled out-of-band directly between the Broker and the Seller.

8.3.2 Customer requested cancellation

Customer requested cancellation
Figure 15 Customer requested cancellation

To cancel an existing OrderItem, the Broker MUST:

  1. Use the latest state of the Order stored during the booking flow and updated via the Orders feed to determine which OrderItems may be cancelled and refunded. If allowCustomerCancellationFullRefund is true and latestCancellationBeforeStartDate subtracted from the Opportunity startDate is in the future, a PATCH against the Order including the OrderItem with "orderItemStatus": "https://openactive.io/CustomerCancelled" SHOULD succeed.

  2. Inform the Customer of the refund being requested before they commit to cancellation, using the existing data available in the stored Order to communicate the OrderItems that are included in the full refund request.

  3. Submit a PATCH for the Order including only the id for each of the OrderItems to be cancelled, with an "orderItemStatus": "https://openactive.io/CustomerCancelled" set on each. On success, a status code 204 response will be received, without any body. On failure a status code 400 response will be received, containing a CancellationNotPermittedError error including a description of the reason, which SHOULD be communicated to the Customer.

  4. Process any refunds based on the resulting updates to the Orders feed, based on the updated totalPaymentDue. Successfully cancelled OrderItems will have orderItemStatus set to https://openactive.io/CustomerCancelled. Note refunds MUST NOT be processed except in response to updates in the Orders feed.

To cancel an entire Order, the PATCH request MUST include "orderItemStatus": "https://openactive.io/CustomerCancelled" for each OrderItem within it.

Note that there is no provision for a Customer to cancel an Order after the cancellation window specified by latestCancellationBeforeStartDate. 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, which can be included in future versions of this specification.

8.3.3 Seller requested cancellation

Seller requested cancellation
Figure 16 Seller requested cancellation

OrderItems cancelled by the Seller will have orderItemStatus set to https://openactive.io/SellerCancelled in the Orders feed, and will optionally include a cancellationMessage. Refunds MUST be processed in response to the resulting updates in the Orders feed, and the Customer MUST be notified with the cancellationMessage if provided.

8.4 Orders feed: Notifications, updates and refunds

An Orders feed, which is a secure [RPDE] feed of Orders, MUST be provided by the Booking System, with the contents of the feed specific to the authenticating Broker. As noted in New Orders and OrderProposals, this list must include all, and only, Orders that have been updated by the Booking System subsequent to successful completion of B. This allows the Broker to maintain an updated state of all their bookings across a number of Booking Systems, even when changes are made outside of the Broker.

The Booking System MUST NOT communicate directly with the Customer using the personal details supplied by the Broker, and MUST instead update the Orders feed and Opportunity data to instruct the Broker to send appropriate notifications. This allows the Broker to handle refunds to cancellations, and process notifications to Customers in a consistent way.

8.4.1 Orders Feed Inconsistencies

An error condition exists where an unrecognised Order item is found by the Broker on the feed. Due to the Order being stored during the Payment Authorisation stage, there should always be a matching Order to any UUID on the Orders feed even if it has been soft-deleted. This would make an unrecognised Order an impossible scenario, and hence an unrecognised Order should be logged as a serious error within the Broker and promptly investigated.

8.4.2 Order stores with eventual consistency

It is important that the storage used by the Broker for Orders provides a linearizability guarantee when Orders are retrieved by ID, due to the partial updates that are applied to it from the Orders feed and the creation of the initial Order being synchronous.

8.4.3 Booking System: Updating the Orders feed

The Booking System must ensure that the Orders in the Orders feed represent the current state of OrderItems within the system, for the properties included in the feed. Hence any change to any property within an Order in the feed must result in a change to the modified property associated with that Order.

8.4.4 Broker: Processing the Orders feed

The Broker MUST maintain an up-to-date store of Orders received from the Booking System. Each Order received is compared to the last Order stored, and any differences between the old and new content trigger notifications and refunds to the Customer, as well as updating the Broker's store.

Hence it is important for the Broker to consider their store of Orders as durable, and not simply a cache, to ensure their Customers always get relevant notifications.

Note that the properties included in the Orders feed MAY be a subset of the Order response from B (effectively making the Orders feed a series of PATCH requests for the Broker). It is the responsibility of the Broker to process the contents of the Orders feed in a manner that maintains a complete view of the Order state.

Brokers MUST harvest the Orders feed with a frequency of at least once every 60 seconds to ensure expediency of updates and notifications to Customers.

8.4.5 New Orders and OrderProposals

A new Order MUST NOT be communicated externally by the Broker and other operations such as cancellation MUST NOT be possible until it has reached the orderCreationStatus of https://openactive.io/OrderCreationComplete, after which the orderCreationStatus can be completely ignored.

New OrderProposals MUST be stored by the Broker based on a successful response to the Order Proposal call.

New Orders and OrderProposals are not included in the feed until they have been updated at least once. This minimises the volume of Orders in the Orders feed, and allows the polling frequency of the feed to be greatly reduced, which reduces the overall load on the Booking System.

Hence all Orders and OrderProposals in the Orders feed will always be recognised by the Broker as updates to an existing stored Order or OrderProposal, and the Broker should treat any unrecognised Orders or OrderProposals in the feed as a serious implementation error.

8.4.6 Cancellation, refund calculation and notification

Regardless of the source of the cancellation, the process for refunding and notifying the Customer and updating the Broker's records is exactly the same.

The Broker MUST process refunds for each new cancellation found in the feed, and it is the Broker's responsibility to continually retry failed refund processing and escalate any processing failure to the Customer accordingly. Note that the Orders feed is "fire and forget" for the Booking System: once an OrderItem is updated with a cancellation status in the Orders feed, the status MUST not be reversed, and is assumed by the Booking System to be processed.

For Orders found with OrderItems that contain orderItemStatus with new https://openactive.io/CustomerCancelled or https://openactive.io/SellerCancelled values and a new corresponding totalPaymentDue value, the Broker MUST instruct the Payment Provider to issue a new Refund such that the new totalPaymentDue value of the Order is equal to the value of the associated Payment less all Refunds including the new one.

The Customer MUST be notified after a refund is successfully processed, using the orderItemStatus to indicate the source of the cancellation.

If refund processing takes longer than 1 minute, and a new status of https://openactive.io/SellerCancelled was detected by the Broker, then the Customer must be notified immediately of a cancellation ahead of the notification of the completed refund. This ensures the Seller can rely on the Broker as a notification channel in the event of cancellation. The Customer MUST be notified with the cancellationMessage for the OrderItem if provided.

Note that the Customer MUST be notified in the event of all cancellations, even if no refund is due for Free Opportunities.

8.4.7 Partial Refunds and out-of-band arrangements

This version of the specification does not allow a refund to be processed (in full or in part) by the Booking System without going via the Broker for any Order that has been placed via the Broker. Booking Systems MUST disable their own refund functionality for Orders placed via a Broker - only allowing Seller requested cancellations which trigger the Broker to provide full item refunds - to ensure that the book keeping and invoices of the Broker are not invalidated.

Partial refunds MAY be handled by out-of-band arrangements between the Broker and the Seller provided they do not have any affect on the Open Booking API interface. For example, the Broker could issue a partial refund and then reassign the remaining funds as a cancellation penalty via a new invoice handled and reconciled directly with the Seller outside the scope of the Booking System and this specification.

8.4.8 Customer notice notifications

The Seller may provide a notice to the Customer ahead of an Opportunity, for example to inform Customers that they are still short of a player in case any friends are interested in attending.

An orderedItem within the OrderItem of the Orders feed MAY be updated to include a customerNotice. The Customer MUST be notified with the contents of the customerNotice for the OrderItem if it differs from the value of customerNotice for that specific orderedItem that the Broker had previously stored (or if a new Order in the Orders feed includes a customerNotice).

Note that notifications that inform the Customer of substantive changes to the Opportunity MUST be triggered by updating the Opportunity Data, which will trigger a Change of logistics notification, instead of using the customerNotice mechanism.

8.4.9 Change of logistics notifications

The orderedItem within the OrderItem of the Orders feed includes an id reference to an Opportunity, which MUST be able to be cross-referenced with other sources of Opportunity Data, for example an [RPDE] open feed.

This specification does not require the Orders feed to include the full contents of the Opportunities within the orderedItem, and hence the Broker MUST monitor the Opportunity Data feeds for this information and MUST provide a notification to the Customer for substantive changes to the Opportunity (e.g. changes to startDate).

Specifically, maximumAttendeeCapacity, remainingAttendeeCapacity, maximumUses and remainingUses MUST NOT be included in the Orders feed.

8.4.10 Opportunity attendance updates

When a Customer has been confirmed as attending an Opportunity that was booked via an Order, the Booking System SHOULD update the Order within the Orders feed by setting orderItemStatus for the relevant OrderItem to https://openactive.io/CustomerAttended.

8.5 Amending the Order after B

OrderItems cannot be replaced or added to an existing Order within this version of the specification. A new Order must be created, and the old Order cancelled.

For the common case of rescheduling a SessionSeries or FacilityUse booking, the following steps are RECOMMENDED for the Broker:

  1. Request an OrderQuote for the new orderItems with a new UUID, using C2.

  2. Check the latest stored state of Orders from the Orders feed to ensure that old OrderItem allows allowCustomerCancellationFullRefund.

  3. Clearly communicate messaging to the Customer that explains that this transaction will involve a refund and new purchase, and so they will expect a refund for the old item and a new transaction for the new one. Information should be communicated to the Customer if any price difference exists between the two Orders, for the Customer to confirm.

  4. Request new payment details from the Customer and Authorise the amount specified by C2, then PATCH the old Order via the Order Cancellation endpoint, and if successful, complete B as normal.

The new Order is completed as described in the Simple Booking Flow, and the existing OrderItem is cancelled with the refund processed as described elsewhere.

8.6 Delivery of Terms and Conditions and Privacy Policy

The Broker MUST make the Customer aware of any termsOfService provided within the seller, broker, and bookingService (which may include Terms and Conditions and Privacy Policy) before the Customer confirms their purchase.

If requiresExplicitConsent is true for any termsOfService then the Customer MUST have explicitly acted to assent to such terms before the Customer completes B.

Since, during booking, the details of the Customer are provided to the Booking System and the Seller, the Broker MUST inform the Customer of any implications with respect to the GDPR or other data protection legislation.

Note that termsOfService may only be specified at the seller, broker or bookingService level within the Order, and may not be provided for a specific Offer or OrderItem.

Example 18: Properties for Terms and Conditions and Privacy Policy
"termsOfService": [
  {
    "@type": "Terms",
    "name": "Privacy Policy",
    "url": "https://example.com/privacy-policy",
    "requiresExplicitConsent": false
  },
  {
    "@type": "Terms",
    "name": "Terms and Conditions",
    "url": "https://example.com/terms-and-conditions",
    "requiresExplicitConsent": true
  }
]

The URLs in the example are only illustrative.

8.7 Access control

For Opportunities that require entry into a restricted building or area to participate, it is REQUIRED that OrderItem include unique access control data within accessCode and accessToken. An accessCode SHOULD be provided for manual use in the event that an accessToken fails.

Each accessCode and accessToken MUST represent unique methods of entry, and MUST NOT be duplicated in different formats. For example, multiple formats (e.g. PNG, JPEG) of the same accessToken MUST NOT be supplied.

A Broker MUST make all supplied accessTokens and accessCodes available to the Customer, with the exception of Barcodes that do not have fallback image url. Barcodes without a url MAY be ignored if the Broker is unable to process them.

8.7.1 Text-based access control

PIN codes, Order identifiers, or simply the e-mail address of the Customer are all permissible for the purposes of access control.

In keeping with the Schema.org definition of PropertyValue, the name property SHOULD be used for the name of the property, and the description property SHOULD be used for any human-readable access control code(s).

Example 19: Example of accessCode used for a PIN Code
"accessCode": [
  {
    "@type": "PropertyValue",
    "name": "PIN Code",
    "description": "1234"
  }
],
Example 20: Example of accessCode used for a booking reference
"accessCode": [
  {
    "@type": "PropertyValue",
    "name": "Booking Reference",
    "description": "123456789"
  }
],

8.7.2 Image-based access control

Images to be displayed to provide access, for example access tickets rendered by the booking system, MAY be included as an ImageObject within the accessToken array.

ImageObject MUST NOT be used for fallback images of a Barcode; the url property of the Barcode MUST be used for this purpose.

Example 21: Example of ImageObject in accessToken
"accessToken": [
  {
    "@type": "ImageObject",
    "url": "https://access.example.com/476ac24c694da79c5e33731ebbb5f1"
  }
],

8.7.3 Extension point for barcode-based access control

Access barcodes to be rendered by the Broker MAY be included as a Barcode within the accessToken array. Note that Barcode subclasses ImageObject, allowing it to contain a fallback rendered barcode image url in addition to the raw barcode details. Barcodes SHOULD include a fallback image url.

Due to the variety of barcode formats available, the specification expects the Barcode to include additional properties using a custom namespace (as specified in the [Modelling-Opportunity-Data] specification), enough to allow the barcode to be reproduced by the Broker. This will help inform future versions of the specification.

Example 22: Example of Barcode in accessToken
"accessToken": [
  {
    "@type": "Barcode",
    "url": "https://fallback.image.example.com/476ac24c694da79c5e33731ebbb5f1",
    "text": "0123456789",
    "bookwhen:codeType": "code128"
  }
],

9. Endpoints

This API has been defined around the concept of URL discovery, using the [Dataset-API-Discovery] specification as a basis. This allows resource URLs which match the naming conventions of each Booking System.

9.1 Paths and Verbs

Due to URL discovery, the paths of the endpoints below are illustrative, as the actual paths may vary provided they are consistent within an implementation.

A summary of endpoints defined by this specification is provided below:

Endpoint Name Status Returns Body Resource HTTP Verb Example Path
OrderQuote Creation C1 REQUIRED Yes OrderQuote PUT /order-quote-templates/{uuid}
OrderQuote Creation C2 REQUIRED Yes OrderQuote PUT /order-quotes/{uuid}
OrderQuote Deletion REQUIRED OrderQuote DELETE /order-quotes/{uuid}
OrderProposal Creation P OPTIONAL Yes OrderProposal PUT /order-proposals/{uuid}
OrderProposal Update OPTIONAL OrderProposal PATCH /order-proposals/{uuid}
Order Creation B REQUIRED Yes Order PUT /orders/{uuid}
Order Deletion REQUIRED Order DELETE /orders/{uuid}
Order Cancellation REQUIRED Order PATCH /orders/{uuid}
Orders RPDE Feed REQUIRED Yes Orders feed GET /orders-rpde
Order Status RECOMMENDED Yes Order GET /orders/{uuid}

Note that for simple implementation, C1 and C2 can be exactly the same endpoint.

9.1.1 Order Endpoint RESTful Semantics

Note that practically all Order endpoints above (those that relate to Order or its subclasses OrderQuote and OrderProposal) share the same UUID namespace, and can be successfully implemented with a single underlying Order record behind them all. These endpoints are distinct purely to reduce the logic required in each and to increase testability and maintainability.

An Order with a particular UUID MUST only exist as either an Order, OrderQuote or OrderProposal at a single point in time.

Hence the Order Status endpoint SHOULD return any stored Order, OrderQuote or OrderProposal with that UUID, and the Orders Feed MUST return both Order and OrderProposal items (noting that OrderQuote are not included in the Orders Feed).

To aid the reader in their semantic reasoning, from a REST perspective, there are four resources:

  • /order-quote-templates and /order-quotes - both are a collection resource of OrderQuotes. These are always ephemeral, so are created only for the duration of the PUT request, unless the Booking System chooses to persist them, in which case they exist until the Lease expires or an OrderProposal or Order is created.
  • /order-proposals - a collection resource of OrderProposals, which are deleted when a corresponding Order is created.
  • /orders - a collection resource of Orders which includes its subclasses OrderQuotes and OrderProposals

9.2 Request and Response

This section includes request and response examples for each of the endpoints specified in this API.

9.2.1 OrderQuote Creation C1

Endpoint Name Status Returns Body Resource HTTP Verb Example Path
OrderQuote Creation C1 REQUIRED Yes OrderQuote PUT /order-quote-templates/{uuid}

The C1 is an endpoint that accepts an OrderQuote without a customer object to check availability and confirm that the specific combination of OrderItems requested can be purchased.

A PUT request to this endpoint of the Booking System with an object of type OrderQuote will return an OrderQuote which represents a "dry run" of the booking, with the state of the Order nearly identical to the result of B (except it excludes customer, attendee, orderItemIntakeFormResponse and the payment identifier), without any side effects (with the exception of optional leasing).

Note that in a simple implementation that does not manage Lease state, attendee details, or additional details, C1 and C2 MAY be exactly the same endpoint that simply validates the email of the customer only if a customer value is supplied. Even for complex implementations, given the similarities between C1 and C2, a single endpoint can used to cover both.

Example 23: OrderQuote Creation C1: example request
PUT /api/order-quote-templates/e11429ea-467f-4270-ab62-e47368996fe8 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "OrderQuote",
  "brokerRole": "https://openactive.io/AgentBroker",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "seller": {
    "@type": "Organization",
    "@id": "https://example.com/api/organisations/123"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "acceptedOffer": {
        "@type": "Offer",
        "@id": "https://example.com/events/452#/offers/878"
      },
      "orderedItem": {
        "@type": "ScheduledSession",
        "@id": "https://example.com/events/452/subEvents/132"
      }
    }
  ]
}

If successful the Booking System will respond with an OrderQuote:

Example 24: OrderQuote Creation C1: example success response
HTTP/1.1 200 OK
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "OrderQuote",
  "@id": "https://example.com/api/order-quotes/e11429ea-467f-4270-ab62-e47368996fe8",
  "brokerRole": "https://openactive.io/AgentBroker",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "seller": {
    "@type": "Organization",
    "@id": "https://example.com/api/organisations/123",
    "identifier": "CRUOZWJ1",
    "name": "Better",
    "taxMode": "https://openactive.io/TaxGross",
    "legalName": "Greenwich Leisure Limited",
    "description": "A charitable social enterprise for all the community",
    "url": "https://www.better.org.uk",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.better.org.uk/images/logo.png"
    },
    "telephone": "020 3457 8700",
    "email": "[email protected]",
    "vatID": "GB 789 1234 56",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    },
    "termsOfService": [
      {
        "@type": "PrivacyPolicy",
        "name": "Privacy Policy",
        "url": "https://example.com/privacy-policy"
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions"
      }
    ]
  },
  "bookingService": {
    "@type": "BookingService",
    "name": "Playwaze",
    "url": "http://www.playwaze.com",
    "termsOfService": [
      {
        "@type": "Terms",
        "url": "https://brokerexample.com/terms.html"
      }
    ]
  },
  "lease": {
    "@type": "Lease",
    "leaseExpires": "2018-10-01T11:00:00Z"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "allowCustomerCancellationFullRefund": true,
      "unitTaxSpecification": [
        {
          "@type": "TaxChargeSpecification",
          "name": "VAT at 20%",
          "price": 1,
          "priceCurrency": "GBP",
          "rate": 0.2
        }
      ],
      "acceptedOffer": {
        "@type": "Offer",
        "@id": "https://example.com/events/452#/offers/878",
        "description": "Winger space for Speedball.",
        "name": "Speedball winger position",
        "price": 10,
        "priceCurrency": "GBP",
        "validFromBeforeStartDate": "P6D",
        "latestCancellationBeforeStartDate": "P1D"
      },
      "orderedItem": {
        "@type": "ScheduledSession",
        "@id": "https://example.com/events/452/subEvents/132",
        "identifier": 123,
        "eventStatus": "https://schema.org/EventScheduled",
        "maximumAttendeeCapacity": 30,
        "remainingAttendeeCapacity": 20,
        "startDate": "2018-10-30T11:00:00Z",
        "endDate": "2018-10-30T12:00:00Z",
        "duration": "PT1H",
        "superEvent": {
          "@type": "SessionSeries",
          "@id": "https://example.com/events/452",
          "name": "Speedball",
          "duration": "PT1H",
          "organizer": {
            "@type": "Organization",
            "name": "Central Speedball Association",
            "url": "http://www.speedball-world.com"
          },
          "location": {
            "@type": "Place",
            "url": "https://www.everyoneactive.com/centres/Middlesbrough-Sports-Village",
            "name": "Middlesbrough Sports Village",
            "identifier": "0140",
            "address": {
              "@type": "PostalAddress",
              "streetAddress": "Alan Peacock Way",
              "addressLocality": "Village East",
              "addressRegion": "Middlesbrough",
              "postalCode": "TS4 3AE",
              "addressCountry": "GB"
            },
            "geo": {
              "@type": "GeoCoordinates",
              "latitude": 54.543964,
              "longitude": -1.20978500000001
            }
          }
        }
      }
    }
  ],
  "totalPaymentDue": {
    "@type": "PriceSpecification",
    "price": 5,
    "priceCurrency": "GBP"
  },
  "totalPaymentTax": [
    {
      "@type": "TaxChargeSpecification",
      "name": "VAT at 20%",
      "price": 1,
      "priceCurrency": "GBP",
      "rate": 0.2
    }
  ]
}

If there are any issues with the OrderItems requested in the OrderQuote, the Booking System MUST respond with a 409 Conflict response (as the error is expected to be resolved, and the request resubmitted, as per [RFC2616]), with error details provided against each offending OrderItem. Note that totalPaymentDue and totalPaymentTax MUST be calculated excluding any OrderItems that include errors.

Example 25: OrderQuote Creation C1: example OrderItem error response
HTTP/1.1 409 Conflict
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "OrderQuote",
  "@id": "https://example.com/api/order-quotes/e11429ea-467f-4270-ab62-e47368996fe8",
  "brokerRole": "https://openactive.io/AgentBroker",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "seller": {
    "@type": "Organization",
    "@id": "https://example.com/api/organisations/123",
    "identifier": "CRUOZWJ1",
    "name": "Better",
    "taxMode": "https://openactive.io/TaxGross",
    "legalName": "Greenwich Leisure Limited",
    "description": "A charitable social enterprise for all the community",
    "url": "https://www.better.org.uk",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.better.org.uk/images/logo.png"
    },
    "telephone": "020 3457 8700",
    "email": "c[email protected]",
    "vatID": "GB 789 1234 56",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    },
    "termsOfService": [
      {
        "@type": "PrivacyPolicy",
        "name": "Privacy Policy",
        "url": "https://example.com/privacy-policy"
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions"
      }
    ]
  },
  "bookingService": {
    "@type": "BookingService",
    "name": "Playwaze",
    "url": "http://www.playwaze.com",
    "termsOfService": [
      {
        "@type": "Terms",
        "url": "https://brokerexample.com/terms.html"
      }
    ]
  },
  "lease": {
    "@type": "Lease",
    "leaseExpires": "2018-10-01T11:00:00Z"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "orderItemStatus": "https://openactive.io/OrderConfirmed",
      "allowCustomerCancellationFullRefund": true,
      "unitTaxSpecification": [
        {
          "@type": "TaxChargeSpecification",
          "name": "VAT at 20%",
          "price": 1,
          "priceCurrency": "GBP",
          "rate": 0.2
        }
      ],
      "acceptedOffer": {
        "@type": "Offer",
        "@id": "https://example.com/events/452#/offers/878",
        "description": "Winger space for Speedball.",
        "name": "Speedball winger position",
        "price": 10,
        "priceCurrency": "GBP",
        "validFromBeforeStartDate": "P6D",
        "latestCancellationBeforeStartDate": "P1D"
      },
      "orderedItem": {
        "@type": "ScheduledSession",
        "@id": "https://example.com/events/452/subEvents/132",
        "identifier": 123,
        "eventStatus": "https://schema.org/EventScheduled",
        "maximumAttendeeCapacity": 30,
        "remainingAttendeeCapacity": 20,
        "startDate": "2018-10-30T11:00:00Z",
        "endDate": "2018-10-30T12:00:00Z",
        "duration": "PT1H",
        "superEvent": {
          "@type": "SessionSeries",
          "@id": "https://example.com/events/452",
          "name": "Speedball",
          "duration": "PT1H",
          "organizer": {
            "@type": "Organization",
            "name": "Central Speedball Association",
            "url": "http://www.speedball-world.com"
          },
          "location": {
            "@type": "Place",
            "url": "https://www.everyoneactive.com/centres/Middlesbrough-Sports-Village",
            "name": "Middlesbrough Sports Village",
            "identifier": "0140",
            "address": {
              "@type": "PostalAddress",
              "streetAddress": "Alan Peacock Way",
              "addressLocality": "Village East",
              "addressRegion": "Middlesbrough",
              "postalCode": "TS4 3AE",
              "addressCountry": "GB"
            },
            "geo": {
              "@type": "GeoCoordinates",
              "latitude": 54.543964,
              "longitude": -1.20978500000001
            }
          }
        }
      },
      "error": [
        {
          "@type": "OpportunityIsFullError",
          "description": "There are no spaces remaining in this opportunity"
        }
      ]
    }
  ],
  "totalPaymentDue": {
    "@type": "PriceSpecification",
    "price": 0,
    "priceCurrency": "GBP"
  },
  "totalPaymentTax": [
    {
      "@type": "TaxChargeSpecification",
      "name": "VAT at 20%",
      "price": 0,
      "priceCurrency": "GBP",
      "rate": 0.2
    }
  ]
}

If there are issues with other properties of the OrderQuote outside of orderedItem, the Booking System MUST respond with a JSON-LD response which includes only the appropriate OpenBookingError and the appropriate status code.

Example 26: OrderQuote Creation C1: example failure response
HTTP/1.1 500 Internal Server Error
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "TemporarilyUnableToProduceOrderQuoteError",
  "description": "Temporary error occurred in the database"
}

9.2.2 OrderQuote Creation C2

Endpoint Name Status Returns Body Resource HTTP Verb Example Path
OrderQuote Creation C2 REQUIRED Yes OrderQuote PUT /order-quotes/{uuid}

The C2 is PUT endpoint that accepts an OrderQuote with a customer object to check availability and confirm that the specific combination of OrderItems requested can be purchased.

A PUT request to this endpoint of the Booking System with an object of type OrderQuote will return an OrderQuote which represents a "dry run" of the booking, with the state of the Order nearly identical to the result of B (except it excludes payment identifier), without any side effects (with the exception of optional leasing).

Note that the Booking System MUST NOT store any personal information of the customer provided for the OrderQuote past the expiry of any associated Lease created.

Example 27: OrderQuote Creation C2: example request
PUT /api/order-quotes/e11429ea-467f-4270-ab62-e47368996fe8 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "OrderQuote",
  "brokerRole": "https://openactive.io/AgentBroker",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "seller": {
    "@type": "Organization",
    "@id": "https://example.com/api/organisations/123"
  },
  "customer": {
    "@type": "Person",
    "email": "[email protected]",
    "telephone": "020 811 8055",
    "givenName": "Geoff",
    "familyName": "Capes"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "acceptedOffer": {
        "@type": "Offer",
        "@id": "https://example.com/events/452#/offers/878"
      },
      "orderedItem": {
        "@type": "ScheduledSession",
        "@id": "https://example.com/events/452/subEvents/132"
      }
    }
  ]
}

If successful the Booking System will respond with an OrderQuote:

Example 28: OrderQuote Creation C2: example success response
HTTP/1.1 200 OK
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "OrderQuote",
  "@id": "https://example.com/api/order-quotes/e11429ea-467f-4270-ab62-e47368996fe8",
  "brokerRole": "https://openactive.io/AgentBroker",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "seller": {
    "@type": "Organization",
    "@id": "https://example.com/api/organisations/123",
    "identifier": "CRUOZWJ1",
    "name": "Better",
    "taxMode": "https://openactive.io/TaxGross",
    "legalName": "Greenwich Leisure Limited",
    "description": "A charitable social enterprise for all the community",
    "url": "https://www.better.org.uk",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.better.org.uk/images/logo.png"
    },
    "telephone": "020 3457 8700",
    "email": "[email protected]",
    "vatID": "GB 789 1234 56",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    },
    "termsOfService": [
      {
        "@type": "PrivacyPolicy",
        "name": "Privacy Policy",
        "url": "https://example.com/privacy-policy"
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions"
      }
    ]
  },
  "customer": {
    "@type": "Person",
    "email": "[email protected]",
    "telephone": "020 811 8055",
    "givenName": "Geoff",
    "familyName": "Capes"
  },
  "bookingService": {
    "@type": "BookingService",
    "name": "Playwaze",
    "url": "http://www.playwaze.com",
    "termsOfService": [
      {
        "@type": "Terms",
        "url": "https://brokerexample.com/terms.html"
      }
    ]
  },
  "lease": {
    "@type": "Lease",
    "leaseExpires": "2018-10-01T11:00:00Z"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "allowCustomerCancellationFullRefund": true,
      "unitTaxSpecification": [
        {
          "@type": "TaxChargeSpecification",
          "name": "VAT at 20%",
          "price": 1,
          "priceCurrency": "GBP",
          "rate": 0.2
        }
      ],
      "acceptedOffer": {
        "@type": "Offer",
        "@id": "https://example.com/events/452#/offers/878",
        "description": "Winger space for Speedball.",
        "name": "Speedball winger position",
        "price": 10,
        "priceCurrency": "GBP",
        "validFromBeforeStartDate": "P6D",
        "latestCancellationBeforeStartDate": "P1D"
      },
      "orderedItem": {
        "@type": "ScheduledSession",
        "@id": "https://example.com/events/452/subEvents/132",
        "identifier": 123,
        "eventStatus": "https://schema.org/EventScheduled",
        "maximumAttendeeCapacity": 30,
        "remainingAttendeeCapacity": 20,
        "startDate": "2018-10-30T11:00:00Z",
        "endDate": "2018-10-30T12:00:00Z",
        "duration": "PT1H",
        "superEvent": {
          "@type": "SessionSeries",
          "@id": "https://example.com/events/452",
          "name": "Speedball",
          "duration": "PT1H",
          "organizer": {
            "@type": "Organization",
            "name": "Central Speedball Association",
            "url": "http://www.speedball-world.com"
          },
          "location": {
            "@type": "Place",
            "url": "https://www.everyoneactive.com/centres/Middlesbrough-Sports-Village",
            "name": "Middlesbrough Sports Village",
            "identifier": "0140",
            "address": {
              "@type": "PostalAddress",
              "streetAddress": "Alan Peacock Way",
              "addressLocality": "Village East",
              "addressRegion": "Middlesbrough",
              "postalCode": "TS4 3AE",
              "addressCountry": "GB"
            },
            "geo": {
              "@type": "GeoCoordinates",
              "latitude": 54.543964,
              "longitude": -1.20978500000001
            }
          }
        }
      }
    }
  ],
  "totalPaymentDue": {
    "@type": "PriceSpecification",
    "price": 5,
    "priceCurrency": "GBP"
  },
  "totalPaymentTax": [
    {
      "@type": "TaxChargeSpecification",
      "name": "VAT at 20%",
      "price": 1,
      "priceCurrency": "GBP",
      "rate": 0.2
    }
  ]
}

Note if the response includes orderRequiresApproval with a value of true the Broker MUST make it clear to the Customer of the additional process they are about to enter into, and once they are aware, move forward according to the Booking Flow with Approval rather than the Simple Booking Flow.

If there are any issues with the OrderItems requested in the OrderQuote, the Booking System MUST respond with a 409 Conflict response (as the error is expected to be resolved, and the request resubmitted, as per [RFC2616]), with error details provided against each offending OrderItem. Note that totalPaymentDue and totalPaymentTax MUST be calculated excluding any OrderItems that include errors.

Example 29: OrderQuote Creation C2: example OrderItem error response
HTTP/1.1 409 Conflict
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "OrderQuote",
  "@id": "https://example.com/api/order-quotes/e11429ea-467f-4270-ab62-e47368996fe8",
  "brokerRole": "https://openactive.io/AgentBroker",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "seller": {
    "@type": "Organization",
    "@id": "https://example.com/api/organisations/123",
    "identifier": "CRUOZWJ1",
    "name": "Better",
    "taxMode": "https://openactive.io/TaxGross",
    "legalName": "Greenwich Leisure Limited",
    "description": "A charitable social enterprise for all the community",
    "url": "https://www.better.org.uk",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.better.org.uk/images/logo.png"
    },
    "telephone": "020 3457 8700",
    "email": "[email protected]",
    "vatID": "GB 789 1234 56",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    },
    "termsOfService": [
      {
        "@type": "PrivacyPolicy",
        "name": "Privacy Policy",
        "url": "https://example.com/privacy-policy"
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions"
      }
    ]
  },
  "customer": {
    "@type": "Person",
    "email": "[email protected]",
    "telephone": "020 811 8055",
    "givenName": "Geoff",
    "familyName": "Capes"
  },
  "bookingService": {
    "@type": "BookingService",
    "name": "Playwaze",
    "url": "http://www.playwaze.com",
    "termsOfService": [
      {
        "@type": "Terms",
        "url": "https://brokerexample.com/terms.html"
      }
    ]
  },
  "lease": {
    "@type": "Lease",
    "leaseExpires": "2018-10-01T11:00:00Z"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "orderItemStatus": "https://openactive.io/OrderConfirmed",
      "allowCustomerCancellationFullRefund": true,
      "unitTaxSpecification": [
        {
          "@type": "TaxChargeSpecification",
          "name": "VAT at 20%",
          "price": 1,
          "priceCurrency": "GBP",
          "rate": 0.2
        }
      ],
      "acceptedOffer": {
        "@type": "Offer",
        "@id": "https://example.com/events/452#/offers/878",
        "description": "Winger space for Speedball.",
        "name": "Speedball winger position",
        "price": 10,
        "priceCurrency": "GBP",
        "validFromBeforeStartDate": "P6D",
        "latestCancellationBeforeStartDate": "P1D"
      },
      "orderedItem": {
        "@type": "ScheduledSession",
        "@id": "https://example.com/events/452/subEvents/132",
        "identifier": 123,
        "eventStatus": "https://schema.org/EventScheduled",
        "maximumAttendeeCapacity": 30,
        "remainingAttendeeCapacity": 20,
        "startDate": "2018-10-30T11:00:00Z",
        "endDate": "2018-10-30T12:00:00Z",
        "duration": "PT1H",
        "superEvent": {
          "@type": "SessionSeries",
          "@id": "https://example.com/events/452",
          "name": "Speedball",
          "duration": "PT1H",
          "organizer": {
            "@type": "Organization",
            "name": "Central Speedball Association",
            "url": "http://www.speedball-world.com"
          },
          "location": {
            "@type": "Place",
            "url": "https://www.everyoneactive.com/centres/Middlesbrough-Sports-Village",
            "name": "Middlesbrough Sports Village",
            "identifier": "0140",
            "address": {
              "@type": "PostalAddress",
              "streetAddress": "Alan Peacock Way",
              "addressLocality": "Village East",
              "addressRegion": "Middlesbrough",
              "postalCode": "TS4 3AE",
              "addressCountry": "GB"
            },
            "geo": {
              "@type": "GeoCoordinates",
              "latitude": 54.543964,
              "longitude": -1.20978500000001
            }
          }
        }
      },
      "error": [
        {
          "@type": "OpportunityIsFullError",
          "description": "There are no spaces remaining in this opportunity"
        }
      ]
    }
  ],
  "totalPaymentDue": {
    "@type": "PriceSpecification",
    "price": 0,
    "priceCurrency": "GBP"
  },
  "totalPaymentTax": [
    {
      "@type": "TaxChargeSpecification",
      "name": "VAT at 20%",
      "price": 0,
      "priceCurrency": "GBP",
      "rate": 0.2
    }
  ]
}

If there are issues with other properties of the OrderQuote outside of orderedItem, the Booking System MUST respond with a JSON-LD response which includes only the appropriate OpenBookingError and the appropriate status code.

Example 30: OrderQuote Creation C2: example failure response
HTTP/1.1 500 Internal Server Error
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "TemporarilyUnableToProduceOrderQuoteError",
  "description": "Temporary error occurred in the database"
}

9.2.3 OrderQuote Deletion

Endpoint Name Status Returns Body Resource HTTP Verb Example Path
OrderQuote Deletion REQUIRED OrderQuote DELETE /order-quotes/{uuid}

Deleting an OrderQuote releases any Lease associated with it, which the Broker SHOULD do as a courtesy if the Customer has abandoned their journey. An OrderQuote Deletion request MUST simply return a 204 success status in all cases (including cases where the Booking Systems has not implemented leasing), as the OrderQuote itself is ephemeral.

Example 31: OrderQuote Deletion: example request
DELETE /api/order-quotes/e11429ea-467f-4270-ab62-e47368996fe8 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1
Example 32: OrderQuote Deletion: example response
HTTP/1.1 204 No Content
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

9.2.4 OrderProposal Creation P

Endpoint Name Status Returns Body Resource HTTP Verb Example Path
OrderProposal Creation P OPTIONAL Yes OrderProposal PUT /order-proposals/{uuid}

P is a PUT endpoint that creates and returns the submitted OrderProposal with an orderProposalStatus of https://openactive.io/AwaitingSellerConfirmation. The details submitted are identical to those required for B with the addition of an optional lease.

Example 33: OrderProposal Creation: example request
PUT /api/order-proposals/e11429ea-467f-4270-ab62-e47368996fe8 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "OrderProposal",
  "brokerRole": "https://openactive.io/AgentBroker",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "seller": {
    "@type": "Organization",
    "@id": "https://example.com/api/organisations/123"
  },
  "customer": {
    "@type": "Person",
    "email": "[email protected]",
    "telephone": "020 811 8055",
    "givenName": "Geoff",
    "familyName": "Capes"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "acceptedOffer": {
        "@type": "Offer",
        "@id": "https://example.com/events/452#/offers/878"
      },
      "orderedItem": {
        "@type": "ScheduledSession",
        "@id": "https://example.com/events/452/subEvents/132"
      }
    }
  ],
  "totalPaymentDue": {
    "@type": "PriceSpecification",
    "price": 5,
    "priceCurrency": "GBP"
  },
  "payment": {
    "@type": "Payment",
    "name": "AcmeBroker Points",
    "identifier": "1234567890npduy2f"
  }
}

If successful the Booking System will respond with an OrderProposal:

Example 34: OrderProposal Creation: example success response
HTTP/1.1 201 Created
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1
Location: /api/order-proposals/e11429ea-467f-4270-ab62-e47368996fe8

{
  "@context": "https://openactive.io/",
  "@type": "OrderProposal",
  "@id": "https://example.com/api/order-proposals/e11429ea-467f-4270-ab62-e47368996fe8",
  "orderNumber": "AB000001",
  "orderProposalVersion": "https://example.com/api/order-proposals/e11429ea-467f-4270-ab62-e47368996fe8/version/8eb1a6ce-3f5b-40b0-87a7-bddb4c5518bd",
  "brokerRole": "https://openactive.io/AgentBroker",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "seller": {
    "@type": "Organization",
    "@id": "https://example.com/api/organisations/123",
    "identifier": "CRUOZWJ1",
    "name": "Better",
    "taxMode": "https://openactive.io/TaxGross",
    "legalName": "Greenwich Leisure Limited",
    "description": "A charitable social enterprise for all the community",
    "url": "https://www.better.org.uk",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.better.org.uk/images/logo.png"
    },
    "telephone": "020 3457 8700",
    "email": "[email protected]",
    "vatID": "GB 789 1234 56",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    },
    "termsOfService": [
      {
        "@type": "PrivacyPolicy",
        "name": "Privacy Policy",
        "url": "https://example.com/privacy-policy"
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions"
      }
    ]
  },
  "customer": {
    "@type": "Person",
    "email": "[email protected]",
    "telephone": "020 811 8055",
    "givenName": "Geoff",
    "familyName": "Capes"
  },
  "bookingService": {
    "@type": "BookingService",
    "name": "Playwaze",
    "url": "http://www.playwaze.com",
    "termsOfService": [
      {
        "@type": "Terms",
        "url": "https://brokerexample.com/terms.html"
      }
    ]
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "@id": "https://example.com/api/orders/123e4567-e89b-12d3-a456-426655440000/order-items/1234",
      "orderItemStatus": "https://openactive.io/OrderConfirmed",
      "allowCustomerCancellationFullRefund": true,
      "unitTaxSpecification": [
        {
          "@type": "TaxChargeSpecification",
          "name": "VAT at 20%",
          "price": 1,
          "priceCurrency": "GBP",
          "rate": 0.2
        }
      ],
      "acceptedOffer": {
        "@type": "Offer",
        "@id": "https://example.com/events/452#/offers/878",
        "description": "Winger space for Speedball.",
        "name": "Speedball winger position",
        "price": 10,
        "priceCurrency": "GBP",
        "validFromBeforeStartDate": "P6D",
        "latestCancellationBeforeStartDate": "P1D"
      },
      "orderedItem": {
        "@type": "ScheduledSession",
        "@id": "https://example.com/events/452/subEvents/132",
        "identifier": 123,
        "eventStatus": "https://schema.org/EventScheduled",
        "startDate": "2018-10-30T11:00:00Z",
        "endDate": "2018-10-30T12:00:00Z",
        "duration": "PT1H",
        "superEvent": {
          "@type": "SessionSeries",
          "@id": "https://example.com/events/452",
          "name": "Speedball",
          "organizer": {
            "@type": "Organization",
            "name": "Central Speedball Association",
            "url": "http://www.speedball-world.com"
          },
          "location": {
            "@type": "Place",
            "url": "https://www.everyoneactive.com/centres/Middlesbrough-Sports-Village",
            "name": "Middlesbrough Sports Village",
            "identifier": "0140",
            "address": {
              "@type": "PostalAddress",
              "streetAddress": "Alan Peacock Way",
              "addressLocality": "Village East",
              "addressRegion": "Middlesbrough",
              "postalCode": "TS4 3AE",
              "addressCountry": "GB"
            },
            "geo": {
              "@type": "GeoCoordinates",
              "latitude": 54.543964,
              "longitude": -1.20978500000001
            }
          }
        }
      },
      "accessToken": [
        {
          "@type": "Barcode",
          "text": "0123456789"
        }
      ]
    }
  ],
  "totalPaymentDue": {
    "@type": "PriceSpecification",
    "price": 5,
    "priceCurrency": "GBP"
  },
  "totalPaymentTax": [
    {
      "@type": "TaxChargeSpecification",
      "name": "VAT at 20%",
      "price": 1,
      "priceCurrency": "GBP",
      "rate": 0.2
    }
  ],
  "payment": {
    "@type": "Payment",
    "name": "AcmeBroker Points",
    "identifier": "1234567890npduy2f"
  },
  "lease": {
    "@type": "Lease",
    "leaseExpires": "2018-10-01T11:00:00Z"
  }
}

If there are issues with any properties of the OrderProposal, including the orderedItem, the Booking System MUST respond with an error response which includes only the appropriate OpenBookingError and the appropriate status code. The Broker is expected to retry the OrderQuote to get specific orderedItem level errors.

Example 35: OrderProposal Creation: example failure response
HTTP/1.1 400 Bad Request
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "IncompleteBrokerDetailsError",
  "description": "Only 'https://openactive.io/CustomerRejected' is permitted for this property."
}

Note that any validation errors raised at P regarding any property values MUST also be raised at C2 (and ideally also C1 if relevant), so it should be very unusual to receive an error at P.

9.2.5 OrderProposal Update

Endpoint Name Status Returns Body Resource HTTP Verb Example Path
OrderProposal Update OPTIONAL OrderProposal PATCH /order-proposals/{uuid}

The endpoint accepts a simple PATCH containing either or both of orderProposalStatus and orderCustomerNote.

Example 36: OrderProposal Update: example request
PATCH /api/order-proposals/e11429ea-467f-4270-ab62-e47368996fe8 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "OrderProposal",
  "orderProposalStatus": "https://openactive.io/CustomerRejected",
  "orderCustomerNote": "Sorry I've actually made other plans, hope you find someone!"
}
Example 37: OrderProposal Update: example response
HTTP/1.1 204 No Content
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

A PATCH to update orderProposalStatus MUST have the value https://openactive.io/CustomerRejected, as that is the only permissible value based on the logic defined in this version of the specification. If another value for orderProposalStatus is specified the endpoint MUST return an PatchNotAllowedOnProperty error with its status code.

Example 38: OrderProposal Update: example property error response
HTTP/1.1 400 Bad Request
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "PatchNotAllowedOnProperty",
  "instance": "https://openactive.io/orderProposalStatus",
  "description": "Only 'https://openactive.io/CustomerRejected' is permitted for this property."
}

If the PATCH request includes any properties other than type, @context, orderProposalStatus and orderCustomerNote (excluding any properties with a custom namespace), the endpoint MUST return a PatchContainsExcessiveProperties error with its status code.

Example 39: OrderProposal Update: example property error response for excessive properties
HTTP/1.1 400 Bad Request
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "PatchContainsExcessiveProperties",
  "description": "PATCH includes unexpected properties that are not permitted."
}

9.2.6 Order Creation B

Endpoint Name Status Returns Body Resource HTTP Verb Example Path
Order Creation B REQUIRED Yes Order PUT /orders/{uuid}

A PUT request to the Order Creation endpoint of the Booking System with an object of type Order will create the Order and complete the booking from the point of the view of the Booking System.

Example 40: Order Creation: example request
PUT /api/orders/e11429ea-467f-4270-ab62-e47368996fe8 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "Order",
  "brokerRole": "https://openactive.io/AgentBroker",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "seller": {
    "@type": "Organization",
    "@id": "https://example.com/api/organisations/123"
  },
  "customer": {
    "@type": "Person",
    "email": "[email protected]",
    "telephone": "020 811 8055",
    "givenName": "Geoff",
    "familyName": "Capes"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "acceptedOffer": {
        "@type": "Offer",
        "@id": "https://example.com/events/452#/offers/878"
      },
      "orderedItem": {
        "@type": "ScheduledSession",
        "@id": "https://example.com/events/452/subEvents/132"
      }
    }
  ],
  "totalPaymentDue": {
    "@type": "PriceSpecification",
    "price": 5,
    "priceCurrency": "GBP"
  },
  "payment": {
    "@type": "Payment",
    "name": "AcmeBroker Points",
    "identifier": "1234567890npduy2f"
  }
}

If successful the server will respond with the location of a newly created Order resource.

For the Order response and the Orders feed, the OrderItems MUST include a unique id. This allows OrderItems to be referenced in subsequent PATCH calls once the Order has been created.

B responses MUST reflect back properties provided by the Booking System, for example:

Example 41: Order Creation: example response
HTTP/1.1 201 Created
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1
Location: /api/orders/e11429ea-467f-4270-ab62-e47368996fe8

{
  "@context": "https://openactive.io/",
  "@type": "Order",
  "@id": "https://example.com/api/orders/e11429ea-467f-4270-ab62-e47368996fe8",
  "orderNumber": "AB000001",
  "brokerRole": "https://openactive.io/AgentBroker",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "customer": {
    "@type": "Person",
    "email": "[email protected]",
    "telephone": "020 811 8055",
    "givenName": "Geoff",
    "familyName": "Capes"
  },
  "seller": {
    "@type": "Organization",
    "@id": "https://example.com/api/organisations/123",
    "identifier": "CRUOZWJ1",
    "name": "Better",
    "taxMode": "https://openactive.io/TaxGross",
    "legalName": "Greenwich Leisure Limited",
    "description": "A charitable social enterprise for all the community",
    "url": "https://www.better.org.uk",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.better.org.uk/images/logo.png"
    },
    "telephone": "020 3457 8700",
    "email": "[email protected]",
    "vatID": "GB 789 1234 56",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    },
    "termsOfService": [
      {
        "@type": "PrivacyPolicy",
        "name": "Privacy Policy",
        "url": "https://example.com/privacy-policy"
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions"
      }
    ]
  },
  "bookingService": {
    "@type": "BookingService",
    "name": "Playwaze",
    "url": "http://www.playwaze.com",
    "termsOfService": [
      {
        "@type": "Terms",
        "url": "https://brokerexample.com/terms.html"
      }
    ]
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "@id": "https://example.com/api/orders/123e4567-e89b-12d3-a456-426655440000/order-items/1234",
      "orderItemStatus": "https://openactive.io/OrderConfirmed",
      "allowCustomerCancellationFullRefund": true,
      "unitTaxSpecification": [
        {
          "@type": "TaxChargeSpecification",
          "name": "VAT at 20%",
          "price": 1,
          "priceCurrency": "GBP",
          "rate": 0.2
        }
      ],
      "acceptedOffer": {
        "@type": "Offer",
        "@id": "https://example.com/events/452#/offers/878",
        "description": "Winger space for Speedball.",
        "name": "Speedball winger position",
        "price": 10,
        "priceCurrency": "GBP",
        "validFromBeforeStartDate": "P6D",
        "latestCancellationBeforeStartDate": "P1D"
      },
      "orderedItem": {
        "@type": "ScheduledSession",
        "@id": "https://example.com/events/452/subEvents/132",
        "identifier": 123,
        "eventStatus": "https://schema.org/EventScheduled",
        "startDate": "2018-10-30T11:00:00Z",
        "endDate": "2018-10-30T12:00:00Z",
        "duration": "PT1H",
        "superEvent": {
          "@type": "SessionSeries",
          "@id": "https://example.com/events/452",
          "name": "Speedball",
          "organizer": {
            "@type": "Organization",
            "name": "Central Speedball Association",
            "url": "http://www.speedball-world.com"
          },
          "location": {
            "@type": "Place",
            "url": "https://www.everyoneactive.com/centres/Middlesbrough-Sports-Village",
            "name": "Middlesbrough Sports Village",
            "identifier": "0140",
            "address": {
              "@type": "PostalAddress",
              "streetAddress": "Alan Peacock Way",
              "addressLocality": "Village East",
              "addressRegion": "Middlesbrough",
              "postalCode": "TS4 3AE",
              "addressCountry": "GB"
            },
            "geo": {
              "@type": "GeoCoordinates",
              "latitude": 54.543964,
              "longitude": -1.20978500000001
            }
          }
        }
      },
      "accessToken": [
        {
          "@type": "Barcode",
          "text": "0123456789"
        }
      ]
    }
  ],
  "totalPaymentDue": {
    "@type": "PriceSpecification",
    "price": 5,
    "priceCurrency": "GBP"
  },
  "totalPaymentTax": [
    {
      "@type": "TaxChargeSpecification",
      "name": "VAT at 20%",
      "price": 1,
      "priceCurrency": "GBP",
      "rate": 0.2
    }
  ],
  "payment": {
    "@type": "Payment",
    "name": "AcmeBroker Points",
    "identifier": "1234567890npduy2f"
  }
}

Technical or conformance issues with any properties of the Order, including in an orderedItem, MUST cause the Booking System to respond with only the appropriate OpenBookingError and associated status code. Following an UnableToProcessOrderItemError or OpportunityHasInsufficientCapacityError, the Broker is expected to retry the OrderQuote at C2 to get specific orderedItem level errors. Any validation errors raised at B regarding any property values MUST also be raised at C2 (and ideally also C1 if relevant), so it should be very unusual to receive an error at B.

Example 42: Order Creation: example failure response
HTTP/1.1 409 Conflict
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "UnableToProcessOrderItemError",
  "description": "An error occurred while processing the items within this booking."
}

For the Booking Flow with Approval specifically, the Broker decides to proceed from A to B by supplying a minimal Order to B that contains just the orderProposalVersion and any additional payment data if reauthorisation was required.

Example 43: Order Creation: example request from OrderProposal
PUT /api/orders/e11429ea-467f-4270-ab62-e47368996fe8 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "Order",
  "orderProposalVersion": "https://example.com/api/order-proposals/e11429ea-467f-4270-ab62-e47368996fe8/version/8eb1a6ce-3f5b-40b0-87a7-bddb4c5518bd",
  "payment": {
    "@type": "Payment",
    "name": "AcmeBroker Points",
    "identifier": "1234567890npduy2f"
  }
}

9.2.7 Order Deletion

Endpoint Name Status Returns Body Resource HTTP Verb Example Path
Order Deletion REQUIRED Order DELETE /orders/{uuid}

Deleting an Order allows for the whole Booking Flow to be reversed for fatal error and testing scenarios.

When an Order is deleted the Booking System SHOULD hard delete it as though it had not been created in the first place, but MAY soft delete it providing all related personal data is purged. The audit for the deletion is guaranteed to be stored in the Broker in all cases.

The Broker MUST NOT use Order Deletion for cancellation, but instead only use it in the unusual case of a fatal error or testing. An appropriate approach for cancellation is defined below.

The corresponding Order MUST be marked as deleted in the Orders feed.

Example 44: Order Deletion: example request
DELETE /api/orders/e11429ea-467f-4270-ab62-e47368996fe8 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1

An Order Deletion request MUST simply return a 204 success status when an Order is successfully deleted.

Example 45: Order Deletion: example success response
HTTP/1.1 204 No Content
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

An Order Deletion request MUST return a 404 status when the specified Order is not recognised, and SHOULD return a NotFoundError response.

Example 46: Order Deletion: example not found response
HTTP/1.1 404 Not Found
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "NotFoundError",
  "description": "This Order does not exist."
}

9.2.8 Order Cancellation

Endpoint Name Status Returns Body Resource HTTP Verb Example Path
Order Cancellation REQUIRED Order PATCH /orders/{uuid}

Cancellation of an Order may be requested by the Customer. OrderItems omitted from the PATCH request MUST be ignored.

Example 47: Order Cancellation: example Order request
PATCH /api/orders/e11429ea-467f-4270-ab62-e47368996fe8 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "Order",
  "orderedItem": [
    {
      "@type": "OrderItem",
      "@id": "https://example.com/api/orders/123e4567-e89b-12d3-a456-426655440000/order-items/1234",
      "orderItemStatus": "https://openactive.io/CustomerCancelled"
    }
  ]
}

The cancellation request succeeds and fails atomically.

Example 48: Order Cancellation: example success response
HTTP/1.1 204 No Content
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1
Example 49: Order Cancellation: example error response
HTTP/1.1 400 Bad Request
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "CancellationNotPermittedError",
  "description": "The horse has already been fed, and cannot be put back in the box."
}

A PATCH to update orderItemStatus MUST have the value https://openactive.io/CustomerCancelled, as that is the only permissible value based on the logic defined in this version of the specification. If another value for orderItemStatus is specified the endpoint MUST return an PatchNotAllowedOnProperty error with its status code.

Example 50: Order Cancellation: example property error response
HTTP/1.1 400 Bad Request
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "PatchNotAllowedOnProperty",
  "instance": "https://openactive.io/orderItemStatus",
  "description": "Only 'https://openactive.io/CustomerCancelled' is permitted for this property."
}

If the PATCH request includes any properties other than type, @context, id, orderedItem and orderItemStatus (excluding any properties with a custom namespace), the endpoint MUST return a PatchContainsExcessiveProperties error with its status code.

Example 51: Order Cancellation: example property error response for excessive properties
HTTP/1.1 400 Bad Request
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "PatchContainsExcessiveProperties",
  "description": "PATCH includes unexpected properties that are not permitted."
}

9.2.9 Orders RPDE feed

Endpoint Name Status Returns Body Resource HTTP Verb Example Path
Orders feed REQUIRED Yes Orders feed GET /orders-rpde

This feed is described at length in the Orders feed section.

An illustrative request and response is provided here.

Example 52: Order RPDE Feed: example request
GET /api/orders-rpde HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1
Example 53: Order RPDE Feed: example response
HTTP/1.1 200 OK
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "next": "/api/orders-rpde?afterTimestamp=1521565719&afterId=e11429ea-467f-4270-ab62-e47368996fe8",
  "items": [
    {
      "state": "updated",
      "kind": "Order",
      "@id": "e11429ea-467f-4270-ab62-e47368996fe8",
      "modified": 1521565719,
      "data": {
        "@context": "https://openactive.io/",
        "@type": "Order",
        "@id": "https://example.com/api/orders/e11429ea-467f-4270-ab62-e47368996fe8",
        "seller": {
          "@type": "Organization",
          "@id": "https://example.com/api/organisations/123",
          "identifier": "CRUOZWJ1",
          "name": "Better",
          "taxMode": "https://openactive.io/TaxGross",
          "legalName": "Greenwich Leisure Limited",
          "description": "A charitable social enterprise for all the community",
          "url": "https://www.better.org.uk",
          "logo": {
            "@type": "ImageObject",
            "url": "http://data.better.org.uk/images/logo.png"
          },
          "telephone": "020 3457 8700",
          "email": "[email protected]",
          "vatID": "GB 789 1234 56",
          "address": {
            "@type": "PostalAddress",
            "streetAddress": "Alan Peacock Way",
            "addressLocality": "Village East",
            "addressRegion": "Middlesbrough",
            "postalCode": "TS4 3AE",
            "addressCountry": "GB"
          },
          "termsOfService": [
            {
              "@type": "PrivacyPolicy",
              "name": "Privacy Policy",
              "url": "https://example.com/privacy-policy"
            },
            {
              "@type": "TermsOfUse",
              "name": "Terms and Conditions",
              "url": "https://example.com/terms-and-conditions"
            }
          ]
        },
        "orderedItem": [
          {
            "@type": "OrderItem",
            "@id": "https://example.com/api/orders/123e4567-e89b-12d3-a456-426655440000/order-items/1234",
            "orderItemStatus": "https://openactive.io/OrderConfirmed",
            "allowCustomerCancellationFullRefund": true,
            "unitTaxSpecification": [
              {
                "@type": "TaxChargeSpecification",
                "name": "VAT at 20%",
                "price": 1,
                "priceCurrency": "GBP",
                "rate": 0.2
              }
            ],
            "acceptedOffer": {
              "@type": "Offer",
              "@id": "https://example.com/events/452#/offers/878",
              "description": "Winger space for Speedball.",
              "name": "Speedball winger position",
              "price": 10,
              "priceCurrency": "GBP",
              "validFromBeforeStartDate": "P6D",
              "latestCancellationBeforeStartDate": "P1D"
            },
            "orderedItem": {
              "@type": "ScheduledSession",
              "@id": "https://example.com/events/452/subEvents/132"
            }
          }
        ],
        "totalPaymentDue": {
          "@type": "PriceSpecification",
          "price": 5,
          "priceCurrency": "GBP"
        },
        "totalPaymentTax": [
          {
            "@type": "TaxChargeSpecification",
            "name": "VAT at 20%",
            "price": 1,
            "priceCurrency": "GBP",
            "rate": 0.2
          }
        ]
      }
    }
  ]
}

9.2.10 Order Status

Endpoint Name Status Returns Body Resource HTTP Verb Example Path
Order Status RECOMMENDED Yes Order GET /orders/{uuid}

A Booking System SHOULD provide an endpoint to allow a Broker to retrieve complete 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, the Broker MUST keep its own record of Orders as described elsewhere in this specification, and not rely on this RECOMMENDED Order Status endpoint.

Example 54: Order Status: example request
GET /api/orders/e11429ea-467f-4270-ab62-e47368996fe8 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1
Example 55: Order Status: example response
HTTP/1.1 200 OK
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1
Location: /api/orders/e11429ea-467f-4270-ab62-e47368996fe8

{
  "@context": "https://openactive.io/",
  "@type": "Order",
  "@id": "https://example.com/api/orders/e11429ea-467f-4270-ab62-e47368996fe8",
  "brokerRole": "https://openactive.io/AgentBroker",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "customer": {
    "@type": "Person",
    "email": "[email protected]",
    "telephone": "020 811 8055",
    "givenName": "Geoff",
    "familyName": "Capes"
  },
  "seller": {
    "@type": "Organization",
    "@id": "https://example.com/api/organisations/123",
    "identifier": "CRUOZWJ1",
    "name": "Better",
    "taxMode": "https://openactive.io/TaxGross",
    "legalName": "Greenwich Leisure Limited",
    "description": "A charitable social enterprise for all the community",
    "url": "https://www.better.org.uk",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.better.org.uk/images/logo.png"
    },
    "telephone": "020 3457 8700",
    "email": "[email protected]",
    "vatID": "GB 789 1234 56",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    },
    "termsOfService": [
      {
        "@type": "PrivacyPolicy",
        "name": "Privacy Policy",
        "url": "https://example.com/privacy-policy"
      },
      {
        "@type": "TermsOfUse",
        "name": "Terms and Conditions",
        "url": "https://example.com/terms-and-conditions"
      }
    ]
  },
  "bookingService": {
    "@type": "BookingService",
    "name": "Playwaze",
    "url": "http://www.playwaze.com",
    "termsOfService": [
      {
        "@type": "Terms",
        "url": "https://brokerexample.com/terms.html"
      }
    ]
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "@id": "https://example.com/api/orders/123e4567-e89b-12d3-a456-426655440000/order-items/1234",
      "orderItemStatus": "https://openactive.io/OrderConfirmed",
      "allowCustomerCancellationFullRefund": true,
      "unitTaxSpecification": [
        {
          "@type": "TaxChargeSpecification",
          "name": "VAT at 20%",
          "price": 1,
          "priceCurrency": "GBP",
          "rate": 0.2
        }
      ],
      "acceptedOffer": {
        "@type": "Offer",
        "@id": "https://example.com/events/452#/offers/878",
        "description": "Winger space for Speedball.",
        "name": "Speedball winger position",
        "price": 10,
        "priceCurrency": "GBP",
        "validFromBeforeStartDate": "P6D",
        "latestCancellationBeforeStartDate": "P1D"
      },
      "orderedItem": {
        "@type": "ScheduledSession",
        "@id": "https://example.com/events/452/subEvents/132",
        "identifier": 123,
        "eventStatus": "https://schema.org/EventScheduled",
        "startDate": "2018-10-30T11:00:00Z",
        "endDate": "2018-10-30T12:00:00Z",
        "duration": "PT1H",
        "superEvent": {
          "@type": "SessionSeries",
          "@id": "https://example.com/events/452",
          "name": "Speedball",
          "organizer": {
            "@type": "Organization",
            "name": "Central Speedball Association",
            "url": "http://www.speedball-world.com"
          },
          "location": {
            "@type": "Place",
            "url": "https://www.everyoneactive.com/centres/Middlesbrough-Sports-Village",
            "name": "Middlesbrough Sports Village",
            "identifier": "0140",
            "address": {
              "@type": "PostalAddress",
              "streetAddress": "Alan Peacock Way",
              "addressLocality": "Village East",
              "addressRegion": "Middlesbrough",
              "postalCode": "TS4 3AE",
              "addressCountry": "GB"
            },
            "geo": {
              "@type": "GeoCoordinates",
              "latitude": 54.543964,
              "longitude": -1.20978500000001
            }
          }
        }
      },
      "accessToken": [
        {
          "@type": "Barcode",
          "text": "0123456789"
        }
      ]
    }
  ],
  "totalPaymentDue": {
    "@type": "PriceSpecification",
    "price": 5,
    "priceCurrency": "GBP"
  },
  "totalPaymentTax": [
    {
      "@type": "TaxChargeSpecification",
      "name": "VAT at 20%",
      "price": 1,
      "priceCurrency": "GBP",
      "rate": 0.2
    }
  ],
  "payment": {
    "@type": "Payment",
    "name": "AcmeBroker Points",
    "identifier": "1234567890npduy2f"
  }
}

9.2.11 URL discovery

Discovery of URLs is an implicit part of the design of this specification, and 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 Systems to advertise the URL for further activity in the booking workflow at the point of need or via the [Dataset-API-Discovery] specification. For example they MUST advertise the URL for the Order resource in the Orders Orders feed by including the id property within the Order.

10. Model

These models are designed to be compatible with [Modelling-Opportunity-Data]. Conformance criteria for Opportunity Data used in the context of the Booking API are at some points stricter or more relaxed than are defined in the [Modelling-Opportunity-Data] specification itself.

10.1 Order Model

10.1.1 schema:Order

Property Broker Request Booking System Response Orders feed Type Notes
@type REQUIRED REQUIRED REQUIRED schema:Text Order
@id - REQUIRED REQUIRED URI A URI providing a unique identifier for the resource, which MUST match the PUT URL containing the UUID. This is NOT REQUIRED in PUT or PATCH requests.
schema:identifier - OPTIONAL - schema:Text The UUID of the Order
schema:seller - REQUIRED REQUIRED schema:Organization or a schema:Person The organisation or person providing access to events or facilities via a Booking System. e.g. a leisure provider or independent running a yoga classes.
oa:taxCalculationExcluded - REQUIRED REQUIRED schema:Boolean Set to true when business-to-business tax calculation is required by the seller or brokerRole settings, but not supported by the Broker.
schema:broker REQUIRED REQUIRED - schema:Organization 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. If brokerRole is set to https://openactive.io/NoBroker this is NOT REQUIRED.
oa:brokerRole REQUIRED REQUIRED - oa:BrokerType Either https://openactive.io/AgentBroker, https://openactive.io/ResellerBroker or https://openactive.io/NoBroker, as agreed in advance between the Broker and Seller.
schema:orderNumber - OPTIONAL - schema:Text The Customer-facing identifier of the Order.
schema:customer REQUIRED REQUIRED - schema:Organization or a schema:Person The person or organization purchasing the Order.
oa:bookingService - REQUIRED - oa:BookingService Details about the Booking System
schema:orderedItem REQUIRED REQUIRED REQUIRED Array of schema:OrderItem The items that constitute the Order
schema:totalPaymentDue REQUIRED REQUIRED REQUIRED schema:PriceSpecification The total price of the Order, which includes or excludes tax depending on the taxMode.
oa:totalPaymentTax - REQUIRED REQUIRED Array of oa:TaxChargeSpecification Breakdown of tax payable for the Order.
oa:payment RECOMMENDED OPTIONAL - oa:Payment The payment associated with the Order by the Broker. It is REQUIRED for cases where a payment has been taken.
oa:orderCreationStatus - - - oa:OrderCreationStatus Can include https://openactive.io/OrderCreationPaymentAuthorized, https://openactive.io/OrderCreationPaymentDue, https://openactive.io/OrderCreationPaymentCaptured, https://openactive.io/OrderCreationComplete. This property is internal to the Broker in this version of the specification.
oa:orderProposalVersion - REQUIRED - schema:URL The unique URL representing this version of the OrderProposal, or the version of the OrderProposal to which this Order is related.

10.1.2 oa:OrderQuote

OrderQuote subclasses Order, and includes all of the above properties as specified for Order with the addition of oa:lease, oa:orderRequiresApproval, and with totalPaymentDue only required in the response.

Property Broker Request Booking System Response Type Notes
@type REQUIRED REQUIRED schema:Text OrderQuote
oa:lease - OPTIONAL oa:Lease The Lease on the OrderItems which lasts for the duration specified by the Booking System.
schema:totalPaymentDue - REQUIRED REQUIRED schema:PriceSpecification
oa:orderRequiresApproval - REQUIRED schema:Boolean Whether the Booking Flow with Approval MUST be used to book the set of OrderItems included. MUST be true if any of the OrderItems require approval.

10.1.3 oa:OrderProposal

OrderProposal subclasses OrderQuote, and includes all of the above properties as specified for OrderQuote, with the further addition of oa:orderProposalStatus, oa:orderSellerNote, oa:orderCustomerNote, and with totalPaymentDue required in the request and response, and Orders feed.

Property Broker Request Booking System Response Type Notes
@type REQUIRED REQUIRED schema:Text OrderQuote
schema:totalPaymentDue REQUIRED REQUIRED REQUIRED schema:PriceSpecification
oa:orderProposalStatus REQUIRED REQUIRED Array of oa:OrderProposalStatus Can include https://openactive.io/AwaitingSellerConfirmation, https://openactive.io/SellerAccepted, https://openactive.io/SellerRejected, https://openactive.io/CustomerRejected
oa:orderSellerNote RECOMMENDED RECOMMENDED - schema:Text
oa:orderCustomerNote RECOMMENDED RECOMMENDED - schema:Text

10.1.4 schema:Organization or schema:Person for seller

The seller MUST be a schema:Organization or schema:Person with the following properties, in order for tax receipts to be successfully generated.

The seller is specified with each Order to account for scenarios where multiple Seller brands are in use.

In line with [Modelling-Opportunity-Data], both the schema:Organization or schema:Person types support a common set of properties that capture the basic information required.

Property Broker Request Booking System Response Type Notes
@type - REQUIRED schema:Text Organization or Person
schema:identifier - OPTIONAL schema:Text The identifier of the Seller used by the Booking System.
schema:name - REQUIRED schema:Text Trading name of the Seller.
schema:legalName - REQUIRED schema:Text Legal name of the Seller, used on tax receipts.
schema:email - RECOMMENDED schema:Text Email address used to contact the Seller.
oa:taxMode - REQUIRED oa:TaxMode Either https://openactive.io/TaxNet or https://openactive.io/TaxGross
schema:vatID - REQUIRED schema:Text The Value-added Tax ID of the of the Seller.
schema:telephone - OPTIONAL schema:Text Telephone number of the Seller.
schema:url - RECOMMENDED schema:URL The URL of the website of the Seller.
schema:logo - RECOMMENDED schema:ImageObject Logo or photo of the Seller, used on tax receipts.
schema:address - REQUIRED schema:PostalAddress Address of the Seller, used on tax receipts.
schema:termsOfService - OPTIONAL Array of oa:Terms The terms of service of the Seller.

10.1.5 schema:Organization for broker

The broker MUST be a schema:Organization with the following properties, in order for the Booking System to represent the third-party booking.

The broker is specified with each Order to account for scenarios where multiple Broker brands are in use.

Property Broker Request Booking System Response Type Notes
@type REQUIRED - schema:Text Organization
schema:identifier OPTIONAL - schema:Text The identifier of the brand used by the Broker.
schema:name REQUIRED - schema:Text Name of the Broker.
schema:email RECOMMENDED - schema:Text Support email address used to contact the Broker.
schema:telephone OPTIONAL - schema:Text Support telephone number of the Broker.
schema:url RECOMMENDED - schema:URL The URL of the website of the Broker.
schema:logo RECOMMENDED - schema:ImageObject Logo of the Broker, used within the Booking System.
schema:termsOfService OPTIONAL - Array of oa:Terms The terms of service of the Broker.

10.1.6 schema:OrderItem

Property Broker Request Booking System Response Type Notes
@type REQUIRED REQUIRED schema:Text OrderItem
@id REQUIRED REQUIRED schema:URL A URI providing a unique identifier for the OrderItem
schema:orderItemStatus - REQUIRED schema:OrderStatus Either https://openactive.io/SellerCancelled, https://openactive.io/CustomerCancelled, https://openactive.io/OrderProposed, https://openactive.io/OrderConfirmed, or https://openactive.io/CustomerAttended
oa:allowCustomerCancellationFullRefund - RECOMMENDED schema:Boolean Whether the event can be cancelled.
oa:unitTaxSpecification - REQUIRED Array of oa:TaxChargeSpecification Breakdown of tax payable for the OrderItem.
schema:acceptedOffer REQUIRED REQUIRED schema:Offer The offer from the associated orderedItem that has been selected by the Customer. The price of this includes or excludes tax depending on the taxMode of the seller.
schema:orderedItem REQUIRED REQUIRED oa:ScheduledSession, oa:Slot, oa:HeadlineEvent, schema:Event or schema:CourseInstance The specific bookable Thing that has been selected by the Customer. See the [Modelling-Opportunity-Data] specification for more information on these types. Note that the Broker Request and Orders feed only require id within these objects to be included; in these contexts, all other properties are ignored.
schema:additionalProperty - RECOMMENDED Array of schema:PropertyValue PropertyValue that contains a text value useful for reconciliation.
schema:accessCode - RECOMMENDED Array of schema:PropertyValue PropertyValue that contains a text value usable for entrance. Not applicable for an OrderQuote.
oa:accessToken - RECOMMENDED Array of schema:ImageObject ImageObject or Barcode that contains reference to an asset (e.g. Barcode, QR code image or PDF) usable for entrance. Not applicable for an OrderQuote.
schema:error - REQUIRED Array of oa:OpenBookingError Array of errors related to the OrderItem being included in the Order, only applicable for an OrderQuote.
oa:cancellationMessage - - schema:Text A message set by the Seller in the event of Opportunity cancellation, only applicable for an Order and where the OrderItem has orderItemStatus set to https://openactive.io/SellerCancelled
oa:customerNotice - - schema:Text A message set by the Seller to trigger a notification to the Customer, only applicable for an Order and where the OrderItem has orderItemStatus set to https://openactive.io/OrderConfirmed or https://openactive.io/CustomerAttended
schema:attendee OPTIONAL OPTIONAL - schema:Person
oa:attendeeDetailsRequired OPTIONAL OPTIONAL - Array of schema:Property
oa:orderItemIntakeForm - OPTIONAL Array of schema:PropertyValueSpecification PropertyValueSpecifications that describe fields in the orderItemIntakeForm.
oa:orderItemIntakeFormResponse OPTIONAL OPTIONAL Array of schema:PropertyValue PropertyValues that contains a text value responses to the orderItemIntakeForm.

10.1.7 schema:Offer

Use of properties of Offer defined in the [Modelling-Opportunity-Data] is also encouraged.

Property Broker Request Booking System Response Type Notes
@type REQUIRED REQUIRED schema:Text Offer
@id REQUIRED REQUIRED schema:URL A URI providing a unique identifier for the Offer
schema:price - REQUIRED schema:Float The offer price available to participants, which includes or excludes tax depending on the taxMode of the seller.
schema:priceCurrency - REQUIRED schema:Text The currency of the price. Specified as a 3-letter ISO 4217 value. If an Offer has a zero price, then this property is not required. Otherwise the priceCurrency MUST be specified.
schema:name - RECOMMENDED schema:Text The name of the Offer suitable for communication to participants.
oa:ageRange RECOMMENDED RECOMMENDED schema:QuantitativeValue Indicates that an Offer is applicable to a specific age range. Specified as a QuantitativeValue with minValue and maxValue properties.
oa:advanceBooking OPTIONAL OPTIONAL oa:RequiredStatusType Indicates whether to accept this offer, a participant must book in advance, whether they must pay on attending, or have option to do either. Values MUST be one of https://openactive.io/Required, https://openactive.io/Optional or https://openactive.io/Unavailable.
oa:prepayment OPTIONAL OPTIONAL oa:RequiredStatusType Indicates whether to accept this offer, a participant must pay in advance, pay when attending, or have the option to do either. Values MUST be one of https://openactive.io/Required, https://openactive.io/Optional or https://openactive.io/Unavailable.
oa:availableChannel REQUIRED REQUIRED Array of oa:AvailableChannelType Can include https://openactive.io/OpenBookingPrepayment, https://openactive.io/TelephoneAdvanceBooking, https://openactive.io/TelephonePrepayment, https://openactive.io/OnlinePrepayment
oa:validFromBeforeStartDate - OPTIONAL schema:Duration The duration before the startDate for which this Offer is valid, given in ISO 8601 format. This is a relatively-defined equivalent of schema:validFrom, to allow for Offer inheritance.
oa:latestCancellationBeforeStartDate - OPTIONAL schema:Duration The duration before the startDate during which this Offer may not be cancelled, given in ISO 8601 format.
oa:openBookingFlowRequirement OPTIONAL OPTIONAL Array of oa:OpenBookingFlowRequirement Can include https://openactive.io/OpenBookingIntakeForm, https://openactive.io/OpenBookingAttendeeDetails, https://openactive.io/OpenBookingApproval, https://openactive.io/OpenBookingNegotiation, https://openactive.io/OpenBookingMessageExchange

Note that for an Offer to be bookable under this specification, availableChannel must include https://openactive.io/OpenBookingPrepayment.

10.1.8 schema:OfferOverride

OfferOverride subclasses Offer.

Property Broker Request Booking System Response Type Notes
@type REQUIRED REQUIRED schema:Text Offer
@id REQUIRED REQUIRED schema:URL A URI providing a unique identifier for the Offer
schema:price REQUIRED REQUIRED schema:Float The offer price available to participants, which includes or excludes tax depending on the taxMode of the seller.
schema:priceCurrency REQUIRED REQUIRED schema:Text The currency of the price. Specified as a 3-letter ISO 4217 value. If a PriceSpecification has a zero price, then this property is not required. Otherwise the priceCurrency MUST be specified.
schema:name RECOMMENDED RECOMMENDED schema:Text The name of the Offer suitable for communication to participants.
oa:availableChannel - - - Not included in OfferOverride.
oa:validFromBeforeStartDate OPTIONAL OPTIONAL schema:Duration The duration before the startDate for which this Offer is valid, given in ISO 8601 format. This is a relatively-defined equivalent of schema:validFrom, to allow for Offer inheritance.
oa:latestCancellationBeforeStartDate OPTIONAL OPTIONAL schema:Duration The duration before the startDate during which this Offer may not be cancelled, given in ISO 8601 format.

10.1.9 schema:Person

For business-to-consumer transactions, the customer MUST be a schema:Person.

The required fields below relate to a schema:Person when used in the customer property.

Property Broker Request Booking System Response Type Notes
@type REQUIRED - schema:Text Person
schema:identifier OPTIONAL - schema:Text The identifier of the Customer used by the Broker and/or Payment Provider.
schema:email REQUIRED - schema:Text Email address used to uniquely identify the Customer.
schema:givenName RECOMMENDED - schema:Text Name of the Customer.
schema:familyName RECOMMENDED - schema:Text Name of the Customer.
schema:telephone OPTIONAL - schema:Text Telephone number of the Customer.

Note that in all cases any Person received from the Booking System when it is reflected back in the response MUST only contain exactly the data that was provided by the Broker and MUST NOT contain any additional data that may be known about the Person by the Booking System. It is strongly RECOMMENDED that the personal data received by the Booking System be treated as a "guest checkout" and is not used to create a Customer record in the Booking System.

In this current iteration of the Booking API specification, the programmatic matching of Person records provided by the Broker to existing records within the Booking System is considered to be out of scope, and is NOT RECOMMENDED.

For conformance with this specification the Booking System MUST NOT require more data about the Customer than is specified as REQUIRED properties above.

10.1.10 schema:Organization for customer

For business-to-business transactions, the customer MUST be a schema:Organization with the following properties.

Property Broker Request Booking System Response Type Notes
@type REQUIRED - schema:Text Organization
schema:identifier OPTIONAL - schema:Text The identifier of the Customer used by the Broker and/or Payment Provider.
schema:email REQUIRED - schema:Text Email address used to uniquely identify the Customer.
schema:name REQUIRED - schema:Text Name of the Customer.
schema:telephone OPTIONAL - schema:Text Telephone number of the Customer.
schema:address REQUIRED - schema:PostalAddress Address of the business Customer.

Note that any Organization received from the Booking System when it is reflected back in the response MUST only contain exactly the data that was provided by the Broker and MUST NOT contain any additional data that may be known about the Organization by the Booking System.

10.1.11 schema:PostalAddress

For schema:Organization when used for customer or seller, the following properties are required.

Property Broker Request Booking System Response Type Notes
@type REQUIRED - schema:Text PostalAddress
schema:streetAddress REQUIRED - schema:Text Street address
schema:addressLocality REQUIRED - schema:Text Town or other area
schema:addressRegion REQUIRED - schema:Text Region
schema:postalCode REQUIRED - schema:Text Postcode
schema:addressCountry REQUIRED - schema:Text ISO 3166-1 alpha-2 country code.

10.1.12 oa:Lease

Used to represent a lease. Leases are defined as objects in this specification to provide for future extension.

Property Broker Request Booking System Response Type Notes
@type - REQUIRED schema:Text Lease
oa:leaseExpires - REQUIRED schema:DateTime Expiry DateTime of the Lease in ISO 8601 format
schema:identifier - OPTIONAL schema:Text Optional identifier of the Lease if useful for audit or debugging purposes.

10.1.13 schema:PriceSpecification

Used for totalPaymentDue.

Property Broker Request Booking System Response Type Notes
@type - REQUIRED schema:Text PriceSpecification
schema:price - REQUIRED schema:Float The total amount.
schema:priceCurrency - REQUIRED schema:Text The currency of the price. Specified as a 3-letter ISO 4217 value. If a PriceSpecification has a zero price, then this property is not required. Otherwise the priceCurrency MUST be specified.

10.1.14 oa:TaxChargeSpecification

TaxChargeSpecification subclasses PriceSpecification, and so includes the same properties and a few additional ones. These are listed below.

TaxChargeSpecification properties are intended to be rendered to the Customer as-is, and are not designed to be manipulated by the Broker.

Property Broker Request Booking System Response Type Notes
@type - REQUIRED schema:Text TaxChargeSpecification
schema:price - REQUIRED schema:Float The monetary value of the tax charge.
schema:priceCurrency - REQUIRED schema:Text The currency of the tax charge. Specified as a 3-letter ISO 4217 value. If an tax charge has a zero price, then this property is not required. Otherwise the priceCurrency MUST be specified.
schema:identifier - OPTIONAL schema:Integer, schema:Text, schema:PropertyValue or an array of schema:PropertyValue A local identifier for the tax charge e.g. "VAT-20", used to identify the type of tax within the Booking System. Note an array of PropertyValue can be used to provide multiple identifiers where available.
schema:name - REQUIRED schema:Text The name of the tax charge, e.g. "VAT at 0% for EU transactions"
oa:rate - OPTIONAL schema:Float The rate of VAT.

10.1.15 oa:Payment

Used for payment.

Property Broker Request Booking System Response Type Notes
@type - REQUIRED schema:Text Payment
schema:name - RECOMMENDED schema:Text Optional free text description of the payment method for the Booking System, to help the Seller in discussions with the Customer (e.g. "AcmeBroker Points" or "AcmeBroker via Credit Card").
schema:identifier - REQUIRED schema:Text The identifier of the payment held by the Broker and/or Payment Provider.
schema:paymentMethod - - schema:PaymentMethod paymentMethod MUST NOT be used, and is reserved for future versions of this specification.
schema:accountId - RECOMMENDED schema:Text A reference used by the Seller to group transactions, which is used to aid reconciliation.
oa:paymentProviderId - RECOMMENDED schema:Text A reference to the specific Payment Provider that is used.

10.1.16 oa:DynamicPayment

oa:DynamicPayment subclasses oa:Payment, and includes all of the above properties as specified for Payment, but with accountId being REQUIRED.

Used for fully dynamic pricing, where price is not known at the point of purchase.

Property Broker Request Booking System Response Type Notes
schema:accountId - REQUIRED schema:Text A reference used by the Seller to group transactions related, which is used to aid reconciliation.

10.1.17 oa:BookingService

oa:BookingService subclasses schema:Service.

Describes the Booking System; used for BookingService.

Property Broker Request Booking System Response Type Notes
@type - REQUIRED schema:Text BookingService
schema:name - OPTIONAL schema:Text The name of the Booking System
schema:url - OPTIONAL schema:URL The URL of the website of the Booking System.
schema:termsOfService - OPTIONAL Array of oa:Terms The terms of service of the Booking System.

10.1.18 oa:Terms

Used for termsOfService.

oa:Terms subclasses schema:DigitalDocument.

Property Broker Request Booking System Response Type Notes
@type - REQUIRED schema:Text Terms
schema:name - REQUIRED schema:Text The name of the terms. The name must distinguish this from other terms fields provided, e.g. "Terms and Conditions" or "Privacy Policy"
schema:url - REQUIRED schema:URL The URL of the webpage containing the contents of the terms.
oa:requiresExplicitConsent - REQUIRED schema:Boolean

10.1.19 oa:PrivacyPolicy

Used for termsOfService.

oa:PrivacyPolicy subclasses oa:Terms.

A specific subclass for Privacy Policies, to help the Broker distinguish these types of Terms in their user experience.

10.1.20 oa:TermsOfUse

Used for termsOfService.

oa:TermsOfUse subclasses oa:Terms.

A specific subclass for Terms of Use, to help the Broker distinguish these types of Terms in their user experience.

10.1.21 schema:PropertyValueSpecification

schema:PropertyValueSpecification MUST NOT be used in its abstract form, but is instead subclassed into more specific types.

Property Booking System Response Type Notes
@type REQUIRED schema:Text PropertyValueSpecification
@id REQUIRED schema:URL A URI providing a unique identifier for the PropertyValueSpecification to be referenced in the PropertyValue response.
schema:name REQUIRED schema:Text Display label for the field.
schema:description RECOMMENDED schema:Text Descriptive help text for the field.
schema:valueRequired OPTIONAL schema:Text Specifies that a value for the field is required to proceed with the booking.

10.1.22 oa:ShortAnswerFormFieldSpecification

Used for orderItemIntakeForm.

oa:ShortAnswerFormFieldSpecification subclasses schema:PropertyValueSpecification.

Specifies a single-line text box field.

10.1.23 oa:ParagraphFormFieldSpecification

Used for orderItemIntakeForm.

oa:ParagraphFormFieldSpecification subclasses schema:PropertyValueSpecification.

Specifies a multi-line text box field.

10.1.24 oa:DropdownFormFieldSpecification

Used for orderItemIntakeForm.

oa:DropdownFormFieldSpecification subclasses schema:PropertyValueSpecification, with the addition of oa:valueOption.

Specifies a list of options in a dropdown format.

Property Booking System Response Type Notes
@type REQUIRED schema:Text DropdownFormFieldSpecification
oa:valueOption REQUIRED Array of schema:Text Specifies an array of display values for the dropdown.

10.1.25 oa:BooleanFormFieldSpecification

Used for orderItemIntakeForm.

oa:BooleanFormFieldSpecification subclasses schema:PropertyValueSpecification, without oa:valueRequired.

Specifies a true or false response.

Property Booking System Response Type Notes
@type REQUIRED schema:Text BooleanFormFieldSpecification
schema:valueRequired - schema:Text MUST NOT be included.

10.1.26 schema:PropertyValue

schema:PropertyValue is used for a variety of purposes within this specification. Its REQUIRED properties are dependent on the context in which it is used, and are specified throughout this specification.

The use here is in keeping with the Schema.org definition of PropertyValue.

Property Type Notes
@type schema:Text PropertyValue
schema:propertyID schema:Text References the id of the PropertyValueSpecification to which this PropertyValue relates.
schema:name schema:Text Name of the property.
schema:description schema:Text Additional human-readable version of the value of the property.
schema:value schema:Text Machine-readable value of the property.

10.2 Error Model

10.2.1 oa:OpenBookingError

Property Status Type Notes
@type REQUIRED schema:Text Error
schema:name RECOMMENDED schema:Text A short, human-readable summary of the problem type, that MUST communicate the associated "Use Case" defined in this section of the specification. It SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization.
schema:description RECOMMENDED schema:Text A human-readable explanation specific to this occurrence of the problem, providing specific information about why the error occurred in this particular case.
oa:instance RECOMMENDED schema:URL The requested URL.
oa:method RECOMMENDED schema:Text The method of the request (e.g. GET).
oa:statusCode OPTIONAL schema:Integer An integer representing the HTTP status code, that MUST match the associated "Status Code" defined in this section of the specification.
oa:invalidParams OPTIONAL Array of schema:Text An array of invalid parameters, if appropriate.
oa:requestId OPTIONAL schema:Text Used by technical support for diagnostics purposes.

10.2.2 oa:OpenBookingError subclasses

A number of OpenBookingError subclasses are specified to identify the problem type.

10.2.2.1 Order Creation - OrderQuote, OrderProposal and Order error responses
Error Type (@type) Status Code (oa:statusCode) Use Case (schema:name)
IncompleteCustomerDetailsError 400 If the email address of the Customer is not supplied within a schema:Person object; or if the customer property supplied is not a valid schema:Person or schema:Organization object.
IncompleteBrokerDetailsError 400 If there is insufficient detail in the schema:Organisation object describing the Broker; or if the broker property supplied is not a valid schema:Organisation object.
IncompletePaymentDetailsError 400 If the payment property of the OrderQuote is missing details required by the Booking System, or if the payment property of the Order is missing or does not include an identifier.
InvalidPaymentDetailsError 400 If the payment property of the OrderQuote or Order contains data that is not accepted the Booking System for reconciliation, e.g. accountId.
TotalPaymentDueMismatchError 400 If the totalPaymentDue property of the submitted Order or OrderProposal does not match the value calculated for that Order or OrderProposal by the Booking System.
OpportunityHasInsufficientCapacityError 409 If there are not enough available spaces in any Opportunity that was included the Order.
UnableToProcessOrderItemError 409 If any OrderItem errors would have been generated at C2 given the same set of OrderItems. The Broker is expected to retry C2 to retrieve such errors.
OrderAlreadyExistsError 500 If the UUID used for an OrderQuote already represents a completed Order with a different set of OrderItems to those specified (note call to B is idempotent for the case where OrderItems match). This happens in the rare event of a UUID clash.
OrderCreationFailedError 500 If B fails for any other reason not specified in this list of errors.
10.2.2.2 Order Cancellation - OrderQuote, OrderProposal and Order error responses
Error Type (@type) Status Code (oa:statusCode) Use Case (schema:name)
CancellationNotPermittedError 400 The cancellation is not permitted due to internal rules of the Booking System not otherwise exposed to the Broker. The description property of the object MUST include a Customer-facing description of the reason that cancellation is not permitted.
10.2.2.3 Order Creation - OrderItem errors

Note that all OrderItem errors for an OrderQuote request result in a 409 Conflict response, as the error is expected to be resolved, and the request resubmitted, as per [RFC2616] section 10.4.10.

Error Type (@type) Status Code (oa:statusCode) Use Case (schema:name)
IncompleteAttendeeDetailsError 409 If the attendeeDetailsRequired properties of the attendee are not supplied within a schema:Person object.
IncompleteOrderItemError 409 If there is a missing acceptedOffer or orderedItem property on the OrderItem.
IncompleteIntakeFormError 409 If the orderItemIntakeFormResponse is missing fields that are specified as valueRequired within the orderItemIntakeForm.
InvalidIntakeFormError 409 If the orderItemIntakeFormResponse contains invalid fields that do not match those specified by the orderItemIntakeForm.
UnacceptableOfferError 409 If the acceptedOffer is not a URL which corresponds to an applicable Offer for the Opportunity.
UnknownOfferError 409 If the acceptedOffer is not a URL which corresponds to an Offer within the Booking System.
UnknownOpportunityDetailsError 409 If the orderedItem is not a URL which corresponds to an Opportunity within the Booking System.
UnavailableOpportunityError 409 If the orderedItem and acceptedOffer combination specified are not bookable.
OpportunityIsInConflictError 409 If the specific OrderItems against which this error is emitted are not able to be booked together.
OpportunityIsFullError 409 If there are no available spaces for the Opportunity contained in the orderedItem property.
OpportunityHasInsufficientCapacityError 409 If there are not enough available spaces in the Opportunity contained in the orderedItem property of the OrderItem to fulfil the number of repeated OrderItems. If the OrderItem is repeated (for multiple attendees) this error MUST only be included in the API response against the OrderItems which are in excess of the capacity - for example in an Opportunity with a remainingAttendeeCapacity of 2 and with 5 OrderItems related to it, this error would only be emitted against 3 of the OrderItems.
OpportunityCapacityIsReservedByLeaseError 409 If the available capacity required to book a specific Opportunity is reserved by a lease held by another Customer. If the OrderItem is repeated (for multiple attendees) this error MUST only be included in the API response against the OrderItems which are in excess of the capacity but which are reserved by the lease of another Customer - for example in an Opportunity with a remainingAttendeeCapacity of 3, with 1 additional space held by another lease, then for an OrderQuote with 9 OrderItems related to it, this error would only be emitted against 1 of the OrderItems, with OpportunityHasInsufficientCapacityError emitted against the other 5 of the OrderItems. This helps a Customer to know whether they should try again.
10.2.2.4 API authentication
Error Type (@type) Status Code (oa:statusCode) Use Case (schema:name)
UnauthenticatedError 403 If the Broker did not supply any form of authentication.
NoAPITokenError 403 If the Broker did not supply an API key.
InvalidAPITokenError 401 If the Broker supplied an invalid API key, either malformed or expired.
InvalidAuthorizationDetailsError 401 If the Broker supplied an invalid set of authorization details, either malformed or expired.
10.2.2.5 Technical errors
Error Type (@type) Status Code (oa:statusCode) Use Case (schema:name)
TemporarilyUnableToProduceOrderQuoteError 503 If the Booking System is unable for technical reasons to produce an OrderQuote where the data provided to it is sufficient to allow it to do so.
TemporarilyUnableToCreateOrderError 503 If the Booking System is unable for technical reasons to create an Order where the data provided to it is sufficient to allow it to do so.
TemporarilyUnableToUpdateOrderError 503 If the Booking System is unable for technical reasons to update an Order (which includes attempting to PATCH for cancellation) where the data provided to it is sufficient to allow it to do so.
TemporarilyUnableToDeleteOrderError 503 If the Booking System is unable for technical reasons to delete an Order where the data provided to it is sufficient to allow it to do so
10.2.2.6 Generic errors
Error Type (@type) Status Code (oa:statusCode) Use Case (schema:name)
UnknownOrderError 404 Where a Booking System has no Order matching the one requested.
UnknownOrIncorrectEndpointError 404 Where a Booking System has no endpoint matching the one requested.
NotFoundError 404 Where a Booking System does not have the generic resource specified.
MethodNotAllowed 405 Where a Booking System does not recognise a specific HTTP method for the endpoint requested with that specific HTTP verb.
PatchContainsExcessiveProperties 400 Where a Booking System recognises a PATCH request but the request object contains properties that the Broker is not permitted to update. Custom namespace properties are always excluded from consideration.
PatchNotAllowedOnProperty 400 Where a Booking System recognises a PATCH request but the request object contains one or many properties that the Broker is not permitted to update to the requested value.
GoneError 410 Where an Order has been soft-deleted by an Order Deletion request.
TooManyRequestsError 429 Where the Booking System is rate-limiting the Broker.

11. Common Requirements

This section defines some common functionality that applies to the design of the whole API.

11.1 Media types

Custom media types are used in this API to allow clients to choose the format of the data they receive, and for Booking Systems to stipulate which versions of the specification they support.

Booking Systems MAY choose to support additional media types but MUST support the media type(s) defined by this specification.

Media types used in the Accept header of requests and Content-Type header of responses will conform to the following pattern, which includes only the major version of the specification:

application/vnd.openactive.booking+json; version=x

The current media type is:

application/vnd.openactive.booking+json; version=1

Brokers will receive data in the most current format within that major version (e.g. 1.1, 1.2 etc).

11.2 Handling Specification Dependencies and Extensions

11.2.1 Realtime Paged Data Exchange 1.0

The Orders feed defined in this specification is an [RPDE] feed which conforms exactly to [RPDE] 1.0, except for section 5.1.3 where instead it uses media type application/vnd.openactive.booking+json; version=1.

Although additional Orders feed conforming to later versions of the [RPDE] specification and even to later versions of this specification MAY be implemented in parallel, implementations that are compliant with this version of the specification MUST utilise an Orders feed that conforms to [RPDE] 1.0.

11.2.2 Modelling Opportunity Data 2.x (where x >= 0)

The model defined in this specification builds on the model defined in [Modelling-Opportunity-Data] 2.0. Concepts from the model fall into three categories:

  • The Opportunity, and all types used within it, are defined entirely in [Modelling-Opportunity-Data] and not within this specification. This is covered in below in Opportunity data extensions.
  • The Offer which is defined across both [Modelling-Opportunity-Data] and within this specification. If any conflict exists in the definition of properties and values between this specification and a future version of [Modelling-Opportunity-Data], the definition in this specification MUST take precedent.
  • For all other types defined in both specifications, the conformance criteria in this specification MUST take precedent, and any conformance criteria in [Modelling-Opportunity-Data] related to such types MUST be ignored.
11.2.2.1 Opportunity data extensions

Within the scope of the Opportunity (orderedItem) and Offer (acceptedOffer), the Booking System MAY include additional properties, where such properties are either:

To ensure backward compatibility, within the scope of the Opportunity (orderedItem) and Offer (acceptedOffer), the Booking System MUST:

  • Conform to the [Modelling-Opportunity-Data] 2.0 specification by providing REQUIRED fields, unless otherwise stated in this specification.
  • Provide any data that is vital for the Customer to make an informed decision about the product they are purchasing (such as important restrictions on purchase), using properties specified within this specification and [Modelling-Opportunity-Data] 2.0, and hence not rely on additional properties.

A Broker MUST ensure that any values of properties found in the Opportunity (orderedItem) and Offer (acceptedOffer), that are defined within this specification and [Modelling-Opportunity-Data] 2.0, and that are vital for the Customer to make an informed decision about the product they are purchasing, are communicated to the Customer. A Broker MAY, at its discretion, also choose to communicate the value of additional properties.

11.2.2.2 Booking functionality extensions

Any extension to the functionality of this specification MUST NOT be inferred from additional properties included in future minor versions of the [Modelling-Opportunity-Data] specification. Given the scope of the [Modelling-Opportunity-Data] specification, such additional properties will serve simply to provide greater expressivity to the Opportunities and Offers the Customer is selecting. Functional extensions to the booking process itself fall properly within the scope of Open Booking API specification, and hence should be integrated with future versions of this specification.

The [OpenActive-Beta-Namespace] and properties in custom namespaces (as described in the [Modelling-Opportunity-Data] specification) MAY be used to extend and experiment with new functionality for future inclusion in this specification. However all functionality MUST work as described in this specification should these extensions be completely ignored by either the Broker, the Booking System or both.

11.2.3 General extension mechanism

Additional OPTIONAL extension terms (including properties, classes and enumerations) MAY be included in any request and response defined within this specification using a custom namespace (as specified in the [Modelling-Opportunity-Data] specification), and the presence and values of such extension terms MAY alter the behaviour of the API.

An API implementing this specification MAY also choose to define additional extension endpoints, which MUST accept and require a different media type to that defined in this specification.

To ensure full compatibility with this specification, use of such extension terms and extension endpoints MUST NOT be required as part of the operations defined in this specification, and the API MUST function in complete conformity with this specification when such extension terms and extension endpoints are omitted or ignored.

Any extension terms and extension endpoints defined for an API implementation MUST be fully documented, in order to ensure that the Seller has a complete view of the functionality it is exposing to the Broker.

11.2.3.1 Extension example: Member booking

The example below demonstrates hypothetical extension terms defined in B to allow for existing registered members to book without supplying customer details or payment details, using the custom "acme" namespace and associated context.

Example 56: Extension Example: Member booking
PUT /api/orders/e11429ea-467f-4270-ab62-e47368996fe8 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1

{
  "@context": [
    "https://openactive.io/",
    "https://acmesystem.example.com/api/context.jsonld"
  ],
  "@type": "Order",
  "brokerRole": "https://openactive.io/NoBroker",
  "customer": {
    "@type": "acme:Member",
    "identifier": "MLD23947233"
  },
  "orderedItem": [
    {
      "@type": "OrderItem",
      "acceptedOffer": {
        "@type": "Offer",
        "@id": "https://example.com/events/452#/offers/878"
      },
      "orderedItem": {
        "@type": "ScheduledSession",
        "@id": "https://example.com/events/452/subEvents/132"
      }
    }
  ],
  "totalPaymentDue": {
    "@type": "PriceSpecification",
    "price": 5,
    "priceCurrency": "GBP"
  },
  "payment": {
    "@type": "acme:StoredPaymentMethod",
    "identifier": 93482
  }
}
11.2.3.2 Extension example: Additional information during the ordering process

The example below demonstrates hypothetical extension terms defined in B that adds additional OPTIONAL properties to help track a bespoke agreement, using the custom "acme" namespace and associated context.

Example 57: Extension Example: Bespoke agreement data
PUT /api/orders/e11429ea-467f-4270-ab62-e47368996fe8 HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.openactive.booking+json; version=1

{
  "@context": [
    "https://openactive.io/",
    "https://acmesystem.example.com/api/context.jsonld"
  ],
  "@type": "Order",
  "brokerRole": "https://openactive.io/AgentBroker",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "seller": {
    "@type": "Organization",
    "@id": "https://example.com/api/organisations/123"
  },
  "customer": {
    "@type": "Person",
    "email": "[email protected]",
    "telephone": "020 811 8055",
    "givenName": "Geoff",
    "familyName": "Capes"
  },
  "orderedItem": {
    "@type": "OrderItem",
    "acceptedOffer": {
      "@type": "Offer",
      "@id": "https://example.com/events/452#/offers/878"
    },
    "orderedItem": {
      "@type": "ScheduledSession",
      "@id": "https://example.com/events/452/subEvents/132"
    },
    "acme:agreement": {
      "@type": "acme:ContractAgreement",
      "acme:multiple": 3,
      "acme:commissionCategory": "https://acmesystem.example.com/ns/BandA"
    }
  },
  "totalPaymentDue": {
    "@type": "PriceSpecification",
    "price": 5,
    "priceCurrency": "GBP"
  },
  "payment": {
    "@type": "Payment",
    "name": "AcmeBroker Points",
    "identifier": "1234567890npduy2f"
  }
}
11.2.3.3 Extension example: Integrated Payments

In some limited circumstances it might also be desirable for an Open Booking API to act as a facade over a Booking System, Payment Provider, and Invoicing systems. For these circumstances the Payment type may be subclassed to facilitate full payment, using a custom namespace and associated context.

Example 58: Extension Example: Integrated Payments
"payment": {
  "@type": "acme:IntegratedPayment",
  "identifier": "12345678ABCD",
  "paymentMethod": "acme:StripePayment",
  "acme:token": "tok_KPte7942xySKBKyrBu11yEpf"
},

Note that due to the variety of business models available in the OpenActive ecosystem, conformance to this specification requires that the use of any native payment functionality in the Booking System be OPTIONAL.

11.2.3.4 Extension example: Waiting list endpoint

The example below demonstrates a hypothetical extension endpoint to support waiting list registration.

Example 59: Extension Example: Waiting List Endpoint
POST /api/sessions/{session-id}/waiting-list HTTP/1.1
Host: example.com
Date: Mon, 8 Oct 2018 20:52:35 GMT
Accept: application/vnd.acmesystem.booking+json; version=0.3

{
  "@context": [
    "https://openactive.io/",
    "https://acmesystem.example.com/api/context.jsonld"
  ],
  "@type": "acme:WaitingListEntry",
  "broker": {
    "@type": "Organization",
    "name": "MyFitnessApp",
    "url": "https://myfitnessapp.example.com",
    "description": "A fitness app for all the community",
    "logo": {
      "@type": "ImageObject",
      "url": "http://data.myfitnessapp.org.uk/images/logo.png"
    },
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "Alan Peacock Way",
      "addressLocality": "Village East",
      "addressRegion": "Middlesbrough",
      "postalCode": "TS4 3AE",
      "addressCountry": "GB"
    }
  },
  "customer": {
    "@type": "Person",
    "email": "[email protected]",
    "telephone": "020 811 8055",
    "givenName": "Geoff",
    "familyName": "Capes"
  }
}

11.3 Response formats

Requests and response documents exchanged via this API will all be valid [JSON-LD] documents, with the exception of the Orders feed, where only documents included in the data property are valid [JSON-LD].

The [JSON-LD] documents MUST include a [JSON-LD] context that refers to the [OpenActive-Vocabulary] as follows:

Example 60: minimal JSON-LD document
{
  "@context": "https://openactive.io/"
}

Unless otherwise specified the request and response documents MUST conform to the [Modelling-Opportunity-Data] data model.

11.4 Errors

Error responses MUST be served using an appropriate HTTP 4XX status code or HTTP 5XX status code specified in the model.

All error responses from this API MUST be returned in a JSON-LD format subclass of the OpenBookingError using a content type of application/vnd.openactive.booking+json; version=1.

Example 61: Error response example
HTTP/1.1 400 Bad Request
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1

{
  "@context": "https://openactive.io/",
  "@type": "IncompleteCustomerDetailsError",
  "description": "No customer details supplied"
}

11.5 Rate Limiting

The Booking System MAY choose to apply rate limiting for each unique set of authentication credentials. In such circumstances where a request is rate limited, the Booking System MUST return a response with status code 429.

To allow for a broad range of API management solutions to be used, the Broker MUST rely only on the status code 429 when detecting and retrying a rate-limited request, as specified elsewhere in this specification. The following guidance is included to encourage convergence in implementations where possible.

All rate-limited responses from this API SHOULD return a JSON-LD format TooManyRequestsError object using a content type of application/vnd.openactive.booking+json; version=1 with status code 429.

The response SHOULD also include the Retry-After HTTP Header with a value set to the number of seconds after which the Broker SHOULD retry as per [RFC2616] section 14.37.

Example 62: Rate limited response example
HTTP/1.1 429 Too Many Requests
Date: Mon, 8 Oct 2018 20:52:36 GMT
Content-Type: application/vnd.openactive.booking+json; version=1
Retry-After: 8

{
  "@context": "https://openactive.io/",
  "@type": "TooManyRequestsError",
  "description": "Rate Limit Reached. Retry in 8 seconds."
}

11.6 Security

Although this specification does not mandate a specific means of securing an API, it does mandate that at a minimum all HTTP requests and responses MUST be secured using SSL.

11.7 Authentication

All of the API transactions described in this document MUST require authentication.

Authentication Credentials to facilitate access to the Open Booking API on behalf of one or many Brokers are uniquely and securely provisioned to the authenticating party by the Booking System on behalf of a specific Seller. The authenticating party may a Broker, or may be a Middleware.

11.7.1 API level authentication and data security

The content and data available to all endpoints provided by the Booking System MUST be specific to the Authentication Credentials in order to ensure that bookings are secure. It is the responsibility of the Booking System to ensure that only the Authentication Credentials responsible for creating an Order (including any of its subclasses) can subsequently access, amend, or cancel that Order.

This specification does not mandate a particular authentication method, but its recommendation is that implementors 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 token-based mechanisms are also appropriate.

11.7.2 Middleware as a catalyst

Authentication Credentials and Middleware
Figure 17 Authentication Credentials may facilitate access to the Booking System directly (Broker) or indirectly (Middleware), for a specific Seller

This specification permits trusted Middleware to use a single set of Authentication Credentials to interact with the Booking System for a particular Seller on behalf of multiple Brokers, with the Seller's explicit permission. This lowers the barrier to entry for the creation of new Brokers that might otherwise need to establish Authentication Credentials and/or relationships with a large number of Sellers.

The definition of the Middleware role in this specification is intentionally broad: it is simply any party that handles Authentication Credentials and lies between one or many public-facing Brokers and the Seller. 'Middleware' thus includes not only dedicated middleware technology companies, but also parent organisations or agencies that manage a number of brands, affiliate platforms, etc.

This specification thus keeps Authentication Credentials separate from the concept of a Broker within the Booking System. While all Brokers must be associated with a set of Authentication Credentials, note that this is a one-to-many relationship: one set of Authentication Credentials MUST be able be used on behalf of many Brokers. The introduction of a new Broker (as specified by the broker property) MUST be possible by simply including the details within the requests of this API, with no additional out-of-band registration required.

Data Isolation within Authentication Credentials
Figure 18 Data is isolated between Authentication Credentials, with one set of Authentication Credentials allowing access to many Brokers

In terms of implementation, all data partitioning and security MUST be based on the Authentication Credentials; the broker property within the Order is for information only.

A client using the same Authentication Credentials MUST be able to represent more than one Broker, and the Booking System MUST make no assumptions regarding which Broker is being represented by a particular set of Authentication Credentials. For the avoidance of doubt: the same Broker accessing a Booking System using two sets of Authentication Credentials MUST NOT be able to access Orders created by one set of Authentication Credentials when using the other, and is considered as two separate identically-named Brokers by the Booking System.

Note that the broker property in the request body is to help the Booking System to clarify which Broker is making the request and to help them in situations such as Customer queries. It is not designed as any part of an authentication which MUST be performed outside of the JSON body of the request. Authentication tokens MUST NOT be present within the broker property of the submitted JSON.

The broker property MUST contain the details of the actual Broker rather than any Middleware.

Where information regarding Middleware or any other authenticating party is required for audit, this SHOULD be captured by the Booking System at the point of provision of Authentication Credentials.

The Booking System MAY choose to produce usage reports against Authentication Credentials, as well as against Brokers, to report at different levels of granularity. However Booking Systems MUST NOT produce reconciliation reports based on anything other than the information provided to the Payment object, i.e. there MUST be no assumed link between the Authentication Credentials used for access, the Broker (broker) data provided for information to the API, and the Payment data detailing the account into which the money is deposited for reconciliation. It MUST be possible for the same Payment details to be used across multiple Authentication Credentials and Brokers.

Any Middleware that conforms with this specification MUST provide data partitioning and security with respect to Broker access, to ensure that Brokers only have access to their own data.

11.7.3 Customer level authentication

OpenID Connect ([OpenIdConnect]) is recommended as 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 MUST NOT be able to be changed by any other Customer. It is the responsibility of the Broker to ensure that subsequent requests to an Order should only be initiated by the Customer which created the Order.

12. Future versions of this API

Future iterations of the specification be shaped by the OpenActive community, and we encourage you to get involved.

A. Acknowledgements

This section is non-normative.

The editors thank all members of the OpenActive Community Group for their contributions.

Icons made by Freepik, Darius Dan, Eucalyp, smalllikeart, bqlqn, Maxim Basinski from www.flaticon.com.

B. References

B.1 Normative references

[Dataset-API-Discovery]
Dataset API Discovery. OpenActive Community Group. URL: https://www.openactive.io/dataset-api-discovery/EditorsDraft/
[JSON-LD]
JSON-LD 1.0. W3C. W3C Recommendation. URL: https://www.w3.org/TR/json-ld/
[Modelling-Opportunity-Data]
Modelling Opportunity Data. OpenActive Community Group. URL: https://www.openactive.io/modelling-opportunity-data/
[OAuth2]
OAuth 2.0. URL: https://oauth.net/2/
[OpenActive-Beta-Namespace]
OpenActive Beta Namespace. OpenActive Community Group. URL: https://www.openactive.io/ns-beta/
[OpenActive-Vocabulary]
OpenActive Vocabulary. OpenActive Community Group. URL: https://www.openactive.io/ns/
[OpenIdConnect]
OpenID Connect. URL: https://openid.net/connect/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[RFC2616]
Hypertext Transfer Protocol -- HTTP/1.1. R. Fielding; J. Gettys; J. Mogul; H. Frystyk; L. Masinter; P. Leach; T. Berners-Lee. IETF. June 1999. Draft Standard. URL: https://tools.ietf.org/html/rfc2616
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. May 2017. Best Current Practice. URL: https://tools.ietf.org/html/rfc8174
[RPDE]
Realtime Paged Data Exchange. OpenActive Community Group. URL: https://www.openactive.io/realtime-paged-data-exchange/